Skip to content

feat: add pipeline versioning support#582

Open
JaimeSeqLabs wants to merge 45 commits intomasterfrom
PLAT-3660-pipeline-versioning-support
Open

feat: add pipeline versioning support#582
JaimeSeqLabs wants to merge 45 commits intomasterfrom
PLAT-3660-pipeline-versioning-support

Conversation

@JaimeSeqLabs
Copy link
Contributor

@JaimeSeqLabs JaimeSeqLabs commented Feb 19, 2026

Summary

  • Adds pipelines versions subcommand group (list, view, update) to manage pipeline versions
  • Wires --version-id / --version-name into existing commands: pipelines view, pipelines export, pipelines update, and launch
  • pipelines add gains --version-name to name the initial version on creation
  • pipelines update detects when a versionable field change creates a new version: auto-generates a name (mirroring the frontend naming algorithm), assigns it, and promotes it to default. Use --allow-draft to skip auto-naming and keep the new version as an unnamed draft for manual management.
  • pipelines view displays version info (name, default flag, hash) when a version is specified
  • pipelines export includes the version name in the exported JSON so it survives import round-trips
  • Moves pipelines labels commands into a labels/ sub-package mirroring the versions/ structure

Test plan

  • ./gradlew test — all unit tests pass
  • TOWER_CLI=./build/native/nativeCompile/tw ./gradlew test — all unit tests pass in binary mode
  • Manual E2E testing against a Platform backend (see testing guidelines below)

Testing guidelines

Setup

export TOWER_ACCESS_TOKEN="<your-token>"
export TOWER_API_ENDPOINT="<your-platform-api-url>"

PIPELINE_NAME="AutoTest"
PIPELINE_URL="https://github.com/pditommaso/nf-sleep"

# Build the CLI native binary
./gradlew nativeCompile
TW="./build/native/nativeCompile/tw"
# when targeting localhost instance
# TW="./build/native/nativeCompile/tw --insecure"

Test 1: Create pipeline with initial version name

$TW pipelines add -n "$PIPELINE_NAME" --version-name "v1.0" "$PIPELINE_URL"

Verify: Pipeline is created successfully.

Test 2: List versions

$TW pipelines versions list -n "$PIPELINE_NAME"
$TW pipelines versions list -n "$PIPELINE_NAME" --filter "v1.0"
$TW pipelines versions list -n "$PIPELINE_NAME" --is-published
$TW pipelines versions list -n "$PIPELINE_NAME" --max 10 --offset 0

Verify: All list variants return v1.0 as the single published version.

Test 3: View pipeline with version targeting

$TW pipelines view -n "$PIPELINE_NAME" --version-name "v1.0"
$TW pipelines view -n "$PIPELINE_NAME" --version-id "$VERSION_ID"
$TW pipelines view -n "$PIPELINE_NAME"

Verify: The first two show Version Name, Version Is Default, and Version Hash rows. The third shows the default pipeline version.

Test 4: Update non-versionable field (no new version)

$TW pipelines update -n "$PIPELINE_NAME" -d "Updated description"

Verify: Output says "Pipeline updated" without any new version message.

Test 5: Update versionable field (auto-names and promotes new version)

$TW pipelines update -n "$PIPELINE_NAME" --main-script "main.nf"

Verify: Output says "Pipeline updated" followed by "New version 'v1.0-1' created and set as default, available in the Launchpad." (name is derived from the current default version name).

Test 6: Confirm auto-named version appears in version list

$TW pipelines versions list -n "$PIPELINE_NAME"

Verify: Two published versions shown — v1.0 and v1.0-1 (or similar). The new one is marked as default.

Test 6b: Update versionable field with --allow-draft (keeps unnamed draft)

DRAFT_ID=$($TW -o json pipelines update -n "$PIPELINE_NAME" --main-script "main.nf" --allow-draft 2>/dev/null \
  | jq -r '.draftVersionId')
echo "Draft version ID: $DRAFT_ID"

Verify: DRAFT_ID is a non-null version ID. Output includes "New draft version created" message.

Test 7: Rename draft version

$TW pipelines versions update -n "$PIPELINE_NAME" --version-id "$DRAFT_ID" --new-name "v2.0"

Verify: Version updated successfully.

Test 8: Set version as default

$TW pipelines versions update -n "$PIPELINE_NAME" --version-name "v2.0" --set-default

Verify: Version updated successfully.

Test 9: Update a specific version by name

$TW pipelines update -n "$PIPELINE_NAME" --version-name "v1.0" -d "Non-versionable change to v1.0"

Verify: Pipeline updated without creating a new draft.

Test 10: Export with version targeting

$TW pipelines export -n "$PIPELINE_NAME" --version-name "v1.0"
$TW pipelines export -n "$PIPELINE_NAME" --version-id "$VERSION_ID"
$TW pipelines export -n "$PIPELINE_NAME"

Verify: The first two include "version": {"name": "v1.0"} in the JSON. The third (default version) omits the version field.

Test 11: Export + import round-trip

EXPORT_FILE=$(mktemp /tmp/pipeline-export-XXXXXX.json)
$TW pipelines export -n "$PIPELINE_NAME" --version-name "v1.0" "$EXPORT_FILE"
$TW pipelines import -n "${PIPELINE_NAME}_imported" "$EXPORT_FILE"
$TW pipelines view -n "${PIPELINE_NAME}_imported"
$TW pipelines delete -n "${PIPELINE_NAME}_imported"
rm -f "$EXPORT_FILE"

Verify: Imported pipeline exists and can be viewed.

Cleanup

$TW pipelines delete -n "$PIPELINE_NAME"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@tcrespog tcrespog left a comment

Choose a reason for hiding this comment

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

Tested and worked successfully.

Copy link
Contributor

@swingingsimian swingingsimian left a comment

Choose a reason for hiding this comment

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

Only reviewed commands so far, I'll have a 2nd pass at the rest asap.

throw new TowerException("No versions available for the pipeline, check if Pipeline versioning feature is enabled for the workspace");
}

List<PipelineVersionFullInfoDto> versions = response.getVersions().stream()
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not how the API works. Listing versions returns full PipelineDbDtos, not just the version meta data. But if we're aiming to replicate an approximation of the UI behaviour, then this is acceptable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand.
I know the listing returns full DTOs, that is why we are calling the .stream and mapping to get the metadata only.

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.

3 participants