Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fd648cc
feat: add pipeline version API client to the base abstract cmd
JaimeSeqLabs Feb 17, 2026
86d0181
feat: utility classes update
JaimeSeqLabs Feb 17, 2026
7403f26
feat: pipeline versions list cmd
JaimeSeqLabs Feb 17, 2026
ff8a78c
fix: missing license header
JaimeSeqLabs Feb 17, 2026
ed4aef5
feat: unit test for pipeline versions cmd
JaimeSeqLabs Feb 17, 2026
935d716
chore: move error handler into base class
JaimeSeqLabs Feb 18, 2026
649fb06
feat: pipeline versions view cmd
JaimeSeqLabs Feb 18, 2026
2557c3b
feat: pipeline version update cmd
JaimeSeqLabs Feb 18, 2026
67c0a77
feat: include commands in the root versioning class
JaimeSeqLabs Feb 18, 2026
ee94e58
feat: unit tests for versions view and update cmds
JaimeSeqLabs Feb 18, 2026
4de3e48
fix: remove unsetting default flag use case, update version by name
JaimeSeqLabs Feb 18, 2026
627b72a
chore: move version resolution to base api cmd
JaimeSeqLabs Feb 18, 2026
4080fc6
feat: versioning support for 'pipelines add' cmd, refactor error hand…
JaimeSeqLabs Feb 19, 2026
f8c6d46
feat: versioning support for 'pipelines view' cmd, refactor error han…
JaimeSeqLabs Feb 19, 2026
aebd939
feat: versioning support for 'pipelines export' cmd
JaimeSeqLabs Feb 19, 2026
a1534f0
feat: versioning support for 'pipelines launch' cmd
JaimeSeqLabs Feb 19, 2026
6acd7b9
feat: versioning support for 'pipelines update' cmd, detect when draf…
JaimeSeqLabs Feb 19, 2026
840a7ce
feat: updated unit tests
JaimeSeqLabs Feb 19, 2026
d7c9857
feat: updated reflection files
JaimeSeqLabs Feb 19, 2026
845818e
feat: include versioning data in 'pipelines export' cmd ouput
JaimeSeqLabs Feb 19, 2026
510a6a8
feat: include versioning data in 'pipelines view' cmd output
JaimeSeqLabs Feb 19, 2026
838be74
feat: update tests with versioning data output
JaimeSeqLabs Feb 19, 2026
8cdca25
refactor: move pipeline labels subcommands to separate package
JaimeSeqLabs Feb 19, 2026
63358de
fix: reflection files
JaimeSeqLabs Feb 19, 2026
56d36d8
merge: resolve conflicts with master (pipelineSchemaId)
JaimeSeqLabs Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 80 additions & 6 deletions conf/reflect-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1469,12 +1469,6 @@
"allDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.LabelsCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.LaunchOptions",
"allDeclaredFields":true,
Expand Down Expand Up @@ -1511,6 +1505,60 @@
"allDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.labels.LabelsCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.ListCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.UpdateCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.UpdateCmd$UpdateOptions",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.VersionRefOptions",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.VersionRefOptions$VersionRef",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.VersionsCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.ViewCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelines.versions.ViewCmd$VersionRef",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.pipelineschemas.AbstractPipelineSchemasCmd",
"allDeclaredFields":true,
Expand Down Expand Up @@ -2187,12 +2235,36 @@
"allDeclaredMethods":true,
"allDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.pipelines.PipelinesUpdated",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.pipelines.PipelinesView",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.pipelines.versions.ListPipelineVersionsCmdResponse",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.pipelines.versions.UpdatePipelineVersionCmdResponse",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.pipelines.versions.ViewPipelineVersionCmdResponse",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.pipelineschemas.PipelineSchemasAdded",
"allDeclaredFields":true,
Expand Down Expand Up @@ -4027,6 +4099,7 @@
},
{
"name":"io.seqera.tower.model.ListPipelineVersionsResponse",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"addVersionsItem","parameterTypes":["io.seqera.tower.model.PipelineDbDto"] }, {"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"getTotalSize","parameterTypes":[] }, {"name":"getVersions","parameterTypes":[] }, {"name":"hashCode","parameterTypes":[] }, {"name":"setTotalSize","parameterTypes":["java.lang.Long"] }, {"name":"setVersions","parameterTypes":["java.util.List"] }, {"name":"toIndentedString","parameterTypes":["java.lang.Object"] }, {"name":"toString","parameterTypes":[] }, {"name":"totalSize","parameterTypes":["java.lang.Long"] }, {"name":"versions","parameterTypes":["java.util.List"] }]
Expand Down Expand Up @@ -4352,6 +4425,7 @@
},
{
"name":"io.seqera.tower.model.PipelineVersionManageRequest",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"getIsDefault","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"hashCode","parameterTypes":[] }, {"name":"isDefault","parameterTypes":["java.lang.Boolean"] }, {"name":"name","parameterTypes":["java.lang.String"] }, {"name":"setIsDefault","parameterTypes":["java.lang.Boolean"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"toIndentedString","parameterTypes":["java.lang.Object"] }, {"name":"toString","parameterTypes":[] }]
Expand Down
4 changes: 2 additions & 2 deletions conf/resource-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@
"locales":["und"]
}, {
"name":"org.glassfish.jersey.client.internal.localization",
"locales":["und"]
"locales":["", "und"]
}, {
"name":"org.glassfish.jersey.internal.localization",
"locales":["und"]
"locales":["", "und"]
}, {
"name":"org.glassfish.jersey.media.multipart.internal.localization"
}]
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/io/seqera/tower/cli/commands/AbstractApiCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.seqera.tower.api.OrgsApi;
import io.seqera.tower.api.PipelineSchemasApi;
import io.seqera.tower.api.PipelineSecretsApi;
import io.seqera.tower.api.PipelineVersionsApi;
import io.seqera.tower.api.PipelinesApi;
import io.seqera.tower.api.PlatformsApi;
import io.seqera.tower.api.ServiceInfoApi;
Expand All @@ -44,6 +45,7 @@
import io.seqera.tower.cli.Tower;
import io.seqera.tower.cli.commands.labels.Label;
import io.seqera.tower.cli.commands.labels.LabelsFinder;
import io.seqera.tower.cli.commands.pipelines.versions.VersionRefOptions;
import io.seqera.tower.cli.exceptions.ComputeEnvNotFoundException;
import io.seqera.tower.cli.exceptions.InvalidWorkspaceParameterException;
import io.seqera.tower.cli.exceptions.MissingTowerAccessTokenException;
Expand All @@ -60,10 +62,12 @@
import io.seqera.tower.model.Credentials;
import io.seqera.tower.model.DataStudioQueryAttribute;
import io.seqera.tower.model.ListComputeEnvsResponseEntry;
import io.seqera.tower.model.ListPipelineVersionsResponse;
import io.seqera.tower.model.ListWorkspacesAndOrgResponse;
import io.seqera.tower.model.OrgAndWorkspaceDto;
import io.seqera.tower.model.PipelineDbDto;
import io.seqera.tower.model.PipelineQueryAttribute;
import io.seqera.tower.model.PipelineVersionFullInfoDto;
import io.seqera.tower.model.UserResponseDto;
import io.seqera.tower.model.WorkflowQueryAttribute;
import org.glassfish.jersey.CommonProperties;
Expand All @@ -83,6 +87,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Predicate;
import java.util.logging.Logger;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -116,6 +121,7 @@ public abstract class AbstractApiCmd extends AbstractCmd {
private PipelinesApi pipelinesApi;
private PipelineSchemasApi pipelineSchemasApi;
private PipelineSecretsApi pipelineSecretsApi;
private PipelineVersionsApi pipelineVersionsApi;
private PlatformsApi platformsApi;
private ServiceInfoApi serviceInfoApi;
private StudiosApi studiosApi;
Expand Down Expand Up @@ -235,6 +241,10 @@ protected PipelinesApi pipelinesApi() throws ApiException {
return pipelinesApi == null ? new PipelinesApi(apiClient()) : pipelinesApi;
}

protected PipelineVersionsApi pipelineVersionsApi() throws ApiException {
return pipelineVersionsApi == null ? new PipelineVersionsApi(apiClient()) : pipelineVersionsApi;
}

protected PlatformsApi platformsApi() throws ApiException {
return platformsApi == null ? new PlatformsApi(apiClient()) : platformsApi;
}
Expand Down Expand Up @@ -580,6 +590,39 @@ protected String workspaceRef(Long workspaceId) throws ApiException {
return buildWorkspaceRef(orgName(workspaceId), workspaceName(workspaceId));
}

protected PipelineVersionFullInfoDto findPipelineVersionByRef(Long pipelineId, Long wspId, VersionRefOptions.VersionRef ref) throws ApiException {
String search = ref.versionName;
Boolean isPublished = ref.versionName != null ? true : null;
Predicate<PipelineVersionFullInfoDto> matcher = ref.versionId != null
? v -> ref.versionId.equals(v.getId())
: v -> ref.versionName.equals(v.getName());

ListPipelineVersionsResponse response = pipelineVersionsApi()
.listPipelineVersions(pipelineId, wspId, null, null, search, isPublished);

if (response.getVersions() == null) {
throw new TowerException("No versions available for the pipeline, check if Pipeline versioning feature is enabled for the workspace");
}

return response.getVersions().stream()
.map(PipelineDbDto::getVersion)
.filter(Objects::nonNull)
.filter(matcher)
.findFirst()
.orElse(null);
}

protected String resolvePipelineVersionId(Long pipelineId, Long wspId, VersionRefOptions.VersionRef versionRef) throws ApiException {
if (versionRef == null) return null;
if (versionRef.versionId != null) return versionRef.versionId;

PipelineVersionFullInfoDto version = findPipelineVersionByRef(pipelineId, wspId, versionRef);
if (version == null) {
throw new TowerException(String.format("Pipeline version '%s' not found", versionRef.versionName));
}
return version.getId();
}

protected Long sourceWorkspaceId(Long currentWorkspace, PipelineDbDto pipeline) {
if (pipeline == null)
return null;
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/io/seqera/tower/cli/commands/LaunchCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.seqera.tower.cli.commands.enums.OutputType;
import io.seqera.tower.cli.commands.global.WorkspaceOptionalOptions;
import io.seqera.tower.cli.commands.labels.Label;
import io.seqera.tower.cli.commands.pipelines.versions.VersionRefOptions;
import io.seqera.tower.cli.exceptions.InvalidResponseException;
import io.seqera.tower.cli.responses.Response;
import io.seqera.tower.cli.responses.runs.RunSubmited;
Expand Down Expand Up @@ -83,6 +84,10 @@ public class LaunchCmd extends AbstractRootCmd {
@Option(names = {"--commit-id"}, description = "Specific Git commit hash to pin the pipeline execution to.")
String commitId;

// Explicit "0..1" for clarity — contrasts with the required "1" in VersionRefOptions. @Mixin won't work here as it would lose mutual exclusivity.
@ArgGroup(multiplicity = "0..1")
VersionRefOptions.VersionRef versionRef;

@Option(names = {"--wait"}, description = "Wait until workflow reaches specified status: ${COMPLETION-CANDIDATES}")
public WorkflowStatus wait;

Expand Down Expand Up @@ -178,8 +183,9 @@ protected Response runTowerPipeline(Long wspId) throws ApiException, IOException
}

Long sourceWorkspaceId = sourceWorkspaceId(wspId, pipe);
String versionId = resolvePipelineVersionId(pipe.getPipelineId(), wspId, versionRef);

LaunchDbDto launch = pipelinesApi().describePipelineLaunch(pipe.getPipelineId(), wspId, sourceWorkspaceId, null).getLaunch();
LaunchDbDto launch = pipelinesApi().describePipelineLaunch(pipe.getPipelineId(), wspId, sourceWorkspaceId, versionId).getLaunch();

WorkflowLaunchRequest launchRequest = createLaunchRequest(launch);
if (computeEnv != null) {
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/io/seqera/tower/cli/commands/PipelinesCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
import io.seqera.tower.cli.commands.pipelines.DeleteCmd;
import io.seqera.tower.cli.commands.pipelines.ExportCmd;
import io.seqera.tower.cli.commands.pipelines.ImportCmd;
import io.seqera.tower.cli.commands.pipelines.LabelsCmd;
import io.seqera.tower.cli.commands.pipelines.labels.LabelsCmd;
import io.seqera.tower.cli.commands.pipelines.ListCmd;
import io.seqera.tower.cli.commands.pipelines.UpdateCmd;
import io.seqera.tower.cli.commands.pipelines.ViewCmd;
import io.seqera.tower.cli.commands.pipelines.versions.VersionsCmd;
import picocli.CommandLine.Command;


Expand All @@ -40,6 +41,7 @@
ExportCmd.class,
ImportCmd.class,
LabelsCmd.class,
VersionsCmd.class
}
)
public class PipelinesCmd extends AbstractRootCmd {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ protected PipelineDbDto fetchPipeline(PipelineRefOptions pipelineRefOptions, Lon
return pipelinesApi().describePipeline(pipelineId, List.of(attributes), wspId, null).getPipeline();
}

protected void throwPipelineNotFoundException(PipelineRefOptions pipelineRefOptions, Long wspId) throws ApiException, PipelineNotFoundException {
if (pipelineRefOptions.pipeline.pipelineId != null) {
throw new PipelineNotFoundException(pipelineRefOptions.pipeline.pipelineId, workspaceRef(wspId));
}
throw new PipelineNotFoundException(pipelineRefOptions.pipeline.pipelineName, workspaceRef(wspId));
}

private static String quotePipelineName(String pipelineName) {

if (pipelineName == null) return null;
Expand Down
Loading