Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/workflows/changesets-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- "release/**"
paths:
- "packages/**"
- ".changeset/**"
Expand Down Expand Up @@ -56,16 +57,21 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=$(gh pr list --head changeset-release/main --json number --jq '.[0].number')
# On main: PR branch is `changeset-release/main`.
# On release/X.Y.x: PR branch is `changeset-release/release/X.Y.x`.
# Same source-branch lookup either way.
SOURCE_BRANCH="${GITHUB_REF_NAME}"
PR_BRANCH="changeset-release/${SOURCE_BRANCH}"
PR_NUMBER=$(gh pr list --head "$PR_BRANCH" --json number --jq '.[0].number')
if [ -n "$PR_NUMBER" ]; then
git fetch origin changeset-release/main
git fetch origin "+${PR_BRANCH}:refs/remotes/origin/${PR_BRANCH}"
# we arbitrarily reference the version of the cli package here; it is the same for all package releases
VERSION=$(git show origin/changeset-release/main:packages/cli-v3/package.json | jq -r '.version')
VERSION=$(git show "origin/${PR_BRANCH}:packages/cli-v3/package.json" | jq -r '.version')
gh pr edit "$PR_NUMBER" --title "chore: release v$VERSION"

# Enhance the PR body with a clean, deduplicated summary
RAW_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body')
ENHANCED_BODY=$(CHANGESET_PR_BODY="$RAW_BODY" node scripts/enhance-release-pr.mjs "$VERSION")
ENHANCED_BODY=$(CHANGESET_PR_BODY="$RAW_BODY" SOURCE_BRANCH="$SOURCE_BRANCH" node scripts/enhance-release-pr.mjs "$VERSION")
if [ -n "$ENHANCED_BODY" ]; then
gh api repos/triggerdotdev/trigger.dev/pulls/"$PR_NUMBER" \
-X PATCH \
Expand Down
26 changes: 23 additions & 3 deletions .github/workflows/publish-webapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
type: string
required: false
default: ""
is_latest:
description: "Whether this version should also tag :v4-beta. release.yml computes via version comparison."
type: boolean
required: false
default: false
secrets:
SENTRY_AUTH_TOKEN:
required: false
Expand Down Expand Up @@ -52,16 +57,31 @@ 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
# Strip leading 'v' to get bare version (vX.Y.Z -> X.Y.Z)
VERSION="${STEPS_GET_TAG_OUTPUTS_TAG#v}"
MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2)

# Per-line floating tag — analog of npm "release-X.Y" dist-tag.
# Always set on a semver build so line-pinned consumers always get
# the latest patch on their line, regardless of whether it's the
# global :v4-beta / :latest.
image_tags=$image_tags,$ref_without_tag:release-${MAJOR_MINOR}

# Global :v4-beta updated only when release.yml decided this version
# is the highest-ever. Hotfixes on lagged lines must NOT clobber it.
# TODO: switch to :latest on v4 GA.
if [[ "${INPUTS_IS_LATEST}" == true ]]; then
image_tags=$image_tags,$ref_without_tag:v4-beta
fi
fi

echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT"
echo "::notice::Docker tags: ${image_tags}"
env:
STEPS_GET_TAG_OUTPUTS_TAG: ${{ steps.get_tag.outputs.tag }}
STEPS_GET_TAG_OUTPUTS_IS_SEMVER: ${{ steps.get_tag.outputs.is_semver }}
INPUTS_IS_LATEST: ${{ inputs.is_latest }}

- name: 📝 Set the build info
id: set_build_info
Expand Down
22 changes: 19 additions & 3 deletions .github/workflows/publish-worker-v4.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ on:
type: string
required: false
default: ""
is_latest:
description: "Whether this version should also tag :v4-beta. release.yml computes via version comparison."
type: boolean
required: false
default: false
push:
tags:
- "re2-test-*"
Expand Down Expand Up @@ -68,17 +73,28 @@ 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
VERSION="${STEPS_GET_TAG_OUTPUTS_TAG#v}"
MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2)

# Per-line floating tag — line-pinned consumers always get the latest
# patch on their line, regardless of whether it's the global :v4-beta.
image_tags=$image_tags,$ref_without_tag:release-${MAJOR_MINOR}

# Global :v4-beta updated only when this is the highest-ever version.
# TODO: switch to :latest on v4 GA.
if [[ "${INPUTS_IS_LATEST}" == true ]]; then
image_tags=$image_tags,$ref_without_tag:v4-beta
fi
fi

echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT"
echo "::notice::Docker tags: ${image_tags}"
env:
STEPS_GET_REPOSITORY_OUTPUTS_REPO: ${{ steps.get_repository.outputs.repo }}
STEPS_GET_TAG_OUTPUTS_TAG: ${{ steps.get_tag.outputs.tag }}
STEPS_GET_TAG_OUTPUTS_IS_SEMVER: ${{ steps.get_tag.outputs.is_semver }}
INPUTS_IS_LATEST: ${{ inputs.is_latest }}

- name: 🐙 Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ on:
description: The image tag to publish
required: true
type: string
is_latest:
description: "Whether this version is the highest released version. Drives :v4-beta / :latest tag updates. release.yml computes this via version comparison."
required: false
type: boolean
default: false
secrets:
DOCKERHUB_USERNAME:
required: false
Expand Down Expand Up @@ -73,6 +78,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
with:
image_tag: ${{ inputs.image_tag }}
is_latest: ${{ inputs.is_latest }}

publish-worker:
needs: [typecheck]
Expand All @@ -95,3 +101,4 @@ jobs:
uses: ./.github/workflows/publish-worker-v4.yml
with:
image_tag: ${{ inputs.image_tag }}
is_latest: ${{ inputs.is_latest }}
96 changes: 85 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
types: [closed]
branches:
- main
- "release/**"
workflow_dispatch:
inputs:
type:
Expand Down Expand Up @@ -38,7 +39,7 @@ jobs:
github.repository == 'triggerdotdev/trigger.dev' &&
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
github.event.pull_request.head.ref == 'changeset-release/main'
startsWith(github.event.pull_request.head.ref, 'changeset-release/')
steps:
- name: Show release summary
env:
Expand All @@ -58,28 +59,39 @@ jobs:
github.repository == 'triggerdotdev/trigger.dev' &&
(
(github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'release') ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.head.ref == 'changeset-release/main')
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'changeset-release/'))
)
outputs:
published: ${{ steps.changesets.outputs.published }}
published_packages: ${{ steps.changesets.outputs.publishedPackages }}
published_package_version: ${{ steps.get_version.outputs.package_version }}
is_latest: ${{ steps.compare.outputs.is_latest }}
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
with:
fetch-depth: 0
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.sha }}

- name: Verify ref is on main
- name: Verify ref is on main or a release branch
if: github.event_name == 'workflow_dispatch'
run: |
if ! git merge-base --is-ancestor "${GITHUB_EVENT_INPUTS_REF}" origin/main; then
echo "Error: ref must be an ancestor of main (i.e., already merged)"
exit 1
fi
env:
GITHUB_EVENT_INPUTS_REF: ${{ github.event.inputs.ref }}
run: |
set -e
if git merge-base --is-ancestor "${GITHUB_EVENT_INPUTS_REF}" origin/main; then
echo "Ref is reachable from main."
exit 0
fi
# Look for any origin/release/* branch that contains this ref.
for branch in $(git branch -r --list 'origin/release/*' --format '%(refname:short)'); do
if git merge-base --is-ancestor "${GITHUB_EVENT_INPUTS_REF}" "${branch}"; then
echo "Ref is reachable from ${branch}."
exit 0
fi
done
echo "Error: ref must be reachable from main or a release/* branch." >&2
exit 1

- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
Expand Down Expand Up @@ -109,11 +121,62 @@ jobs:
- name: Type check
run: pnpm run typecheck --filter "@trigger.dev/*" --filter "trigger.dev"

# Decide whether this publish should become the new "latest" everywhere
# (npm dist-tag, Docker `:v4-beta` / `:latest`, GitHub release "Latest"
# badge, marketing-site changelog).
#
# Right rule: compare the new version to the current `latest`. NEW > CURRENT
# => becomes latest. Otherwise => goes to a per-line dist-tag (release-X.Y).
#
# Handles two scenarios with one rule:
# - Lagged hotfix (main shipped 4.6.0, hotfix 4.4.7 from release/4.4.x):
# 4.4.7 < 4.6.0 -> NOT latest. dist-tag release-4.4.
# - Fresh hotfix while main has unreleased work (main released 4.4.5,
# PR #3173 merged but unreleased; hotfix 4.4.6 from release/4.4.x):
# 4.4.6 > 4.4.5 -> IS latest. Customers running `npm install` get it.
#
# Source of truth: npm's `latest` dist-tag for the canonical package
# (`@trigger.dev/sdk`). All public packages are version-locked via the
# `fixed` config, so any one is canonical.
- name: Compare new version to current latest
id: compare
run: |
set -euo pipefail
NEW=$(jq -r '.version' packages/cli-v3/package.json)
CURRENT=$(npm view @trigger.dev/sdk dist-tags.latest 2>/dev/null || true)
if [ -z "$CURRENT" ] || [ "$CURRENT" = "undefined" ]; then
CURRENT="0.0.0"
fi

# sort -V is semver-aware. NEW strictly greater than CURRENT => becomes latest.
HIGHER=$(printf '%s\n%s\n' "$NEW" "$CURRENT" | sort -V | tail -1)
if [ "$HIGHER" = "$NEW" ] && [ "$NEW" != "$CURRENT" ]; then
IS_LATEST=true
DIST_TAG=""
else
IS_LATEST=false
DIST_TAG="release-$(echo "$NEW" | cut -d. -f1,2)"
fi

{
echo "new=${NEW}"
echo "current=${CURRENT}"
echo "is_latest=${IS_LATEST}"
echo "dist_tag=${DIST_TAG}"
} >> "$GITHUB_OUTPUT"

echo "::notice::Publishing ${NEW}; current latest=${CURRENT}; is_latest=${IS_LATEST}; dist_tag=${DIST_TAG:-latest}"

- name: Publish
id: changesets
uses: changesets/action@6a0a831ff30acef54f2c6aa1cbbc1096b066edaf # v1.7.0
with:
publish: pnpm run changeset:release
# When this publish lags (lower than current latest), pass --tag release-<M.m>
# so npm's `latest` dist-tag is not touched. Otherwise default (= latest).
# `release-` prefix because npm rejects dist-tag names that parse as a valid
# semver range (`v4.4`, `4.4`, `4.4.x` would all be rejected).
# Build was done above; this step only publishes.
publish: ${{ steps.compare.outputs.dist_tag != '' && format('pnpm exec changeset publish --tag {0}', steps.compare.outputs.dist_tag) || 'pnpm exec changeset publish' }}
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -133,13 +196,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 }}
IS_LATEST: ${{ steps.compare.outputs.is_latest }}
run: |
VERSION="${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}"
node scripts/generate-github-release.mjs "$VERSION" > /tmp/release-body.md
# --target dropped: changesets created the per-package tags on the
# release commit (which lives on main OR a release/* branch); the
# tag itself carries the right commit, no need to pin --target.
# --latest set explicitly: GitHub auto-detect uses publish date,
# which would mark a lagged hotfix as "Latest" by accident.
gh release create "v${VERSION}" \
--title "trigger.dev v${VERSION}" \
--notes-file /tmp/release-body.md \
--target main
--latest="${IS_LATEST}"

- name: Create and push Docker tag
if: steps.changesets.outputs.published == 'true'
Expand Down Expand Up @@ -176,6 +245,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
with:
image_tag: v${{ needs.release.outputs.published_package_version }}
is_latest: ${{ needs.release.outputs.is_latest == 'true' }}

# Trigger Helm chart release directly via workflow_call (same GITHUB_TOKEN
# limitation as the Docker path). Runs after Docker images are published so
Expand Down Expand Up @@ -247,7 +317,11 @@ jobs:
token: ${{ secrets.CROSS_REPO_PAT }}
repository: triggerdotdev/trigger.dev-site-v3
event-type: new-release
client-payload: '{"version": "${{ needs.release.outputs.published_package_version }}"}'
# is_latest is included so the marketing site's changelog can
# decide how to render lagged-line releases (e.g. demote to a
# secondary section, or skip headline placement). Existing site
# consumers ignoring the field are unaffected.
client-payload: '{"version": "${{ needs.release.outputs.published_package_version }}", "is_latest": ${{ needs.release.outputs.is_latest }}}'

# The prerelease job needs to be on the same workflow file due to a limitation related to how npm verifies OIDC claims.
prerelease:
Expand Down
Loading
Loading