Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 11 additions & 1 deletion USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,16 @@ tw managed-identities add -o my-org -n hpc1 -p slurm -H login.hpc.example.com
tw managed-identities credentials -o my-org -n hpc1 add -l my_linux_user -k ~/.ssh/id_rsa
```

## Lineage

Run `tw lineage -h` to view the supported operations: `resolve`, `view`, `search`, `upstream`, and `downstream`. Lineage must be enabled for the workspace.

```bash
tw lineage resolve -w my-org/my-workspace --file-path s3://bucket/results/out.bam
tw lineage resolve -w my-org/my-workspace --session-id <session-id> --run-name <run-name>
tw lineage upstream -w my-org/my-workspace -i <lid>
```

## Other additions

- `tw compute-envs enable|disable`: enable or disable a compute environment.
Expand All @@ -765,7 +775,7 @@ tw managed-identities credentials -o my-org -n hpc1 add -l my_linux_user -k ~/.s
- `tw launch`, `tw pipelines add|update`, `tw runs relaunch`, and `tw actions add|update`: `--tower-config`. List the Nextflow and Fusion versions you can launch with `tw info versions` (`-c fusion` for Fusion).
- `tw runs star|unstar`, `tw runs view log [--next <cursor>]`, and `tw runs delete -i <id>,<id>`.
- `tw pipelines schema`: show a pipeline's parameter schema.
- `tw studios star|unstar|extend|rename-checkpoint`. `--mount-data-uris` also accepts a folder inside a data link.
- `tw studios star|unstar|extend|logs|rename-checkpoint`. `--mount-data-uris` also accepts a folder inside a data link.
- `tw data-links delete-content|download-script`.
- `tw datasets add --url`, `tw datasets update --url`, and `tw datasets disable-version`.

Expand Down
102 changes: 102 additions & 0 deletions conf/reflect-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@
"allDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.LineageCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.ManagedIdentitiesCmd",
"queryAllDeclaredMethods":true,
Expand Down Expand Up @@ -1503,6 +1509,71 @@
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.AbstractLineageCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.commands.lineage.AbstractNavigateCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"lidOptions"}, {"name":"type"}, {"name":"workspace"}]
},
{
"name":"io.seqera.tower.cli.commands.lineage.DownstreamCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.LidOptions",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"lid"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.ResolveCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"source"}, {"name":"workspace"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.ResolveCmd$Run",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"runName"}, {"name":"sessionId"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.ResolveCmd$Source",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"filePath"}, {"name":"run"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.SearchCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"max"}, {"name":"pageToken"}, {"name":"query"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.UpstreamCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.lineage.ViewCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"lidOptions"}, {"name":"summary"}, {"name":"workspace"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.managedidentities.AbstractManagedIdentitiesCmd",
"queryAllDeclaredMethods":true,
Expand Down Expand Up @@ -2171,6 +2242,13 @@
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.studios.LogsCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"maxLength"}, {"name":"next"}, {"name":"studioRefOptions"}, {"name":"workspace"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.studios.ParentStudioRefOptions",
"allDeclaredFields":true,
Expand Down Expand Up @@ -2776,6 +2854,24 @@
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.lineage.LineageRecordView",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.lineage.LineageRecordsList",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.lineage.LineageResolved",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.managedidentities.ManagedCredentialsAdded",
"allDeclaredFields":true,
Expand Down Expand Up @@ -3210,6 +3306,12 @@
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.studios.StudioLog",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.studios.StudioStarred",
"allDeclaredFields":true,
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/seqera/tower/cli/Tower.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.seqera.tower.cli.commands.DatasetsCmd;
import io.seqera.tower.cli.commands.InfoCmd;
import io.seqera.tower.cli.commands.LaunchCmd;
import io.seqera.tower.cli.commands.LineageCmd;
import io.seqera.tower.cli.commands.ManagedIdentitiesCmd;
import io.seqera.tower.cli.commands.MembersCmd;
import io.seqera.tower.cli.commands.OrganizationsCmd;
Expand Down Expand Up @@ -72,6 +73,7 @@
InfoCmd.class,
LabelsCmd.class,
LaunchCmd.class,
LineageCmd.class,
ManagedIdentitiesCmd.class,
MembersCmd.class,
OrganizationsCmd.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.seqera.tower.api.IdentitiesApi;
import io.seqera.tower.api.LabelsApi;
import io.seqera.tower.api.LaunchApi;
import io.seqera.tower.api.LineageApi;
import io.seqera.tower.api.NextflowApi;
import io.seqera.tower.api.OrgsApi;
import io.seqera.tower.api.PipelineSchemasApi;
Expand Down Expand Up @@ -253,6 +254,10 @@ protected LaunchApi launchApi() throws ApiException {
return launchApi == null ? new LaunchApi(apiClient()) : launchApi;
}

protected LineageApi lineageApi() throws ApiException {
return new LineageApi(apiClient());
}

protected NextflowApi nextflowApi() throws ApiException {
return nextflowApi == null ? new NextflowApi(apiClient()) : nextflowApi;
}
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/io/seqera/tower/cli/commands/LineageCmd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2021-2026, Seqera.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package io.seqera.tower.cli.commands;

import io.seqera.tower.cli.commands.lineage.DownstreamCmd;
import io.seqera.tower.cli.commands.lineage.ResolveCmd;
import io.seqera.tower.cli.commands.lineage.SearchCmd;
import io.seqera.tower.cli.commands.lineage.UpstreamCmd;
import io.seqera.tower.cli.commands.lineage.ViewCmd;
import picocli.CommandLine.Command;

@Command(
name = "lineage",
description = "Explore data lineage records. Lineage must be enabled for the workspace.",
subcommands = {
ResolveCmd.class,
ViewCmd.class,
SearchCmd.class,
UpstreamCmd.class,
DownstreamCmd.class,
}
)
public class LineageCmd extends AbstractRootCmd {
}
2 changes: 2 additions & 0 deletions src/main/java/io/seqera/tower/cli/commands/StudiosCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.seqera.tower.cli.commands.studios.CheckpointsCmd;
import io.seqera.tower.cli.commands.studios.DeleteCmd;
import io.seqera.tower.cli.commands.studios.ExtendCmd;
import io.seqera.tower.cli.commands.studios.LogsCmd;
import io.seqera.tower.cli.commands.studios.RenameCheckpointCmd;
import io.seqera.tower.cli.commands.studios.StarCmd;
import io.seqera.tower.cli.commands.studios.UnstarCmd;
Expand Down Expand Up @@ -50,6 +51,7 @@
StarCmd.class,
UnstarCmd.class,
ExtendCmd.class,
LogsCmd.class,
}
)
public class StudiosCmd extends AbstractRootCmd {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2021-2026, Seqera.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package io.seqera.tower.cli.commands.lineage;

import io.seqera.tower.cli.commands.AbstractApiCmd;
import picocli.CommandLine.Command;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

@Command
public abstract class AbstractLineageCmd extends AbstractApiCmd {

/**
* The SDK encodes the LID path segment once, and the API decodes it twice: proxies normalize an
* encoded '/' or ':' in paths, so the web UI double-encodes LIDs. Encode once more here to match.
*/
protected static String lidPathSegment(String lid) {
return URLEncoder.encode(lid, StandardCharsets.UTF_8);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2021-2026, Seqera.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package io.seqera.tower.cli.commands.lineage;

import io.seqera.tower.ApiException;
import io.seqera.tower.cli.commands.global.WorkspaceRequiredOptions;
import io.seqera.tower.cli.responses.Response;
import io.seqera.tower.cli.responses.lineage.LineageRecordsList;
import io.seqera.tower.model.SearchResult;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;

@Command
public abstract class AbstractNavigateCmd extends AbstractLineageCmd {

public enum RecordType {FileOutput, TaskRun}

@Mixin
public WorkspaceRequiredOptions workspace;

@Mixin
public LidOptions lidOptions;

@Option(names = {"-t", "--type"}, description = "Show only records of this type (${COMPLETION-CANDIDATES})")
public RecordType type;

protected abstract String direction();

protected abstract SearchResult navigate(String lid, Long wspId, String type) throws ApiException;

@Override
protected Response exec() throws ApiException {
Long wspId = workspaceId(workspace.workspace);
SearchResult result = navigate(lidPathSegment(lidOptions.lid), wspId, type == null ? null : type.name());
return new LineageRecordsList(String.format("Lineage records %s of '%s'", direction(), lidOptions.lid), result.getRecords(), result.getNextPageToken());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2021-2026, Seqera.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package io.seqera.tower.cli.commands.lineage;

import io.seqera.tower.ApiException;
import io.seqera.tower.model.SearchResult;
import picocli.CommandLine.Command;

@Command(
name = "downstream",
description = "List the records one hop downstream of a task or file record. By default, the tasks that consumed a file, or the output files of a task."
)
public class DownstreamCmd extends AbstractNavigateCmd {

@Override
protected String direction() {
return "downstream";
}

@Override
protected SearchResult navigate(String lid, Long wspId, String type) throws ApiException {
return lineageApi().navigateLineageDownstream(lid, wspId, type);
}
}
Loading
Loading