Skip to content

Commit 49c49a8

Browse files
committed
Fix issue with multiple positional args, update tests [ci fast]
Signed-off-by: Ben Sherman <[email protected]>
1 parent a2063eb commit 49c49a8

File tree

5 files changed

+305
-45
lines changed

5 files changed

+305
-45
lines changed

modules/nextflow/src/main/groovy/nextflow/cli/v2/PluginCmd.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ class PluginCmd extends AbstractCmd {
4141
@ParentCommand
4242
private Launcher launcher
4343

44-
@Parameters
44+
@Parameters(index = '0')
4545
String command
4646

47-
@Parameters
47+
@Parameters(index = '1..*')
4848
List<String> args
4949

5050
@Override

modules/nextflow/src/main/groovy/nextflow/cli/v2/RunCmd.groovy

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ class RunCmd extends AbstractCmd implements RunImpl.Options, HubOptions {
5252
@ParentCommand
5353
private Launcher launcher
5454

55-
@Parameters(description = 'Project name or repository url')
55+
@Parameters(index = '0', description = 'Project name or repository url')
5656
String pipeline
5757

58-
@Parameters(description = 'Pipeline script args')
58+
@Parameters(index = '1..*', description = 'Pipeline script args')
5959
List<String> args
6060

6161
@Option(names = ['--ansi-log'], arity = '1', description = 'Use ANSI logging')
@@ -224,54 +224,88 @@ class RunCmd extends AbstractCmd implements RunImpl.Options, HubOptions {
224224
@Option(names = ['--with-weblog'], arity = '0..1', fallbackValue = '-', description = 'Send workflow status messages via HTTP to target URL')
225225
String withWebLog
226226

227-
@Parameters(description = 'Pipeline parameters')
228-
List<String> params
227+
private List<String> pipelineArgs = null
229228

230-
private Map<String,String> paramsMap = null
229+
private Map<String,String> pipelineParams = null
231230

232231
/**
233-
* Get the pipeline params as a map.
232+
* Parse the pipeline args and params from the positional
233+
* args parsed by picocli. This method assumes that the first
234+
* positional arg that starts with '--' is the first param,
235+
* and parses the remaining args as params.
234236
*
235-
* The double-dash ('--') notation is normally used to separate
236-
* positional parameters from options. As a result, params will also
237-
* contain the positional parameters of the `run` command (i.e. args),
238-
* so they must be skipped when constructing the params map.
239-
*
240-
* This method assumes that params are specified as option-value pairs
241-
* separated by a space. The equals-sign ('=') separator is not supported.
237+
* NOTE: While the double-dash ('--') notation can be used to
238+
* distinguish pipeline params from CLI options, it cannot be
239+
* used to distinguish pipeline params from pipeline args.
242240
*/
243-
@Override
244-
Map<String,String> getParams() {
245-
if( paramsMap == null ) {
246-
paramsMap = [:]
247-
248-
int i = args.size()
249-
while( i < params.size() ) {
250-
String current = params[i++]
251-
252-
String key
253-
String value
254-
if( current.contains('=') ) {
255-
int split = current.indexOf('=')
256-
key = current.substring(0, split)
257-
value = current.substring(split+1)
258-
}
259-
else if( i < params.size() && !params[i].startsWith('--') ) {
260-
key = current
261-
value = params[i++]
262-
}
263-
else {
264-
key = current
265-
value = 'true'
266-
}
267-
268-
paramsMap.put(key, value)
241+
private void parseArgs() {
242+
// parse pipeline args
243+
int i = args.findIndexOf { it.startsWith('--') }
244+
pipelineArgs = args[0..<i]
245+
246+
// parse pipeline params
247+
pipelineParams = [:]
248+
249+
if( i == -1 )
250+
return
251+
252+
while( i < args.size() ) {
253+
String current = args[i++]
254+
if( !current.startsWith('--') ) {
255+
throw new IllegalArgumentException("Invalid argument '${current}' -- unable to parse it as a pipeline arg, pipeline param, or CLI option")
256+
}
257+
258+
String key
259+
String value
260+
261+
// parse '--param=value'
262+
if( current.contains('=') ) {
263+
int split = current.indexOf('=')
264+
key = current.substring(2, split)
265+
value = current.substring(split+1)
266+
}
267+
268+
// parse '--param value'
269+
else if( i < args.size() && !args[i].startsWith('--') ) {
270+
key = current.substring(2)
271+
value = args[i++]
269272
}
270273

271-
log.trace "Parsing params from CLI: $paramsMap"
274+
// parse '--param1 --param2 ...' as '--param1 true --param2 ...'
275+
else {
276+
key = current.substring(2)
277+
value = 'true'
278+
}
279+
280+
pipelineParams.put(key, value)
281+
}
282+
283+
log.trace "Parsing pipeline args from CLI: $pipelineArgs"
284+
log.trace "Parsing pipeline params from CLI: $pipelineParams"
285+
}
286+
287+
/**
288+
* Get the list of pipeline args.
289+
*/
290+
@Override
291+
List<String> getArgs() {
292+
if( pipelineArgs == null ) {
293+
parseArgs()
294+
}
295+
296+
return pipelineArgs
297+
}
298+
299+
/**
300+
* Get the map of pipeline params.
301+
*/
302+
@Override
303+
Map<String,String> getParams() {
304+
if( pipelineParams == null ) {
305+
parseArgs()
272306
}
273307

274-
return paramsMap
308+
return pipelineParams
275309
}
276310

277311
@Override
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2020-2022, Seqera Labs
3+
* Copyright 2013-2019, Centre for Genomic Regulation (CRG)
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package nextflow.cli
19+
20+
import spock.lang.Specification
21+
22+
/**
23+
*
24+
* @author Paolo Di Tommaso <[email protected]>
25+
*/
26+
class HubOptionsTest extends Specification {
27+
28+
def testUserV1() {
29+
30+
when:
31+
def cmd = [:] as v1.HubOptions
32+
cmd.hubUserCli = credential
33+
then:
34+
cmd.getHubUser() == user
35+
cmd.getHubPassword() == password
36+
37+
where:
38+
credential | user | password
39+
null | null | null
40+
'paolo' | 'paolo' | null
41+
'paolo:secret' | 'paolo' | 'secret'
42+
43+
}
44+
45+
def testUserV2() {
46+
47+
when:
48+
def cmd = [:] as v2.HubOptions
49+
cmd.hubUserCli = credential
50+
then:
51+
cmd.getHubUser() == user
52+
cmd.getHubPassword() == password
53+
54+
where:
55+
credential | user | password
56+
null | null | null
57+
'paolo' | 'paolo' | null
58+
'paolo:secret' | 'paolo' | 'secret'
59+
60+
}
61+
62+
}

modules/nextflow/src/test/groovy/nextflow/cli/v1/LauncherTest.groovy

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ class LauncherTest extends Specification {
4545
then:
4646
assert launcher.options.fullVersion
4747

48-
4948
}
5049

5150
def 'should return `help` command' () {
@@ -135,9 +134,11 @@ class LauncherTest extends Specification {
135134
launcher.command.hubProvider == 'github'
136135

137136
when:
138-
launcher = new Launcher().parseMainArgs('run', 'script.nf', '--alpha', '0', '--omega', '9')
137+
launcher = new Launcher().parseMainArgs('run', 'script.nf', 'arg1', 'arg2', '--alpha', '0', '--omega', '9')
139138
then:
140139
launcher.command instanceof RunCmd
140+
launcher.command.pipeline == 'script.nf'
141+
launcher.command.args == ['arg1', 'arg2']
141142
launcher.command.params.'alpha' == '0'
142143
launcher.command.params.'omega' == '9'
143144

@@ -357,4 +358,5 @@ class LauncherTest extends Specification {
357358
String opt4
358359

359360
}
361+
360362
}

0 commit comments

Comments
 (0)