Skip to content

Releasing

Wes Key edited this page Sep 15, 2026 · 2 revisions

Releases are cut by merging pull requests. Nothing is tagged or published by hand.

The Release workflow (.github/workflows/release.yml) runs on every merge into main and releases whatever the PR changed:

The PR… On merge
has a release:* label the CLI is released
changes the extension package the extension is published to the Marketplace
does both the CLI is released first, then the extension is published
does neither nothing is released

Either way, the PR gets a comment saying what was released, or what failed.

CLI releases

Before merging a PR, label it with exactly one of:

Label Bump For
release:patch v1.2.3 → v1.2.4 fixes
release:minor v1.2.3 → v1.3.0 new flags, task inputs, report fields
release:major v1.2.3 → v2.0.0 anything a pipeline has to change for

Merging it:

  1. works out the next version from the newest stable vX.Y.Z tag
  2. tags the merge commit, re-runs vet and tests on it, builds the Linux and Windows binaries, and publishes a GitHub Release marked latest, with notes listing the PRs merged since the previous release
  3. updates the README's release badge

A PR without a label releases no CLI, so several PRs can land and go out together under the next labelled one.

Extension releases

No label is needed. If a merged PR changed anything that goes into the extension package, the extension is published:

  • extension/tab/, extension/images/ and the task folders under extension/tasks/
  • extension/vss-extension.json and extension/overview.md (the Marketplace listing)
  • extension/package.json and package-lock.json

Changes to the extension's READMEs, tools/, dev/ and configs/ don't count, since none of them are packaged.

Order matters. When a PR releases both, the extension waits for the CLI release, because the install task installs the newest stable release: tasks must never reach agents before the binary they install. If the CLI release fails, the extension is not published.

Version. The extension's version is independent of the CLI's: 0.<minor>.<patch>, with the patch one past the newest version already on the Marketplace. The major stays 0 because pipelines reference the tasks as tf-snag@0. Bump the minor in extension/vss-extension.json for a deliberate step.

Visibility. The extension publishes privately, shared with the organisation in the ADO_ORG variable, until the repo variable EXTENSION_PUBLIC is set to true. The Marketplace only accepts public extensions from a verified publisher.

CI packages the extension on every PR exactly as a release would, so a manifest the Marketplace would reject fails before the merge.

Details, including the manual dev channel, are in the extension README.

Releasing by hand

Run Release from the Actions tab on main, and choose the CLI bump (or none) and whether to publish the extension.

To publish only the extension, or to the dev channel for testing before a merge, run Publish ADO extension instead.

Pre-releases

Push a hyphenated tag by hand. CI builds it and publishes a GitHub pre-release; nothing goes to the Marketplace.

git tag v1.3.0-dev.1 && git push origin v1.3.0-dev.1

A plain vX.Y.Z tag pushed by hand fails CI and publishes nothing, so every stable release goes through a PR. Delete one with git push origin :refs/tags/vX.Y.Z.

Release badge

The README's release badge is a shields.io endpoint badge that reads tf-snag-release.json from a public gist, which the Release workflow rewrites after each CLI release. It needs the repo variable GIST_ID and the secret GIST_TOKEN, a fine-grained token with Gists: Read and write. Without GIST_ID the step is skipped.

Clone this wiki locally