Skip to content

Commit 5117eca

Browse files
committed
Decouple nf-tower plugin from cli module
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent a27ea08 commit 5117eca

12 files changed

Lines changed: 176 additions & 121 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2013-2026, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.cli
18+
19+
import org.pf4j.ExtensionPoint
20+
21+
/**
22+
* Extension point interface for the `auth` command.
23+
*
24+
* @see io.seqera.tower.plugin.auth.AuthCommandImpl
25+
*
26+
* @author Phil Ewels <phil.ewels@seqera.io>
27+
*/
28+
interface AuthCommand extends ExtensionPoint {
29+
/**
30+
* Authenticates with Seqera Platform and saves credentials to config.
31+
*
32+
* @param url the Seqera Platform API endpoint URL (null for default)
33+
*/
34+
void login(String url)
35+
36+
/**
37+
* Revokes access token and removes authentication from local config.
38+
*/
39+
void logout()
40+
41+
/**
42+
* Configures Seqera Platform settings (workspace, monitoring, compute environment).
43+
*/
44+
void config()
45+
46+
/**
47+
* Displays current authentication status and configuration sources.
48+
*/
49+
void status()
50+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2013-2026, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.cli
18+
19+
import org.pf4j.ExtensionPoint
20+
21+
/**
22+
* Extension point interface for the `launch` command.
23+
*
24+
* @see io.seqera.tower.plugin.launch.LaunchCommandImpl
25+
*
26+
* @author Phil Ewels <phil.ewels@seqera.io>
27+
*/
28+
interface LaunchCommand extends ExtensionPoint {
29+
void launch(LaunchOptions options)
30+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2013-2026, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.cli
18+
19+
import groovy.transform.CompileStatic
20+
21+
/**
22+
* Data class to hold launch options
23+
*
24+
* @author Phil Ewels <phil.ewels@seqera.io>
25+
*/
26+
@CompileStatic
27+
class LaunchOptions {
28+
String pipeline
29+
String workspace
30+
String computeEnv
31+
String runName
32+
String workDir
33+
String revision
34+
String profile
35+
List<String> configFiles
36+
String paramsFile
37+
String entryName
38+
String resume
39+
boolean latest
40+
boolean stubRun
41+
String mainScript
42+
Map<String, String> params
43+
List<String> userSecrets
44+
List<String> workspaceSecrets
45+
}

modules/nf-cli-v1/src/main/groovy/nextflow/cli/CmdAuth.groovy

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import nextflow.config.ConfigCmdAdapter
2626
import nextflow.exception.AbortOperationException
2727
import nextflow.platform.PlatformHelper
2828
import nextflow.plugin.Plugins
29-
import org.pf4j.ExtensionPoint
3029

3130
/**
3231
* Command-line interface for managing Seqera Platform authentication and configuration.
@@ -107,47 +106,6 @@ class CmdAuth extends CmdBase implements UsageAware {
107106
void usage(List<String> result)
108107
}
109108

110-
/**
111-
* Extension point interface for authentication command implementations.
112-
*
113-
* <p>This interface is implemented by the {@code nf-tower} plugin to provide the actual
114-
* authentication logic while keeping the CLI interface in the core Nextflow codebase.
115-
* The plugin system uses PF4J to discover and load implementations at runtime.
116-
*
117-
* <p>The implementation handles:
118-
* <ul>
119-
* <li>OAuth2 device flow for Seqera Platform Cloud
120-
* <li>PAT (Personal Access Token) authentication for Enterprise
121-
* <li>Token storage and management in config files
122-
* <li>Workspace and compute environment configuration
123-
* </ul>
124-
*
125-
* @see io.seqera.tower.plugin.auth.AuthCommandImpl
126-
*/
127-
interface AuthCommand extends ExtensionPoint {
128-
/**
129-
* Authenticates with Seqera Platform and saves credentials to config.
130-
*
131-
* @param url the Seqera Platform API endpoint URL (null for default)
132-
*/
133-
void login(String url)
134-
135-
/**
136-
* Revokes access token and removes authentication from local config.
137-
*/
138-
void logout()
139-
140-
/**
141-
* Configures Seqera Platform settings (workspace, monitoring, compute environment).
142-
*/
143-
void config()
144-
145-
/**
146-
* Displays current authentication status and configuration sources.
147-
*/
148-
void status()
149-
}
150-
151109
static public final String NAME = 'auth'
152110

153111
private List<SubCmd> commands = []

modules/nf-cli-v1/src/main/groovy/nextflow/cli/CmdLaunch.groovy

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import groovy.transform.CompileStatic
2323
import groovy.util.logging.Slf4j
2424
import nextflow.exception.AbortOperationException
2525
import nextflow.plugin.Plugins
26-
import org.pf4j.ExtensionPoint
2726

2827
/**
2928
* Implements the 'nextflow launch' command
@@ -35,10 +34,6 @@ import org.pf4j.ExtensionPoint
3534
@Parameters(commandDescription = "Launch a workflow in Seqera Platform")
3635
class CmdLaunch extends CmdBase implements UsageAware {
3736

38-
interface LaunchCommand extends ExtensionPoint {
39-
void launch(LaunchOptions options)
40-
}
41-
4237
static final public String NAME = 'launch'
4338

4439
@Override
@@ -130,7 +125,6 @@ class CmdLaunch extends CmdBase implements UsageAware {
130125
stubRun: stubRun,
131126
mainScript: mainScript,
132127
params: params,
133-
launcher: launcher,
134128
userSecrets: userSecrets,
135129
workspaceSecrets: workspaceSecrets
136130
)
@@ -179,29 +173,4 @@ class CmdLaunch extends CmdBase implements UsageAware {
179173
// Get Launch command operations implementation from plugins
180174
return Plugins.getExtension(LaunchCommand)
181175
}
182-
183-
/**
184-
* Data class to hold launch options
185-
*/
186-
@CompileStatic
187-
static class LaunchOptions {
188-
String pipeline
189-
String workspace
190-
String computeEnv
191-
String runName
192-
String workDir
193-
String revision
194-
String profile
195-
List<String> configFiles
196-
String paramsFile
197-
String entryName
198-
String resume
199-
boolean latest
200-
boolean stubRun
201-
String mainScript
202-
Map<String, String> params
203-
Launcher launcher
204-
List<String> userSecrets
205-
List<String> workspaceSecrets
206-
}
207176
}

modules/nf-cli-v1/src/test/groovy/nextflow/cli/CmdAuthTest.groovy

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class CmdAuthTest extends Specification {
8383
given:
8484
def cmd = Spy(CmdAuth)
8585
cmd.args = ['unknown']
86-
def operation = Mock(CmdAuth.AuthCommand)
86+
def operation = Mock(AuthCommand)
8787

8888
when:
8989
cmd.run()
@@ -111,7 +111,7 @@ class CmdAuthTest extends Specification {
111111
def 'login command should validate too many arguments'() {
112112
given:
113113
def cmd = Spy(CmdAuth)
114-
def operation = Mock(CmdAuth.AuthCommand)
114+
def operation = Mock(AuthCommand)
115115
cmd.args = ['login', 'extra']
116116

117117
when:
@@ -126,7 +126,7 @@ class CmdAuthTest extends Specification {
126126
def 'logout command should validate too many arguments'() {
127127
given:
128128
def cmd = Spy(CmdAuth)
129-
def operation = Mock(CmdAuth.AuthCommand)
129+
def operation = Mock(AuthCommand)
130130
cmd.args = ['logout', 'extra']
131131

132132
when:
@@ -141,7 +141,7 @@ class CmdAuthTest extends Specification {
141141
def 'config command should validate too many arguments'() {
142142
given:
143143
def cmd = Spy(CmdAuth)
144-
def operation = Mock(CmdAuth.AuthCommand)
144+
def operation = Mock(AuthCommand)
145145
cmd.args = ['config', 'extra']
146146

147147
when:
@@ -156,7 +156,7 @@ class CmdAuthTest extends Specification {
156156
def 'status command should validate too many arguments'() {
157157
given:
158158
def cmd = Spy(CmdAuth)
159-
def operation = Mock(CmdAuth.AuthCommand)
159+
def operation = Mock(AuthCommand)
160160
cmd.args = ['status', 'extra']
161161

162162
when:
@@ -171,7 +171,7 @@ class CmdAuthTest extends Specification {
171171
def 'login command should use provided API URL'() {
172172
given:
173173
def cmd = Spy(CmdAuth)
174-
def operation = Mock(CmdAuth.AuthCommand)
174+
def operation = Mock(AuthCommand)
175175
cmd.args = ['login']
176176
cmd.apiUrl = 'https://api.example.com'
177177

plugins/nf-seqera/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ configurations {
4848
}
4949

5050
dependencies {
51-
compileOnly project(':nf-cli-v1')
51+
compileOnly project(':nextflow')
5252
compileOnly 'org.slf4j:slf4j-api:2.0.17'
5353
compileOnly 'org.pf4j:pf4j:3.14.1'
5454

plugins/nf-tower/src/main/io/seqera/tower/plugin/BaseCommandImpl.groovy

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,13 @@ class BaseCommandImpl {
5050
return apiEndpoint.replace('://api.', '://').replace('/api', '')
5151
}
5252

53-
protected Map readConfig() {
53+
protected ConfigObject readConfig() {
5454
final configFile = Const.APP_HOME_DIR.resolve('config')
55-
return new ConfigBuilder().build(configFile.exists() ? [ configFile ] : []).flatten()
55+
return new ConfigBuilder().build(configFile.exists() ? [ configFile ] : [])
56+
}
57+
58+
protected Map readConfigFlat() {
59+
return readConfig().flatten()
5660
}
5761

5862
protected List<Map> listUserWorkspaces(TowerClient client, String userId) {

plugins/nf-tower/src/main/io/seqera/tower/plugin/auth/AuthCommandImpl.groovy

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ import groovy.transform.InheritConstructors
3737
import groovy.util.logging.Slf4j
3838
import nextflow.Const
3939
import nextflow.SysEnv
40-
import nextflow.cli.CmdAuth
41-
import nextflow.config.ConfigCmdAdapter
40+
import nextflow.cli.AuthCommand
41+
import nextflow.config.ConfigBuilder
4242
import nextflow.exception.AbortOperationException
4343
import nextflow.platform.PlatformHelper
4444
import nextflow.util.ProxyConfig
@@ -61,7 +61,7 @@ import static nextflow.util.ColorUtil.colorize
6161
@Slf4j
6262
@InheritConstructors
6363
@CompileStatic
64-
class AuthCommandImpl extends BaseCommandImpl implements CmdAuth.AuthCommand {
64+
class AuthCommandImpl extends BaseCommandImpl implements AuthCommand {
6565
static final String OIDC_CLIENT_ID = 'nextflow_cli'
6666
static final int WORKSPACE_SELECTION_THRESHOLD = 8 // Max workspaces to show in single list; above this uses org-first selection
6767

@@ -237,9 +237,8 @@ class AuthCommandImpl extends BaseCommandImpl implements CmdAuth.AuthCommand {
237237
private String normalizeApiUrl(String url) {
238238
if( !url ) {
239239
// Read config to get the actual resolved endpoint value
240-
final builder = new ConfigCmdAdapter().setHomeDir(Const.APP_HOME_DIR).setCurrentDir(Const.APP_HOME_DIR)
241-
final configObject = builder.buildConfigObject()
242-
final towerConfig = configObject.navigate('tower') as Map ?: [:]
240+
final configObject = readConfig()
241+
final towerConfig = configObject.tower as Map ?: [:]
243242
return PlatformHelper.getEndpoint(towerConfig, SysEnv.get())
244243
}
245244
if( !url.startsWith('http://') && !url.startsWith('https://') ) {
@@ -405,12 +404,11 @@ class AuthCommandImpl extends BaseCommandImpl implements CmdAuth.AuthCommand {
405404
@Override
406405
void config(Boolean showHeader = true) {
407406
// Read from both main config and seqera-auth.config file
408-
final builder = new ConfigCmdAdapter().setHomeDir(Const.APP_HOME_DIR).setCurrentDir(Const.APP_HOME_DIR)
409-
final configObject = builder.buildConfigObject()
407+
final configObject = readConfig()
410408
final config = configObject.flatten()
411409

412410
// Navigate to tower config section (returns map without 'tower.' prefix)
413-
final towerConfig = configObject.navigate('tower') as Map ?: [:]
411+
final towerConfig = configObject.tower as Map ?: [:]
414412
final existingToken = PlatformHelper.getAccessToken(towerConfig, SysEnv.get())
415413
final endpoint = PlatformHelper.getEndpoint(towerConfig, SysEnv.get())
416414

@@ -793,7 +791,7 @@ class AuthCommandImpl extends BaseCommandImpl implements CmdAuth.AuthCommand {
793791
*/
794792
@Override
795793
void status() {
796-
final config = readConfig()
794+
final config = readConfigFlat()
797795
printStatus(collectStatus(config))
798796
}
799797

0 commit comments

Comments
 (0)