Skip to content

fix: bump tower-java-sdk to 1.230.0, support API 1.230 features and stop launch --wait duplicate runs [COMP-2645] - #692

Closed
cristianrcv wants to merge 16 commits into
feat/NOTASK-api-1.200-catch-upfrom
fix/COMP-2645-sdk-1.230-launch-wait
Closed

cristianrcv wants to merge 16 commits into
feat/NOTASK-api-1.200-catch-upfrom
fix/COMP-2645-sdk-1.230-launch-wait

Conversation

@cristianrcv

@cristianrcv cristianrcv commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This is the last PR of the API catch-up stack. It has three parts:

  1. SDK bump: tower-java-sdk 1.200.0 → 1.230.0. That is the API of the stage release (v27.1.0-cycle75_d932c88) that goes to prod next; prod runs 1.226.0. Published from seqeralabs/tower-sdk-gencode#111.
  2. COMP-2645 fix: tw launch --wait exited 1 after a successful submit when a single status poll failed (a transient 4xx/5xx, or a status the SDK doesn't know). Automation then retried and launched duplicate runs.
  3. Features introduced in API 1.230.0, plus the build.gradle changes they need.

Changes

  • build(deps): bump tower-java-sdk to 1.230.0 COMP-2645 (6352f73)
  • fix(launch): keep waiting through failed status polls after submit COMP-2645 (0038894)
  • fix(build): allow PATCH requests on Java 25 COMP-2645 (d3f2375)
  • fix(build): open the JDK packages for PATCH in the native image too COMP-2645 (cb7e4e0)
  • feat(service-accounts): add service account commands COMP-2645 (11fc328)
  • feat(actions): add and update pipeline-status actions COMP-2645 (38c04da)
  • feat(actions): add 'actions triggers list' and 'actions triggers view' COMP-2645 (09b0c6c)
  • feat(launch): add --fusion-version COMP-2645 (352e0ca)
  • feat(info): list Fusion versions from the compatibility catalog in 'tw info versions' COMP-2645 (b953816)
  • feat(credentials): add credentials federation-setup command COMP-2645 (b255283)
  • feat(compute-envs): add --key-vault-url to azure-cloud COMP-2645 (e288e68)
  • feat(compute-envs): add --billing-export-table to google-cloud COMP-2645 (060c052)
  • feat(agents): set an agent's service account and GitHub App credentials COMP-2645 (b83a26d)
  • feat(lineage): resolve runs by session ID and run name, and show the pipeline COMP-2645 (c95bb45)
  • docs: document the commands and options added for API 1.230.0 COMP-2645 (5c351a5)
  • build(native): register reflection metadata for the API 1.230 commands COMP-2645 (07ea5c8)

SDK bump (first commit)

  • DeleteCredentialsConflictResponseConflict is now ConflictingResource. New optional query params are passed as null.
  • The SDK 1.200 workarounds in the lower PRs are reverted: LogPage.entries is read as a list again, and managed-identity 409s are parsed with their own response class.
  • PLAT-6702 made many response fields JsonNullable. prettyJson drops null properties, so -o json, views and CE export look the same as before.

launch --wait fix

  • A failed poll or an unrecognised status keeps polling with the existing backoff.
  • Polling gives up only on 401/403/404, or after 10 minutes of failed polls. It then exits with code 3, not 1, and prints the run id and a tw runs view hint.
  • UNKNOWN is treated as a failed poll.
  • The compute-env and studio waits share the same loop.

Why build.gradle changes here

tw service-accounts update (API 1.230) is the first PATCH call tw makes. Jersey's SET_METHOD_WORKAROUND reflects into HttpURLConnection.method and HttpsURLConnectionImpl.delegate, and Java 25 blocks that. The JVM reported a bogus "Connection error", and the native binary silently sent POST. Both packages are opened:

  • for the test and run JVMs;
  • in the tw.jar manifest (Add-Opens);
  • as native-image --add-opens.

The two fields are also registered in reflect-config.json.

1.230 features

  • service-accounts (and OrgRole.service_account shown in members).
  • Actions: pipeline-status source, actions triggers list|view.
  • --fusion-version, plus Fusion and catalog versions in tw info versions.
  • credentials federation-setup -p aws|google.
  • azure-cloud --key-vault-url (COMP-2043), google-cloud --billing-export-table (COMP-2502).
  • Agents: --service-account-id and --github-app-credentials-id.
  • Lineage: --run-name (required with --session-id since API 1.211), and the Pipeline row.

JIRA

COMP-2645 (related: COMP-2644)

Verification

🤖 Generated with Claude Code

cristianrcv and others added 16 commits October 2, 2026 19:29
Platform API 1.230.0 is what the STABLE-27.1.x stage image
(v27.1.0-cycle75_d932c88) serves and will reach prod next; prod runs
1.226.0. tw was built against 1.200.0, so it lacked enum values the
server already returns (OrgRole.service_account, WspRole.project,
Action.Source.pipeline_status, ...), which surface as "Connection error".

Adaptations the new SDK requires:
- DeleteCredentialsConflictResponseConflict was renamed to
  ConflictingResource, and the managed-credentials 409 body now has its
  own DeleteManagedCredentialsConflictResponse, so managed identities
  stop parsing it as the credentials one.
- LogPage.entries is now typed List<String>, so runs view log and
  studios logs read it directly instead of converting an Object.
- listDataLinks, describeDataLink and listWorkflowTasks gained optional
  query params (creationSource, includeVersioning, process); pass null
  to keep current behaviour.
- resolveLineage gained the runName param (API 1.211). Pass null for
  now; lineage resolve exposes it in a later commit.
- PLAT-6702 marked many response fields nullable, which the generator
  maps to JsonNullable. NON_NULL does not omit a JsonNullable holding an
  explicit server null, so -o json, the config views and CE export
  started printing null keys. prettyJson now drops null properties from
  the tree, restoring the previous output, and the views and export use
  it.
- Regenerated reflect-config.json with runReflectionConfigGenerator on
  GraalVM 25. The GraalVM 25 agent writes the unified
  reachability-metadata.json and dropped the resource entries, so only
  the regenerated io.seqera.tower.model reflection entries were merged
  back into the legacy file; classes no longer in the SDK were removed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…OMP-2645]

