Skip to content

Commit 3750366

Browse files
authored
chore(repo): refactor publish.yml for PR releases (#26509)
1 parent c86de97 commit 3750366

File tree

5 files changed

+460
-84
lines changed

5 files changed

+460
-84
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<!-- Please make sure that your commit message follows our format -->
55
<!-- Example: `fix(nx): must begin with lowercase` -->
66

7+
<!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. -->
8+
79
## Current Behavior
810
<!-- This is the behavior we have today -->
911

.github/workflows/publish.yml

Lines changed: 201 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,122 @@
11
name: publish
22

33
on:
4+
# Automated schedule - canary releases from master
45
schedule:
56
- cron: "0 3 * * 2-6" # Tuesdays - Saturdays, at 3am UTC
7+
# Manual trigger - PR releases or dry-runs (based on workflow inputs)
68
workflow_dispatch:
9+
inputs:
10+
pr:
11+
description: "If set, a real release will be created for the branch associated with the given PR number. If blank, a dry-run of the currently selected branch will be performed."
12+
required: false
13+
type: number
714
release:
815
types: [ published ]
916

17+
# Dynamically generate the display name for the GitHub UI based on the event type and inputs
18+
run-name: ${{ github.event.inputs.pr && format('PR Release for {0}', github.event.inputs.pr) || github.event_name == 'schedule' && 'Canary Release' || github.event_name == 'workflow_dispatch' && !github.event.inputs.pr && 'Release Dry-Run' || github.ref_name }}
19+
1020
env:
1121
DEBUG: napi:*
1222
NX_RUN_GROUP: ${{ github.run_id }}-${{ github.run_attempt }}
1323
CYPRESS_INSTALL_BINARY: 0
24+
NODE_VERSION: 18
25+
PNPM_VERSION: 8.15.7 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
1426

1527
jobs:
28+
# We first need to determine the version we are releasing, and if we need a custom repo or ref to use for the git checkout in subsequent steps.
29+
# These values depend upon the event type that triggered the workflow:
30+
#
31+
# - schedule:
32+
# - We are running a canary release which always comes from the master branch, we can use default ref resolution
33+
# in actions/checkout. The exact version will be generated within scripts/nx-release.ts.
34+
#
35+
# - release:
36+
# - We are running a full release which is based on the tag that triggered the release event, we can use default
37+
# ref resolution in actions/checkout. The exact version will be generated within scripts/nx-release.ts.
38+
#
39+
# - workflow_dispatch:
40+
# - We are either running a dry-run on the current branch, in which case the version will be statica and we can use
41+
# default ref resolution in actions/checkout, or we are creating a PR release for the given PR number, in which case
42+
# we should generate an applicable version number within publish-resolve-data.js and use a custom ref of the PR branch name.
43+
resolve-required-data:
44+
name: Resolve Required Data
45+
if: ${{ github.repository_owner == 'nrwl' }}
46+
runs-on: ubuntu-latest
47+
outputs:
48+
version: ${{ steps.script.outputs.version }}
49+
dry_run_flag: ${{ steps.script.outputs.dry_run_flag }}
50+
success_comment: ${{ steps.script.outputs.success_comment }}
51+
publish_branch: ${{ steps.script.outputs.publish_branch }}
52+
ref: ${{ steps.script.outputs.ref }}
53+
repo: ${{ steps.script.outputs.repo }}
54+
env:
55+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
56+
steps:
57+
# Default checkout on the triggering branch so that the latest publish-resolve-data.js script is available
58+
- uses: actions/checkout@v4
59+
60+
# Set up pnpm and node so that we can verify our setup and that the NPM_TOKEN secret will work later
61+
- uses: pnpm/action-setup@v4
62+
with:
63+
version: ${{ env.PNPM_VERSION }}
64+
65+
- name: Setup node
66+
uses: actions/setup-node@v4
67+
with:
68+
node-version: ${{ env.NODE_VERSION }}
69+
registry-url: 'https://registry.npmjs.org'
70+
check-latest: true
71+
cache: 'pnpm'
72+
73+
# Ensure that the NPM_TOKEN secret is still valid before wasting any time deriving data or building projects
74+
- name: Check NPM Credentials
75+
run: npm whoami && echo "NPM credentials are valid" || (echo "NPM credentials are invalid or have expired." && exit 1)
76+
77+
- name: Resolve and set checkout and version data to use for release
78+
id: script
79+
uses: actions/github-script@v7
80+
env:
81+
PR_NUMBER: ${{ github.event.inputs.pr }}
82+
with:
83+
github-token: ${{ secrets.GITHUB_TOKEN }}
84+
script: |
85+
const script = require('${{ github.workspace }}/scripts/publish-resolve-data.js');
86+
await script({ github, context, core });
87+
88+
- name: (PR Release Only) Check out latest master
89+
if: ${{ steps.script.outputs.ref != '' }}
90+
uses: actions/checkout@v4
91+
with:
92+
# Check out the latest master branch to get its copy of nx-release.ts
93+
repository: nrwl/nx
94+
ref: master
95+
path: latest-master-checkout
96+
97+
- name: (PR Release Only) Check out PR branch
98+
if: ${{ steps.script.outputs.ref != '' }}
99+
uses: actions/checkout@v4
100+
with:
101+
# Check out the PR branch to get its copy of nx-release.ts
102+
repository: ${{ steps.script.outputs.repo }}
103+
ref: ${{ steps.script.outputs.ref }}
104+
path: pr-branch-checkout
105+
106+
- name: (PR Release Only) Ensure that nx-release.ts has not changed in the PR being released
107+
if: ${{ steps.script.outputs.ref != '' }}
108+
env:
109+
FILE_TO_COMPARE: "scripts/nx-release.ts"
110+
run: |
111+
if ! cmp -s "latest-master-checkout/${{ env.FILE_TO_COMPARE }}" "pr-branch-checkout/${{ env.FILE_TO_COMPARE }}"; then
112+
echo "🛑 Error: The file ${{ env.FILE_TO_COMPARE }} is different on the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} vs latest master on nrwl/nx, cancelling workflow."
113+
exit 1
114+
else
115+
echo "✅ The file ${{ env.FILE_TO_COMPARE }} is identical between the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} and latest master on nrwl/nx."
116+
fi
117+
16118
build:
119+
needs: [resolve-required-data]
17120
if: ${{ github.repository_owner == 'nrwl' }}
18121
strategy:
19122
fail-fast: false
@@ -100,16 +203,19 @@ jobs:
100203
runs-on: ${{ matrix.settings.host }}
101204
steps:
102205
- uses: actions/checkout@v4
206+
with:
207+
repository: ${{ needs.resolve-required-data.outputs.repo }}
208+
ref: ${{ needs.resolve-required-data.outputs.ref }}
103209

104-
- uses: pnpm/action-setup@v2
210+
- uses: pnpm/action-setup@v4
105211
with:
106-
version: 8
212+
version: ${{ env.PNPM_VERSION }}
107213

108214
- name: Setup node
109215
uses: actions/setup-node@v4
110216
if: ${{ !matrix.settings.docker }}
111217
with:
112-
node-version: 18
218+
node-version: ${{ env.NODE_VERSION }}
113219
check-latest: true
114220
cache: 'pnpm'
115221

@@ -120,7 +226,7 @@ jobs:
120226
targets: ${{ matrix.settings.target }}
121227

122228
- name: Cache cargo
123-
uses: actions/cache@v3
229+
uses: actions/cache@v4
124230
with:
125231
path: |
126232
~/.cargo/registry/index/
@@ -129,59 +235,72 @@ jobs:
129235
.cargo-cache
130236
target/
131237
key: ${{ matrix.settings.target }}-cargo-registry
238+
132239
- uses: goto-bus-stop/setup-zig@v2
133240
if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' }}
134241
with:
135242
version: 0.10.0
243+
136244
- name: Setup toolchain
137245
run: ${{ matrix.settings.setup }}
138246
if: ${{ matrix.settings.setup }}
139247
shell: bash
248+
140249
- name: Setup node x86
141250
if: matrix.settings.target == 'i686-pc-windows-msvc'
142251
run: yarn config set supportedArchitectures.cpu "ia32"
143252
shell: bash
253+
144254
- name: Install dependencies
145255
if: ${{ !matrix.settings.docker }}
146256
run: pnpm install --frozen-lockfile
147257
timeout-minutes: 30
258+
148259
- name: Setup node x86
149260
uses: actions/setup-node@v4
150261
if: matrix.settings.target == 'i686-pc-windows-msvc'
151262
with:
152-
node-version: 18
263+
node-version: ${{ env.NODE_VERSION }}
153264
check-latest: true
154265
cache: pnpm
155266
architecture: x86
267+
156268
- name: Build in docker
157269
uses: addnab/docker-run-action@v3
158270
if: ${{ matrix.settings.docker }}
159271
with:
160272
image: ${{ matrix.settings.docker }}
161273
options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build
162274
run: ${{ matrix.settings.build }}
275+
163276
- name: Build
164277
run: ${{ matrix.settings.build }}
165278
if: ${{ !matrix.settings.docker }}
166279
shell: bash
280+
167281
- name: Upload artifact
168-
uses: actions/upload-artifact@v3
282+
uses: actions/upload-artifact@v4
169283
with:
170284
name: bindings-${{ matrix.settings.target }}
171285
path: packages/**/*.node
172286
if-no-files-found: error
173287

174288
build-freebsd:
289+
needs: [resolve-required-data]
175290
if: ${{ github.repository_owner == 'nrwl' }}
176291
runs-on: macos-13-large
177292
name: Build FreeBSD
178293
timeout-minutes: 45
179294
steps:
180295
- uses: actions/checkout@v4
181-
if: ${{ github.event_name != 'schedule' }}
296+
if: ${{ github.event_name != 'schedule' && !github.event.inputs.pr }}
297+
with:
298+
repository: ${{ needs.resolve-required-data.outputs.repo }}
299+
ref: ${{ needs.resolve-required-data.outputs.ref }}
300+
182301
- name: Build
183302
id: build
184-
if: ${{ github.event_name != 'schedule' }}
303+
if: ${{ github.event_name != 'schedule' && !github.event.inputs.pr }}
185304
uses: cross-platform-actions/[email protected]
186305
env:
187306
DEBUG: napi:*
@@ -223,9 +342,10 @@ jobs:
223342
echo "KILL ALL NODE PROCESSES"
224343
killall node || true
225344
echo "COMPLETE"
345+
226346
- name: Upload artifact
227-
if: ${{ github.event_name != 'schedule' }}
228-
uses: actions/upload-artifact@v3
347+
if: ${{ github.event_name != 'schedule' && !github.event.inputs.pr }}
348+
uses: actions/upload-artifact@v4
229349
with:
230350
name: bindings-freebsd
231351
path: packages/**/*.node
@@ -238,7 +358,9 @@ jobs:
238358
permissions:
239359
id-token: write
240360
contents: write
361+
pull-requests: write
241362
needs:
363+
- resolve-required-data
242364
- build-freebsd
243365
- build
244366
env:
@@ -247,45 +369,91 @@ jobs:
247369
NPM_CONFIG_PROVENANCE: true
248370
steps:
249371
- uses: actions/checkout@v4
250-
- uses: pnpm/action-setup@v2
251372
with:
252-
version: 8
373+
repository: ${{ needs.resolve-required-data.outputs.repo }}
374+
ref: ${{ needs.resolve-required-data.outputs.ref }}
375+
376+
- uses: pnpm/action-setup@v4
377+
with:
378+
version: ${{ env.PNPM_VERSION }}
379+
253380
- name: Setup node
254381
uses: actions/setup-node@v4
255382
with:
256-
node-version: 18
383+
node-version: ${{ env.NODE_VERSION }}
257384
registry-url: 'https://registry.npmjs.org'
258385
check-latest: true
259386
cache: 'pnpm'
260-
- name: Check NPM Credentials
261-
run: npm whoami && echo "NPM credentials are valid" || (echo "NPM credentials are invalid or have expired." && exit 1)
387+
262388
- name: Install dependencies
263389
run: pnpm install --frozen-lockfile
390+
264391
- name: Download all artifacts
265-
uses: actions/download-artifact@v3
392+
uses: actions/download-artifact@v4
266393
with:
267394
path: artifacts
395+
396+
# This command will appropriately fail if no artifacts are available
268397
- name: List artifacts
269398
run: ls -R artifacts
270399
shell: bash
400+
271401
- name: Publish
402+
env:
403+
VERSION: ${{ needs.resolve-required-data.outputs.version }}
404+
DRY_RUN: ${{ needs.resolve-required-data.outputs.dry_run_flag }}
405+
PUBLISH_BRANCH: ${{ needs.resolve-required-data.outputs.publish_branch }}
272406
run: |
273-
git checkout -b publish/$GITHUB_REF_NAME
274-
# If triggered by the cron, create a canary release
275-
if [ "${{ github.event_name }}" = "schedule" ]; then
276-
VERSION="canary"
277-
else
278-
# Otherwise, use the tag name (if triggered via release), or explicit version (if triggered via workflow_dispatch)
279-
VERSION="${GITHUB_REF_NAME}"
280-
fi
281-
# If triggered via workflow_dispatch, perform a dry-run
282-
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
283-
DRY_RUN="--dry-run"
284-
else
285-
DRY_RUN=""
286-
fi
407+
echo ""
408+
# Create and check out the publish branch
409+
git checkout -b $PUBLISH_BRANCH
410+
echo ""
411+
echo "Version set to: $VERSION"
412+
echo "DRY_RUN set to: $DRY_RUN"
413+
echo ""
287414
pnpm nx-release --local=false $VERSION $DRY_RUN
288-
- name: Trigger Docs Release
289-
# Publish docs only on a full release
290-
if: ${{ !github.event.release.prerelease }}
415+
416+
- name: (Stable Release Only) Trigger Docs Release
417+
if: ${{ !github.event.release.prerelease && github.event_name == 'release' }}
291418
run: npx ts-node ./scripts/release-docs.ts
419+
420+
- name: (PR Release Only) Create comment for successful PR release
421+
if: success() && github.event.inputs.pr
422+
uses: actions/github-script@v7
423+
env:
424+
SUCCESS_COMMENT: ${{ needs.resolve-required-data.outputs.success_comment }}
425+
with:
426+
github-token: ${{ secrets.GITHUB_TOKEN }}
427+
script: |
428+
const successComment = JSON.parse(process.env.SUCCESS_COMMENT);
429+
await github.rest.issues.createComment({
430+
owner: context.repo.owner,
431+
repo: context.repo.repo,
432+
issue_number: ${{ github.event.inputs.pr }},
433+
body: successComment
434+
});
435+
436+
pr_failure_comment:
437+
# Run this job if it is a PR release, running on the nrwl origin, and any of the required jobs failed
438+
if: ${{ github.repository_owner == 'nrwl' && github.event.inputs.pr && always() && contains(needs.*.result, 'failure') }}
439+
needs: [resolve-required-data, build, build-freebsd, publish]
440+
name: (PR Release Failure Only) Create comment for failed PR release
441+
runs-on: ubuntu-latest
442+
steps:
443+
- name: Create comment for failed PR release
444+
uses: actions/github-script@v7
445+
with:
446+
github-token: ${{ secrets.GITHUB_TOKEN }}
447+
# This script is intentionally kept inline (and e.g. not generated in publish-resolve-data.js)
448+
# to ensure that an error within the data generation itself is not missed.
449+
script: |
450+
const message = `
451+
Failed to publish a PR release of this pull request, triggered by @${{ github.triggering_actor }}.
452+
See the failed workflow run at: https://github.com/nrwl/nx/actions/runs/${{ github.run_id }}
453+
`;
454+
await github.rest.issues.createComment({
455+
owner: context.repo.owner,
456+
repo: context.repo.repo,
457+
issue_number: ${{ github.event.inputs.pr }},
458+
body: message
459+
});

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,7 @@ Closes #157
355355
To simplify and automate the process of committing with this format,
356356
**Nx is a [Commitizen](https://github.com/commitizen/cz-cli) friendly repository**, just do `git add` and
357357
execute `pnpm commit`.
358+
359+
#### PR releases
360+
361+
If you are working on a particularly complex change or feature addition, you can request a dedicated Nx release for the associated pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate.

0 commit comments

Comments
 (0)