Skip to content

Commit a0bdfae

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

11 files changed

Lines changed: 158 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-2025, 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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2013-2025, 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+
import org.pf4j.ExtensionPoint
21+
22+
/**
23+
* Extension point interface for the `launch` command.
24+
*
25+
* @see io.seqera.tower.plugin.launch.LaunchCommandImpl
26+
*
27+
* @author Phil Ewels <phil.ewels@seqera.io>
28+
*/
29+
interface LaunchCommand extends ExtensionPoint {
30+
void launch(LaunchOptions options)
31+
}
32+
33+
34+
/**
35+
* Data class to hold launch options
36+
*/
37+
@CompileStatic
38+
class LaunchOptions {
39+
String pipeline
40+
String workspace
41+
String computeEnv
42+
String runName
43+
String workDir
44+
String revision
45+
String profile
46+
List<String> configFiles
47+
String paramsFile
48+
String entryName
49+
String resume
50+
boolean latest
51+
boolean stubRun
52+
String mainScript
53+
Map<String, String> params
54+
List<String> userSecrets
55+
List<String> workspaceSecrets
56+
}

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: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,14 @@ class BaseCommandImpl {
5050
return apiEndpoint.replace('://api.', '://').replace('/api', '')
5151
}
5252

53-
protected Map readConfig() {
53+
protected ConfigObject readConfig() {
54+
// note: the Nextflow home config file is `$HOME/.nextflow/config` (not `nextflow.config`)
5455
final configFile = Const.APP_HOME_DIR.resolve('config')
55-
return new ConfigBuilder().build(configFile.exists() ? [ configFile ] : []).flatten()
56+
return new ConfigBuilder().build(configFile.exists() ? [ configFile ] : [])
57+
}
58+
59+
protected Map readConfigFlat() {
60+
return readConfig().flatten()
5661
}
5762

5863
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

@@ -60,7 +60,7 @@ import static nextflow.util.ColorUtil.colorize
6060
@Slf4j
6161
@InheritConstructors
6262
@CompileStatic
63-
class AuthCommandImpl extends BaseCommandImpl implements CmdAuth.AuthCommand {
63+
class AuthCommandImpl extends BaseCommandImpl implements AuthCommand {
6464
static final String OIDC_CLIENT_ID = 'nextflow_cli'
6565
static final int WORKSPACE_SELECTION_THRESHOLD = 8 // Max workspaces to show in single list; above this uses org-first selection
6666

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

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

@@ -792,7 +790,7 @@ class AuthCommandImpl extends BaseCommandImpl implements CmdAuth.AuthCommand {
792790
*/
793791
@Override
794792
void status() {
795-
final config = readConfig()
793+
final config = readConfigFlat()
796794
printStatus(collectStatus(config))
797795
}
798796

0 commit comments

Comments
 (0)