tw launch --wait exited 1 after a successful submit when a single
describeWorkflow poll failed: checkWorkflowStatus turned any ApiException
into null, waitStatus mapped null past every target, and the loop ended
as [ERROR]. An unknown status value threw ProcessingException, which
escaped as "Connection error". Automation that retries on a non-zero
exit then launched the pipeline again while the first run kept going,
duplicating runs and compute spend. A single transient 400/5xx was
enough, e.g. during the 2026-10-01 Cloud DB pool exhaustion.

waitStatus now polls a Callable. A failed poll or an unrecognized
status keeps polling with the existing backoff. It gives up only on
401/403/404 or after polls have kept failing for 10 minutes, and then
throws StatusCheckFailedException, which exits with code 3 instead of 1.
A run in UNKNOWN status is treated as a failed poll, since Platform lost
contact but the run may still be running. Launch reports the run id and
a tw runs view hint. Exit 1 is now only for runs that actually ended in
another end state. The compute-env add/delete and studio waits share
the same loop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Service account update is the first PATCH call in tw. The SDK's ApiClient enables Jersey's
SET_METHOD_WORKAROUND, which sets HttpURLConnection.method (and HttpsURLConnectionImpl.delegate for
HTTPS) by reflection. On Java 25 that throws InaccessibleObjectException, which tw reported as a
connection error. Open both packages to the test and run JVMs, and to java -jar tw.jar through the
Add-Opens manifest attribute. The tracing-agent blocks now append to the JVM args instead of replacing
them. The native image needs reflection entries for the same fields (next commit).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…COMP-2645]

Native images enforce module encapsulation for setAccessible as well. Even with reflection entries for
HttpURLConnection.method and HttpsURLConnectionImpl.delegate, the native binary failed with
'module java.base does not opens java.net'. Pass the same --add-opens to native-image, and register
both fields in reflect-config.json: the tracing agent cannot record them, because its access filter
only keeps io.seqera callers. Verified with the native binary: service-accounts update sends PATCH
over HTTP (MockServer suite) and over HTTPS (local TLS server).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Platform API 1.230.0 (PLAT-6535) made the organization service account
endpoints generally available, but tw had no way to manage them.

