name: ๐Ÿฆ‹ Changesets Release on: pull_request: types: [closed] branches: - main workflow_dispatch: inputs: type: description: "Select release type" required: true type: choice options: - release - prerelease default: "prerelease" ref: description: "The ref (branch, tag, or SHA) to checkout and release from" required: true type: string prerelease_tag: description: "The npm dist-tag for the prerelease (e.g., 'v4-prerelease')" required: false type: string default: "prerelease" concurrency: group: ${{ github.workflow }} cancel-in-progress: false jobs: show-release-summary: name: ๐Ÿ“‹ Release Summary runs-on: ubuntu-latest permissions: {} if: | 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' steps: - name: Show release summary env: PR_BODY: ${{ github.event.pull_request.body }} run: | echo "$PR_BODY" | sed -n '/^# Releases/,$p' >> "$GITHUB_STEP_SUMMARY" release: name: ๐Ÿš€ Release npm packages runs-on: ubuntu-latest environment: npm-publish permissions: contents: write packages: write id-token: write if: | 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') ) outputs: 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # 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 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 }} - name: Setup pnpm uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 with: version: 10.33.2 - name: Setup node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22.23.1 cache: "pnpm" # npm v11.5.1 or newer is required for OIDC support # https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/#whats-new - name: Setup npm 11.x for OIDC run: npm install -g npm@11.6.4 - name: Install dependencies run: pnpm install --frozen-lockfile - name: Generate Prisma client run: pnpm run generate - name: Build run: pnpm run build --filter "@trigger.dev/*" --filter "trigger.dev" - name: Type check run: pnpm run typecheck --filter "@trigger.dev/*" --filter "trigger.dev" - name: Publish id: changesets uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0 with: publish: pnpm run changeset:release createGithubReleases: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Show package version if: steps.changesets.outputs.published == 'true' id: get_version 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 }} - name: Create unified GitHub release if: steps.changesets.outputs.published == 'true' env: 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}" # On the pull_request merge event RELEASE_PR_BODY is the merged "Version Packages" # PR body, which holds the changelog. On a workflow_dispatch re-run (the recovery # path after a failed merge-triggered run) there is no pull_request context, so it's # empty. Fall back to the body of the most recently merged changeset-release/main PR # so the "What's changed" section is still populated. if [ -z "${RELEASE_PR_BODY}" ]; then echo "RELEASE_PR_BODY is empty (workflow_dispatch re-run); fetching merged changeset-release/main PR body" RELEASE_PR_BODY="$(gh pr list --repo triggerdotdev/trigger.dev \ --head changeset-release/main --state merged \ --json body,mergedAt --limit 10 \ -q 'sort_by(.mergedAt) | reverse | .[0].body')" fi export RELEASE_PR_BODY 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 \ $PRERELEASE_FLAG - name: Create and push Docker tag if: steps.changesets.outputs.published == 'true' run: | set -e git tag "v.docker.${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}" git push origin "v.docker.${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}" env: STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION: ${{ steps.get_version.outputs.package_version }} - name: Create and push Helm chart tag if: steps.changesets.outputs.published == 'true' run: | set -e git tag "helm-v${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}" git push origin "helm-v${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}" env: STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION: ${{ steps.get_version.outputs.package_version }} # Trigger Docker builds directly via workflow_call since tags pushed with # GITHUB_TOKEN don't trigger other workflows (GitHub Actions limitation). publish-docker: name: ๐Ÿณ Publish Docker images needs: release if: needs.release.outputs.published == 'true' permissions: contents: read packages: write id-token: write attestations: write uses: ./.github/workflows/publish.yml secrets: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} with: image_tag: v${{ needs.release.outputs.published_package_version }} # Trigger Helm chart release directly via workflow_call (same GITHUB_TOKEN # limitation as the Docker path). Runs after Docker images are published so # the chart never references images that don't exist yet. publish-helm: name: ๐Ÿงญ Publish Helm chart needs: [release, publish-docker] if: needs.release.outputs.published == 'true' permissions: contents: write packages: write uses: ./.github/workflows/release-helm.yml with: chart_version: ${{ needs.release.outputs.published_package_version }} # After Docker images are published, update the GitHub release with the exact GHCR tag URL. # The GHCR package version ID is only known after the image is pushed, so we query for it here. update-release: name: ๐Ÿ”— Update release Docker link needs: [release, publish-docker] if: needs.release.outputs.published == 'true' runs-on: warp-ubuntu-latest-x64-2x permissions: contents: write packages: read steps: - name: Update GitHub release with Docker image link env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NEEDS_RELEASE_OUTPUTS_PUBLISHED_PACKAGE_VERSION: ${{ needs.release.outputs.published_package_version }} run: | set -e VERSION="${NEEDS_RELEASE_OUTPUTS_PUBLISHED_PACKAGE_VERSION}" TAG="v${VERSION}" # Query GHCR for the version ID matching this tag VERSION_ID=$(gh api --paginate -H "Accept: application/vnd.github+json" \ /orgs/triggerdotdev/packages/container/trigger.dev/versions \ --jq ".[] | select(.metadata.container.tags[] == \"${TAG}\") | .id" \ | head -1) if [ -z "$VERSION_ID" ]; then echo "Warning: Could not find GHCR version ID for tag ${TAG}, skipping update" exit 0 fi DOCKER_URL="https://github.com/triggerdotdev/trigger.dev/pkgs/container/trigger.dev/${VERSION_ID}?tag=${TAG}" GENERIC_URL="https://github.com/triggerdotdev/trigger.dev/pkgs/container/trigger.dev" # Get current release body and replace the generic link with the tag-specific one. # Use word boundary after GENERIC_URL (closing paren) to avoid matching URLs that # already have a version ID appended (idempotent on re-runs). gh release view "${TAG}" --repo triggerdotdev/trigger.dev --json body --jq '.body' > /tmp/release-body.md sed -i "s|${GENERIC_URL})|${DOCKER_URL})|g" /tmp/release-body.md gh release edit "${TAG}" --repo triggerdotdev/trigger.dev --notes-file /tmp/release-body.md # Dispatch changelog entry creation to the marketing site repo. # Runs after update-release so the GitHub release body already has the exact Docker image URL. dispatch-changelog: name: ๐Ÿ“ Dispatch changelog PR needs: [release, update-release] if: needs.release.outputs.published == 'true' && needs.release.outputs.is_prerelease != 'true' runs-on: warp-ubuntu-latest-x64-2x permissions: {} steps: - uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: token: ${{ secrets.CROSS_REPO_PAT }} repository: triggerdotdev/trigger.dev-site-v3 event-type: new-release client-payload: '{"version": "${{ needs.release.outputs.published_package_version }}"}' # The prerelease job needs to be on the same workflow file due to a limitation related to how npm verifies OIDC claims. prerelease: name: ๐Ÿงช Prerelease runs-on: ubuntu-latest environment: npm-publish permissions: contents: read id-token: write if: github.repository == 'triggerdotdev/trigger.dev' && github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'prerelease' steps: - name: Checkout repo uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 ref: ${{ github.event.inputs.ref }} persist-credentials: false - name: Setup pnpm uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 with: version: 10.33.2 - name: Setup node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22.23.1 cache: "pnpm" # npm v11.5.1 or newer is required for OIDC support # https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/#whats-new - name: Setup npm 11.x for OIDC run: npm install -g npm@11.6.4 - name: Download deps run: pnpm install --frozen-lockfile - name: Generate Prisma Client run: pnpm run generate - name: Exit changeset pre mode (if active) run: | if [ -f .changeset/pre.json ]; then echo "Repo is in changeset pre mode; exiting so snapshot release can run" pnpm exec changeset pre exit fi - name: Snapshot version run: pnpm exec changeset version --snapshot "${GITHUB_EVENT_INPUTS_PRERELEASE_TAG}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_EVENT_INPUTS_PRERELEASE_TAG: ${{ github.event.inputs.prerelease_tag }} - name: Clean run: pnpm run clean --filter "@trigger.dev/*" --filter "trigger.dev" - name: Build run: pnpm run build --filter "@trigger.dev/*" --filter "trigger.dev" - name: Publish prerelease run: pnpm exec changeset publish --no-git-tag --snapshot --tag "${GITHUB_EVENT_INPUTS_PRERELEASE_TAG}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_EVENT_INPUTS_PRERELEASE_TAG: ${{ github.event.inputs.prerelease_tag }}