Skip to content
Closed
16 changes: 13 additions & 3 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,10 +713,19 @@ $ tw data-links browse -w seqeralabs/showcase -i v1-user-6d8f44c239e2a098b3e02e9

These commands act on an organization (`-o`/`--organization`). Most require the organization `OWNER` role.

### Custom roles

Run `tw roles -h` to view the supported operations: `list`, `view`, `add`, `update`, `delete`, and `permissions`. `tw participants update --role` accepts custom role names.

```bash
tw roles permissions -o my-org
tw roles add -o my-org -n launcher -d "Launch only" -p <permission>,<permission>
```

### Teams and quotas

- `tw teams view|update|workspaces`: view a team, update its name or description, and list its workspaces.
- `tw organizations quotas`: view organization quotas.
- `tw organizations quotas` and `tw members roles -u <user>`: view organization quotas and a member's workspace roles.

## Personal access

Expand Down Expand Up @@ -756,9 +765,10 @@ 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`.
- `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 extend|rename-checkpoint`.
- `tw actions add bucket`.
- `tw studios extend|rename-checkpoint`. `--mount-data-uris` also accepts a folder inside a data link.
- `tw data-links delete-content|download-script`.
- `tw datasets disable-version`.
- `tw datasets add --url`, `tw datasets update --url`, and `tw datasets disable-version`.

[compute-envs]: https://docs.seqera.io/platform/latest/compute-envs/overview
[credentials]: https://docs.seqera.io/platform/latest/credentials/overview
Expand Down
106 changes: 106 additions & 0 deletions conf/reflect-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@
"allDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.RolesCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.RunsCmd",
"allDeclaredFields":true,
Expand Down Expand Up @@ -371,6 +377,13 @@
"allDeclaredFields":true,
"allDeclaredMethods":true
},
{
"name":"io.seqera.tower.cli.commands.actions.add.AddBucketCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"dataLinkId"}, {"name":"events"}, {"name":"markerFile"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.actions.add.AddGitHubCmd",
"allDeclaredFields":true,
Expand Down Expand Up @@ -1614,6 +1627,13 @@
"allDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.members.RolesCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"organizationRef"}, {"name":"user"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.members.UpdateCmd",
"allDeclaredFields":true,
Expand Down Expand Up @@ -1859,6 +1879,48 @@
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.roles.AddCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"description"}, {"name":"name"}, {"name":"organizationRef"}, {"name":"permissions"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.roles.DeleteCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"name"}, {"name":"organizationRef"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.roles.ListCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"filter"}, {"name":"organizationRef"}, {"name":"paginationOptions"}, {"name":"type"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.roles.PermissionsCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"organizationRef"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.roles.UpdateCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"description"}, {"name":"name"}, {"name":"newName"}, {"name":"organizationRef"}, {"name":"permissions"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.roles.ViewCmd",
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"fields":[{"name":"name"}, {"name":"organizationRef"}],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"io.seqera.tower.cli.commands.runs.AbstractRunsCmd",
"allDeclaredFields":true,
Expand Down Expand Up @@ -2741,6 +2803,14 @@
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.members.MemberRolesList",
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allDeclaredConstructors":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.members.MembersAdded",
"allDeclaredFields":true,
Expand Down Expand Up @@ -2907,6 +2977,42 @@
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.roles.RoleAdded",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.roles.RoleDeleted",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.roles.RolePermissionsList",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.roles.RoleUpdated",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.roles.RoleView",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.roles.RolesList",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true
},
{
"name":"io.seqera.tower.cli.responses.runs.RunCanceled",
"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 @@ -37,6 +37,7 @@
import io.seqera.tower.cli.commands.ParticipantsCmd;
import io.seqera.tower.cli.commands.PipelinesCmd;
import io.seqera.tower.cli.commands.PipelineSchemasCmd;
import io.seqera.tower.cli.commands.RolesCmd;
import io.seqera.tower.cli.commands.RunsCmd;
import io.seqera.tower.cli.commands.SecretsCmd;
import io.seqera.tower.cli.commands.SshKeysCmd;
Expand Down Expand Up @@ -77,6 +78,7 @@
ParticipantsCmd.class,
PipelinesCmd.class,
PipelineSchemasCmd.class,
RolesCmd.class,
RunsCmd.class,
SshKeysCmd.class,
TeamsCmd.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import io.seqera.tower.api.PipelineVersionsApi;
import io.seqera.tower.api.PipelinesApi;
import io.seqera.tower.api.PlatformsApi;
import io.seqera.tower.api.RolesApi;
import io.seqera.tower.api.ServiceInfoApi;
import io.seqera.tower.api.SshKeysApi;
import io.seqera.tower.api.StudiosApi;
Expand Down Expand Up @@ -133,6 +134,7 @@ public abstract class AbstractApiCmd extends AbstractCmd {
private PipelineSecretsApi pipelineSecretsApi;
private PipelineVersionsApi pipelineVersionsApi;
private PlatformsApi platformsApi;
private RolesApi rolesApi;
private ServiceInfoApi serviceInfoApi;
private SshKeysApi sshKeysApi;
private StudiosApi studiosApi;
Expand Down Expand Up @@ -264,6 +266,10 @@ protected PlatformsApi platformsApi() throws ApiException {
return platformsApi == null ? new PlatformsApi(apiClient()) : platformsApi;
}

protected RolesApi rolesApi() throws ApiException {
return rolesApi == null ? new RolesApi(apiClient()) : rolesApi;
}

protected ServiceInfoApi serviceInfoApi() throws ApiException {
return serviceInfoApi == null ? new ServiceInfoApi(apiClient()) : serviceInfoApi;
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/seqera/tower/cli/commands/MembersCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.seqera.tower.cli.commands.members.DeleteCmd;
import io.seqera.tower.cli.commands.members.LeaveCmd;
import io.seqera.tower.cli.commands.members.ListCmd;
import io.seqera.tower.cli.commands.members.RolesCmd;
import io.seqera.tower.cli.commands.members.UpdateCmd;
import picocli.CommandLine;

Expand All @@ -32,6 +33,7 @@
DeleteCmd.class,
UpdateCmd.class,
LeaveCmd.class,
RolesCmd.class,
}
)
public class MembersCmd extends AbstractRootCmd {
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/io/seqera/tower/cli/commands/RolesCmd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.roles.AddCmd;
import io.seqera.tower.cli.commands.roles.DeleteCmd;
import io.seqera.tower.cli.commands.roles.ListCmd;
import io.seqera.tower.cli.commands.roles.PermissionsCmd;
import io.seqera.tower.cli.commands.roles.UpdateCmd;
import io.seqera.tower.cli.commands.roles.ViewCmd;
import picocli.CommandLine.Command;

@Command(
name = "roles",
description = "Manage workspace roles. Custom roles are not available for Seqera Cloud Basic organizations.",
subcommands = {
ListCmd.class,
ViewCmd.class,
AddCmd.class,
UpdateCmd.class,
DeleteCmd.class,
PermissionsCmd.class,
}
)
public class RolesCmd extends AbstractRootCmd {
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.seqera.tower.cli.commands.actions;

import io.seqera.tower.ApiException;
import io.seqera.tower.cli.commands.actions.add.AddBucketCmd;
import io.seqera.tower.cli.commands.actions.add.AddGitHubCmd;
import io.seqera.tower.cli.commands.actions.add.AddTowerCmd;
import io.seqera.tower.cli.exceptions.ShowUsageException;
Expand All @@ -30,7 +31,8 @@
description = "Add a pipeline action",
subcommands = {
AddGitHubCmd.class,
AddTowerCmd.class
AddTowerCmd.class,
AddBucketCmd.class,
}
)
public class AddCmd extends AbstractActionsCmd {
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/io/seqera/tower/cli/commands/actions/UpdateCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
import io.seqera.tower.cli.utils.FilesHelper;
import io.seqera.tower.cli.utils.ModelHelper;
import io.seqera.tower.model.ActionResponseDto;
import io.seqera.tower.model.ActionSource;
import io.seqera.tower.model.BucketActionRequest;
import io.seqera.tower.model.UpdateActionRequest;
import io.seqera.tower.model.WorkflowLaunchRequest;
import picocli.CommandLine;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import static io.seqera.tower.cli.utils.ModelHelper.coalesce;

Expand All @@ -49,6 +52,12 @@ public class UpdateCmd extends AbstractActionsCmd {
@CommandLine.Option(names = {"--new-name"}, description = "Updated action name. Must be unique per workspace. Names consist of alphanumeric, hyphen, and underscore characters.")
public String newName;

@CommandLine.Option(names = {"--marker-file"}, description = "Marker file of a bucket action, relative to the data link path.")
public String markerFile;

@CommandLine.Option(names = {"--events"}, split = ",", description = "Comma-separated bucket events that trigger a bucket action: object:created, object:deleted.")
public List<String> events;

@CommandLine.Mixin
public WorkspaceOptionalOptions workspace;

Expand Down Expand Up @@ -106,6 +115,11 @@ protected Response exec() throws ApiException, IOException {
request.setName(newName != null ? newName : actionName);
request.setLaunch(workflowLaunchRequest);

if (markerFile != null || events != null) {
requireSource(action, ActionSource.bucket, "--marker-file and --events");
request.setBucket(new BucketActionRequest().markerFile(markerFile).events(events));
}

try {
actionsApi().updateAction(action.getId(), request, wspId);
} catch (Exception e) {
Expand All @@ -126,4 +140,11 @@ protected Response exec() throws ApiException, IOException {

return new ActionUpdate(actionName, workspaceRef(wspId), action.getId());
}

// The API refuses a trigger block that does not match the action source, so say which options apply.
private static void requireSource(ActionResponseDto action, ActionSource source, String options) throws TowerException {
if (action.getSource() != source) {
throw new TowerException(String.format("Options %s apply only to %s actions, but action '%s' is a %s action", options, source, action.getName(), action.getSource()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ protected Response exec() throws ApiException, IOException {
request.setName(actionName);
request.setSource(getSource());
request.setLaunch(workflowLaunchRequest);
configureTrigger(request);

if (overwrite) tryDeleteAction(actionName, wspId);

Expand All @@ -121,4 +122,8 @@ private void tryDeleteAction(String actionName, Long wspId) throws ApiException
}

protected abstract ActionSource getSource();

/** Sets the source-specific trigger configuration. GitHub and Seqera Platform actions have none. */
protected void configureTrigger(CreateActionRequest request) {
}
}
Loading
Loading