Add 'tw service-accounts' with list, add, view, update and delete over
/orgs/{orgId}/service-accounts. 'list --workspace' uses
/orgs/{orgId}/workspaces/{workspaceId}/service-accounts to show the
accounts assigned to one workspace; that endpoint pages with an opaque
token, so every page is fetched and --offset/--max are applied locally.

Members of the new OrgRole.service_account now render as
SERVICE_ACCOUNT in 'tw members list' instead of the raw enum value.

Note: update uses PATCH, which Jersey's HttpURLConnection connector only
sends through a reflective workaround that needs
--add-opens java.base/java.net=ALL-UNNAMED on Java 16+.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Platform API 1.230.0 (PLAT-6639) added the pipeline_status action source:
an action that launches its pipeline when a run of a watched Launchpad
pipeline ends in a chosen state. tw had no way to create one.

- 'actions add pipeline-status --watch-pipeline-id <id> --run-status
  <state>' sends CreateActionRequest.pipelineStatus.
- 'actions update --watch-pipeline-id/--run-status' sends a partial
  UpdateActionRequest.pipelineStatus; the API keeps omitted fields.
- Only SUCCEEDED, FAILED and CANCELLED can fire the action, as in the UI
  and the API's terminal-state check, so tw refuses other states before
  calling the API instead of hiding the 400 behind its generic error.
- 'actions view' shows the watched pipeline and run state.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…' [COMP-2645]

Platform API 1.230.0 records every time an action fires, with how it
ended (launched, launch failed, or suppressed by the cycle or rate guard)
and the event that caused it. The UI shows it as the action's trigger
history; tw had no way to see why an action did or did not launch.

- 'actions triggers list -n|-i <action> [--outcome ...] [--max/--page/
  --offset]' calls GET /actions/{id}/triggers, newest first.
- 'actions triggers view -n|-i <action> --trigger-id <id>' calls
  GET /actions/{id}/triggers/{triggerId} and prints the event payload.

The event summary, outcome detail and payload come from outside
Platform (a GitHub commit message, an S3 notification), and the API
marks them as untrusted. The console output strips their control
characters through FormatHelper.formatUntrusted so they cannot inject
terminal escape sequences. JSON output keeps them as sent, escaped by
the JSON encoder.

The firedAfterOrEqual/firedBeforeOrEqual filters are not exposed yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WorkflowLaunchRequest gained fusionVersion in API 1.230.0 (Component
Compatibility Catalog, COMP-2121), but it could not be set from the CLI.
It is the Fusion version to run the workflow with; it must exist in the
Platform catalog and only applies when the compute environment enables
Fusion v2.

--fusion-version is JsonNullable on the request, so it joins
NullableLaunchOptions and is applied only when given, in launch,
pipelines add|update, actions add|update and runs relaunch; the guard
test covers it automatically. A stored value is inherited by pipelines
update and runs relaunch through ModelHelper's JSON copy; the tests now
assert that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…w info versions' [COMP-2645]

--fusion-version must name a version Platform knows, but 'tw info
versions' only listed the Nextflow versions configured in
GET /platform/versions (API 1.190.0). API 1.230.0 (COMP-2121) added
GET /catalog/components/{key}/versions: the catalog's non-yanked
versions of one component with its default, optionally constrained by
compatibility with other components. It only exists when the catalog is
enabled, which /platform/versions reports as catalogEnabled.

The command now mirrors the launch form: it reads /platform/versions,
and when the catalog is on it lists the requested component
(-c nextflow|fusion) from the catalog, constrained to this Platform
release (the form passes the same platform context). --nextflow/--fusion
narrow the list to versions compatible with the given ones, e.g. the
Fusion versions that work with a Nextflow pick. Without the catalog only
Nextflow versions exist, so asking for another component or a
compatibility filter fails instead of returning a list that silently
ignores the request.

The aggregated /catalog/versions is not used: across all components it
also returns platform, wave and sched, which have no launch option.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
API 1.230 added GET /credentials/federation-setup, which returns the
values an administrator must configure in AWS or Google Cloud before
creating workload identity credentials: the Platform public address,
audience and the OIDC subject claims Platform mints for the workspace.
tw added workload identity credentials in e6a8fb9 but users had no way
to get these values from the CLI.

