From 424de1a33c738f49f9c90f55d79224302d542335 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 15 May 2026 15:50:14 +0100 Subject: [PATCH] feat(ci): support release candidates via changesets pre mode Detect prereleases by hyphen-in-version in release.yml and: - mark the GitHub release with --prerelease (no Latest badge) - skip the marketing-site changelog dispatch (no PR per RC) - keep the dispatch firing for stable releases Also fix the CLI version-check (initialBanner.ts) to use semver.lt instead of locale string compare so users on an RC are correctly told to upgrade once the matching stable ships. Removes the stale :v4-beta Docker floating tag from the webapp and worker-v4 publish workflows. v4 is GA; the tag is misleading and inconsistent with the npm side (where v4-beta is already frozen at 4.0.4). Self-hosters should pin to a versioned tag. --- .github/workflows/publish-webapp.yml | 6 ------ .github/workflows/publish-worker-v4.yml | 6 ------ .github/workflows/release.yml | 17 +++++++++++++++-- packages/cli-v3/src/utilities/initialBanner.ts | 13 ++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/publish-webapp.yml b/.github/workflows/publish-webapp.yml index 036d65728ed..466eaf855c0 100644 --- a/.github/workflows/publish-webapp.yml +++ b/.github/workflows/publish-webapp.yml @@ -53,12 +53,6 @@ jobs: ref_without_tag=ghcr.io/triggerdotdev/trigger.dev image_tags=$ref_without_tag:${STEPS_GET_TAG_OUTPUTS_TAG} - # if tag is a semver, also tag it as v4 - if [[ "${STEPS_GET_TAG_OUTPUTS_IS_SEMVER}" == true ]]; then - # TODO: switch to v4 tag on GA - image_tags=$image_tags,$ref_without_tag:v4-beta - fi - # when pushing the mutable main tag, also push an immutable-by-convention # full-commit-sha tag so a commit can be resolved to a specific digest if [[ "${STEPS_GET_TAG_OUTPUTS_TAG}" == "main" ]]; then diff --git a/.github/workflows/publish-worker-v4.yml b/.github/workflows/publish-worker-v4.yml index c3b72c6b7d9..6ed490c9471 100644 --- a/.github/workflows/publish-worker-v4.yml +++ b/.github/workflows/publish-worker-v4.yml @@ -68,12 +68,6 @@ jobs: ref_without_tag=ghcr.io/triggerdotdev/${STEPS_GET_REPOSITORY_OUTPUTS_REPO} image_tags=$ref_without_tag:${STEPS_GET_TAG_OUTPUTS_TAG} - # if tag is a semver, also tag it as v4 - if [[ "${STEPS_GET_TAG_OUTPUTS_IS_SEMVER}" == true ]]; then - # TODO: switch to v4 tag on GA - image_tags=$image_tags,$ref_without_tag:v4-beta - fi - echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT" env: STEPS_GET_REPOSITORY_OUTPUTS_REPO: ${{ steps.get_repository.outputs.repo }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 07af45a8a40..d352752fb0d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,6 +64,7 @@ jobs: published: ${{ steps.changesets.outputs.published }} published_packages: ${{ steps.changesets.outputs.publishedPackages }} published_package_version: ${{ steps.get_version.outputs.package_version }} + is_prerelease: ${{ steps.get_version.outputs.is_prerelease }} steps: - name: Checkout repo uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # zizmor: ignore[artipacked] needs persisted git creds for tag push; no artifact upload here so no leak path @@ -124,6 +125,12 @@ jobs: run: | package_version=$(echo "${STEPS_CHANGESETS_OUTPUTS_PUBLISHEDPACKAGES}" | jq -r '.[0].version') echo "package_version=${package_version}" >> "$GITHUB_OUTPUT" + # Any semver with a hyphen is a prerelease (e.g. 4.5.0-rc.0, 0.0.0-snapshot-...) + if [[ "${package_version}" == *-* ]]; then + echo "is_prerelease=true" >> "$GITHUB_OUTPUT" + else + echo "is_prerelease=false" >> "$GITHUB_OUTPUT" + fi env: STEPS_CHANGESETS_OUTPUTS_PUBLISHEDPACKAGES: ${{ steps.changesets.outputs.publishedPackages }} @@ -133,13 +140,19 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_PR_BODY: ${{ github.event.pull_request.body }} STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION: ${{ steps.get_version.outputs.package_version }} + STEPS_GET_VERSION_OUTPUTS_IS_PRERELEASE: ${{ steps.get_version.outputs.is_prerelease }} run: | VERSION="${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}" node scripts/generate-github-release.mjs "$VERSION" > /tmp/release-body.md + PRERELEASE_FLAG="" + if [ "${STEPS_GET_VERSION_OUTPUTS_IS_PRERELEASE}" = "true" ]; then + PRERELEASE_FLAG="--prerelease" + fi gh release create "v${VERSION}" \ --title "trigger.dev v${VERSION}" \ --notes-file /tmp/release-body.md \ - --target main + --target main \ + $PRERELEASE_FLAG - name: Create and push Docker tag if: steps.changesets.outputs.published == 'true' @@ -239,7 +252,7 @@ jobs: dispatch-changelog: name: 📝 Dispatch changelog PR needs: [release, update-release] - if: needs.release.outputs.published == 'true' + if: needs.release.outputs.published == 'true' && needs.release.outputs.is_prerelease != 'true' runs-on: ubuntu-latest permissions: {} steps: diff --git a/packages/cli-v3/src/utilities/initialBanner.ts b/packages/cli-v3/src/utilities/initialBanner.ts index c30deeee7fe..74e217c6e61 100644 --- a/packages/cli-v3/src/utilities/initialBanner.ts +++ b/packages/cli-v3/src/utilities/initialBanner.ts @@ -1,5 +1,6 @@ import chalk from "chalk"; import { getLatestVersion } from "fast-npm-meta"; +import * as semver from "semver"; import { VERSION } from "../version.js"; import { chalkGrey, chalkRun, chalkTask, chalkWorker, logo } from "./cliOutput.js"; import { logger } from "./logger.js"; @@ -105,18 +106,16 @@ async function doUpdateCheck(): Promise { return; } - const compareVersions = (a: string, b: string) => - a.localeCompare(b, "en-US", { numeric: true }); - - const comparison = compareVersions(VERSION, meta.version); - - if (comparison === -1) { + // Use real semver comparison (loose) so prereleases sort correctly against + // their stable counterpart — e.g. a user on `4.5.0-rc.0` sees `4.5.0` as + // newer. String/locale comparison gets this wrong for `X.Y.Z-rc.N` vs `X.Y.Z`. + if (semver.lt(VERSION, meta.version, true)) { return meta.version; } return; } catch (err) { - // ignore error + // ignore error (covers both network failures and any version-parse oddities) logger.debug(err); return;