Skip to content

Commit 3401a1d

Browse files
authored
feat(releases): require approval for package releases (#2753)
**Background** Currently the changeset PR creation and the publishing is handled by the same workflow. This is not ideal: - The build steps are executed on every run of the pipeline, even though they're only needed for the publish case. - The PR creation workflow does not need permissions to publish to npm, only the release path needs them. - Adding an approval step is painful as we'd need to also approve each changeset PR creation workflow run. **Changes in this PR** - Separated the changeset PR creation into its own workflow and minimum permission set. - Added a GH environment with an approval step for the package publishing workflow (also for prereleases). - New publish workflow runs will not cancel in-progress runs; helps avoid partial failures in publishing. These changes also enable hardening the npm OIDC setup by tying it to a GH environment that requires approval.
1 parent 3f982ed commit 3401a1d

File tree

2 files changed

+122
-84
lines changed

2 files changed

+122
-84
lines changed

.github/workflows/changeset-pr.yml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: 🦋 Changeset PR
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- "packages/**"
9+
- ".changeset/**"
10+
- "package.json"
11+
- "pnpm-lock.yaml"
12+
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
release-pr:
19+
name: Create Release PR
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: write
23+
pull-requests: write
24+
if: github.repository == 'triggerdotdev/trigger.dev'
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v4
28+
with:
29+
fetch-depth: 0
30+
31+
- name: Setup pnpm
32+
uses: pnpm/action-setup@v4
33+
34+
- name: Setup node
35+
uses: buildjet/setup-node@v4
36+
with:
37+
node-version: 20.19.0
38+
cache: "pnpm"
39+
40+
- name: Install dependencies
41+
run: pnpm install --frozen-lockfile
42+
43+
- name: Create release PR
44+
id: changesets
45+
uses: changesets/action@v1
46+
with:
47+
version: pnpm run changeset:version
48+
commit: "chore: release"
49+
title: "chore: release"
50+
env:
51+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52+
53+
- name: Update PR title with version
54+
if: steps.changesets.outputs.published != 'true'
55+
env:
56+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57+
run: |
58+
PR_NUMBER=$(gh pr list --head changeset-release/main --json number --jq '.[0].number')
59+
if [ -n "$PR_NUMBER" ]; then
60+
git fetch origin changeset-release/main
61+
# we arbitrarily reference the version of the cli package here; it is the same for all package releases
62+
VERSION=$(git show origin/changeset-release/main:packages/cli-v3/package.json | jq -r '.version')
63+
gh pr edit "$PR_NUMBER" --title "chore: release v$VERSION"
64+
fi
65+
66+
update-lockfile:
67+
name: Update lockfile on release PR
68+
runs-on: ubuntu-latest
69+
needs: release-pr
70+
permissions:
71+
contents: write
72+
steps:
73+
- name: Checkout release branch
74+
uses: actions/checkout@v4
75+
with:
76+
ref: changeset-release/main
77+
78+
- name: Setup pnpm
79+
uses: pnpm/action-setup@v4
80+
with:
81+
version: 10.23.0
82+
83+
- name: Setup node
84+
uses: buildjet/setup-node@v4
85+
with:
86+
node-version: 20.19.0
87+
88+
- name: Install and update lockfile
89+
run: pnpm install --no-frozen-lockfile
90+
91+
- name: Commit and push lockfile
92+
run: |
93+
set -e
94+
if git diff --quiet pnpm-lock.yaml; then
95+
echo "No lockfile changes"
96+
else
97+
git config user.name "github-actions[bot]"
98+
git config user.email "github-actions[bot]@users.noreply.github.com"
99+
git add pnpm-lock.yaml
100+
git commit -m "chore: update lockfile for release"
101+
git push origin changeset-release/main
102+
fi

.github/workflows/release.yml

Lines changed: 20 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
name: 🦋 Changesets Release
1+
name: 🚀 Release npm packages
22

33
on:
4-
push:
4+
pull_request:
5+
types: [closed]
56
branches:
67
- main
7-
paths-ignore:
8-
- "docs/**"
9-
- "**.md"
10-
- ".github/CODEOWNERS"
11-
- ".github/ISSUE_TEMPLATE/**"
128
workflow_dispatch:
139
inputs:
1410
ref:
@@ -19,22 +15,26 @@ on:
1915
description: "The npm dist-tag for the prerelease (e.g., 'v4-prerelease')"
2016
required: true
2117
type: string
22-
default: "v4-prerelease"
18+
default: "prerelease"
2319

2420
concurrency:
25-
group: ${{ github.workflow }}-${{ github.ref }}
26-
cancel-in-progress: true
21+
group: ${{ github.workflow }}
22+
cancel-in-progress: false
2723

2824
jobs:
2925
release:
3026
name: 🦋 Changesets Release
3127
runs-on: ubuntu-latest
28+
environment: npm-publish
3229
permissions:
33-
contents: write
30+
contents: read
3431
packages: write
35-
pull-requests: write
3632
id-token: write
37-
if: github.repository == 'triggerdotdev/trigger.dev' && github.event_name != 'workflow_dispatch'
33+
if: |
34+
github.repository == 'triggerdotdev/trigger.dev' &&
35+
github.event_name != 'workflow_dispatch' &&
36+
github.event.pull_request.merged == true &&
37+
startsWith(github.event.pull_request.head.ref, 'changeset-release/')
3838
outputs:
3939
published: ${{ steps.changesets.outputs.published }}
4040
published_packages: ${{ steps.changesets.outputs.publishedPackages }}
@@ -56,10 +56,10 @@ jobs:
5656
node-version: 20.19.0
5757
cache: "pnpm"
5858

59-
- name: Download deps
59+
- name: Install dependencies
6060
run: pnpm install --frozen-lockfile
6161

62-
- name: Generate Prisma Client
62+
- name: Generate Prisma client
6363
run: pnpm run generate
6464

6565
- name: Build
@@ -68,99 +68,35 @@ jobs:
6868
- name: Type check
6969
run: pnpm run typecheck --filter "@trigger.dev/*" --filter "trigger.dev"
7070

71-
# This action has two responsibilities. The first time the workflow runs
72-
# (initial push to the `main` branch) it will create a new branch and
73-
# then open a PR with the related changes for the new version. After the
74-
# PR is merged, the workflow will run again and this action will build +
75-
# publish to npm.
7671
- name: Publish
77-
if: ${{ !env.ACT }}
7872
id: changesets
7973
uses: changesets/action@v1
8074
with:
81-
version: pnpm run changeset:version
82-
commit: "chore: release packages"
83-
title: "chore: release packages"
8475
publish: pnpm run changeset:release
8576
createGithubReleases: true
8677
env:
8778
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8879

89-
- name: Get package version
80+
- name: Show package version
9081
if: steps.changesets.outputs.published == 'true'
9182
id: get_version
9283
run: |
9384
package_version=$(echo '${{ steps.changesets.outputs.publishedPackages }}' | jq -r '.[0].version')
9485
echo "package_version=${package_version}" >> "$GITHUB_OUTPUT"
9586
9687
# this triggers the publish workflow for the docker images
97-
- name: Create and push docker tag
88+
- name: Create and push Docker tag
9889
if: steps.changesets.outputs.published == 'true'
9990
run: |
10091
set -e
10192
git tag "v.docker.${{ steps.get_version.outputs.package_version }}"
10293
git push origin "v.docker.${{ steps.get_version.outputs.package_version }}"
10394
104-
- name: Update PR title with version
105-
if: steps.changesets.outputs.published != 'true'
106-
env:
107-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
108-
run: |
109-
PR_NUMBER=$(gh pr list --head changeset-release/main --json number --jq '.[0].number')
110-
if [ -n "$PR_NUMBER" ]; then
111-
git fetch origin changeset-release/main
112-
# we arbitrarily reference the version of the cli package here; it is the same for all package releases
113-
VERSION=$(git show origin/changeset-release/main:packages/cli-v3/package.json | jq -r '.version')
114-
gh pr edit "$PR_NUMBER" --title "chore: release v$VERSION"
115-
fi
116-
117-
update-lockfile:
118-
name: Update lockfile on release PR
119-
runs-on: ubuntu-latest
120-
needs: release
121-
if: needs.release.outputs.published != 'true'
122-
permissions:
123-
contents: write
124-
steps:
125-
- name: Checkout release branch
126-
uses: actions/checkout@v4
127-
with:
128-
ref: changeset-release/main
129-
130-
- name: Setup pnpm
131-
uses: pnpm/action-setup@v4
132-
with:
133-
version: 10.23.0
134-
135-
- name: Setup node
136-
uses: buildjet/setup-node@v4
137-
with:
138-
node-version: 20.19.0
139-
140-
# npm v11.5.1 or newer is required for OIDC support
141-
# https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/#whats-new
142-
- name: Setup npm 11.x for OIDC
143-
run: npm install -g npm@11.6.4
144-
145-
- name: Install and update lockfile
146-
run: pnpm install --no-frozen-lockfile
147-
148-
- name: Commit and push lockfile
149-
run: |
150-
set -e
151-
if git diff --quiet pnpm-lock.yaml; then
152-
echo "No lockfile changes"
153-
else
154-
git config user.name "github-actions[bot]"
155-
git config user.email "github-actions[bot]@users.noreply.github.com"
156-
git add pnpm-lock.yaml
157-
git commit -m "chore: update lockfile for release"
158-
git push origin changeset-release/main
159-
fi
160-
95+
# The prerelease job needs to be on the same workflow file due to a limitation related to how npm verifies OIDC claims.
16196
prerelease:
162-
name: 🚀 Prerelease
97+
name: 🧪 Prerelease
16398
runs-on: ubuntu-latest
99+
environment: npm-publish
164100
permissions:
165101
contents: read
166102
id-token: write

0 commit comments

Comments
 (0)