Platform switches on 'aws' and 'google' exactly and returns no values
for anything else, so a typo such as '-p AWS' would print 'No setup
values', which reads as nothing to configure. The provider is an enum,
so picocli rejects unknown values.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AzCloudConfig.keyVaultUrl (API 1.229.0) selects a per-CE Key Vault for
pipeline secrets (COMP-2043). tw never set it, so Azure Cloud secrets
fell back to an installation default that Cloud does not set. Only
Intelligent Compute resolves these secrets, so the option documents
that it is only used with --sched-enabled.

keyVaultUrl is JsonNullable, so it is set only when given: setting null
would send an explicit null.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…2645]

API 1.230.0 added SchedConfig.billingExportTable: the BigQuery table
holding the Cloud Billing export, which enables cost reporting for an
Intelligent Compute CE. tw could not set it. Platform rejects it on
other providers, so only google-cloud exposes the option, matching the
tower-web form.

billingExportTable is JsonNullable, so it is set only when given:
setting null would send an explicit null.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ls [COMP-2645]

API 1.230.0 lets an agent run as an organization service account and
use GitHub App credentials to clone, commit and push, but tw could not
set either.

- 'agents add|update --service-account-id --github-app-credentials-id'
  send serviceAccountId and githubAppCredentialId; update keeps the
  stored values when the options are omitted.
- 'agents view' shows the service account, the GitHub App credentials
  and who created the agent (createdByUserName, API 1.211.0).
- 'agents runs list --filter' documents the serviceAccountId and
  serviceAccountName keywords.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…pipeline [COMP-2645]

API 1.211.0 made GET /lineage/resolve take runName with sessionId: a
resumed run shares the session ID of its parent, so the session ID alone
does not identify one run. 'lineage resolve --session-id' now requires
--run-name and sends both; the SDK bump passed null until now.

API 1.230.0 added the pipeline to lineage records (DisplayData
pipelineName/pipelineId) and the pipeline/pipelineId search qualifiers.
'lineage view' shows a Pipeline row and 'lineage search --query'
documents the new qualifiers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…645]

Add the service accounts section and the 'Other additions' bullets for
the API 1.230.0 items: --fusion-version, the pipeline-status action
source and 'actions triggers', and 'credentials federation-setup'. The
new 'tw info versions' wording covers Fusion as well as Nextflow. Usage
examples are plain bash blocks (markdownlint MD014).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s [COMP-2645]

The native binary instantiates picocli commands and serializes -o json
responses by reflection, so every new tw class needs an entry. Copied
from the full tracing-agent run of the catch-up branch: service
accounts, pipeline-status actions, action triggers and credentials
federation-setup. Response classes register all declared fields, since
Jackson serializes every field for -o json, not only the ones a traced
test read.

Also adds the entries that run captured for existing classes:
Tower.main and the AzBatchForgePlatform head/worker pool option
constructors.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@cristianrcv
cristianrcv added this pull request to stack #693 October 2, 2026 18:17
@cristianrcv
cristianrcv requested review from a team and jonmarti October 2, 2026 18:17

@jonmarti jonmarti left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cristianrcv

Copy link
Copy Markdown
Contributor Author

Superseded by #701 (API 1.230, COMP-2645). Pipeline-status actions and action triggers moved to #703 (actions), and the agent service-account options to #702 (agents). @jonmarti, your approval here doesn't carry over; #701 has the same SDK bump, the launch --wait fix and the build.gradle changes.

The stack is now: #685 (1.101) → #686 (1.113) → #697 (1.145) → #698 (1.167) → #699 (1.190) → #700 (1.200) → #701 (1.230) → #702 (agents) → #703 (actions). The code at the top of the stack is identical to the old one; only the grouping changed. Agents and actions changes are grouped into one PR each instead of being spread across API versions. New branches were used instead of force-pushing.

@cristianrcv cristianrcv closed this Oct 5, 2026
@cristianrcv
cristianrcv removed this pull request from stack #693 October 5, 2026 16:19
@cristianrcv
cristianrcv deleted the fix/COMP-2645-sdk-1.230-launch-wait branch October 8, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants