1
1
name : Publish Packages
2
2
3
+ # This workflow publishes packages to NPM when changes are merged to main branch or when manually triggered.
4
+ # It runs automatically after successful tests or can be run manually for specific packages.
5
+
3
6
on :
4
7
workflow_run :
8
+ # Only run after linting and tests have passed on main branch
5
9
workflows : ['Linting and Tests']
6
10
types : [completed]
11
+ # For security reasons, this should never be set to anything but `main`
7
12
branches : [main]
8
13
workflow_dispatch :
9
14
inputs :
@@ -16,44 +21,40 @@ permissions:
16
21
contents : read
17
22
18
23
env :
24
+ # Use the SHA from the workflow run that triggered this or the current SHA for manual runs
19
25
COMMIT_SHA : ${{ github.event.workflow_run.head_sha || github.sha }}
20
26
21
27
jobs :
22
- detect -packages :
28
+ prepare -packages :
23
29
runs-on : ubuntu-latest
30
+ # Only run if manually triggered or if the triggering workflow succeeded from a push event
31
+ if : github.event_name == 'workflow_dispatch' || (
32
+ github.event.workflow_run.conclusion == 'success' &&
33
+ github.event.workflow_run.event == 'push' &&
34
+ github.repository == 'nodejs/nodejs.org')
24
35
outputs :
25
- packages : ${{ steps.find-packages.outputs.packages }}
26
- steps :
27
- - name : Checkout repository
28
- uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
29
-
30
- - name : Find packages
31
- id : find-packages
32
- env :
33
- PACKAGE : ${{ github.event.inputs.package }}
34
- run : |
35
- if [ "$PACKAGE" != "" ]; then
36
- echo "packages=[\"$PACKAGE\"]" >> $GITHUB_OUTPUT
37
- else
38
- PACKAGES=$(ls -d packages/* | xargs -n 1 basename | jq -R -s -c 'split("\n")[:-1]')
39
- echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
40
- fi
41
-
42
- verify-commit :
43
- runs-on : ubuntu-latest
44
- if : github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push')
36
+ # Output the matrix of packages to publish for use in the publish job
37
+ matrix : ${{ steps.generate-matrix.outputs.matrix }}
45
38
steps :
46
- - name : Checkout repository
47
- uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
39
+ - name : Harden Runner
40
+ uses : step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
41
+ with :
42
+ egress-policy : audit
48
43
49
44
- name : Verify commit authenticity
45
+ # Skip verification for manual runs since they're initiated by trusted users
46
+ if : github.event_name != 'workflow_dispatch'
50
47
env :
51
48
GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
52
49
run : |
50
+ # Get commit data from GitHub API to verify its authenticity
53
51
COMMIT_DATA=$(gh api repos/${{ github.repository }}/commits/$COMMIT_SHA)
52
+ # Check if commit signature is verified (GPG signed)
54
53
VERIFIED=$(echo "$COMMIT_DATA" | jq -r '.commit.verification.verified')
54
+ # Check if commit was made through GitHub's web interface (merge queue)
55
55
COMMITTER=$(echo "$COMMIT_DATA" | jq -r '.commit.committer.email')
56
56
57
+ # Security checks to ensure we only publish from verified and trusted sources
57
58
if [[ "$VERIFIED" != "true" ]]; then
58
59
echo "❌ Unverified commit! Aborting."
59
60
exit 1
@@ -66,49 +67,85 @@ jobs:
66
67
67
68
echo "✅ Commit is verified and trusted."
68
69
69
- publish :
70
- needs : [detect-packages, verify-commit]
71
- runs-on : ubuntu-latest
72
- strategy :
73
- matrix :
74
- package : ${{ fromJson(needs.detect-packages.outputs.packages) }}
75
- fail-fast : false
76
- steps :
77
70
- name : Checkout repository
78
71
uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
79
72
with :
80
- fetch-depth : 2
73
+ fetch-depth : 2 # Need at least 2 commits to detect changes between commits
81
74
82
- - name : Check for package changes
83
- if : github.event_name != 'workflow_dispatch'
84
- id : check_changes
75
+ - name : Generate package matrix
76
+ id : generate-matrix
85
77
env :
86
- PACKAGE : ${{ matrix.package }}
78
+ PACKAGE : ${{ github.event.inputs.package }}
79
+ EVENT_NAME : ${{ github.event_name }}
87
80
run : |
88
- if git diff --quiet $COMMIT_SHA~1 $COMMIT_SHA -- "packages/$PACKAGE/"; then
89
- echo "changed=false" >> $GITHUB_OUTPUT
81
+ if [ -n "$PACKAGE" ]; then
82
+ # If a specific package is requested via workflow_dispatch, just publish that one
83
+ echo "matrix={\"package\":[\"$PACKAGE\"]}" >> $GITHUB_OUTPUT
90
84
else
91
- echo "changed=true" >> $GITHUB_OUTPUT
85
+ # Otherwise, identify all packages with changes since the last commit
86
+ CHANGED_PACKAGES=()
87
+ for pkg in $(ls -d packages/*); do
88
+ PKG_NAME=$(basename "$pkg")
89
+ # For manual runs, include all packages. For automatic runs, only include packages with changes
90
+ if [ "$EVENT_NAME" == "workflow_dispatch" ] || ! git diff --quiet $COMMIT_SHA~1 $COMMIT_SHA -- "$pkg/"; then
91
+ CHANGED_PACKAGES+=("$PKG_NAME")
92
+ fi
93
+ done
94
+
95
+ # Format the output for GitHub Actions matrix using jq
96
+ PACKAGES_JSON=$(printf '%s\n' "${CHANGED_PACKAGES[@]}" | jq -R . | jq -s .)
97
+ echo "matrix={\"package\":$PACKAGES_JSON}" >> $GITHUB_OUTPUT
92
98
fi
93
99
100
+ publish :
101
+ needs : prepare-packages
102
+ runs-on : ubuntu-latest
103
+ # Use the dynamic matrix from prepare-packages job to create parallel jobs for each package
104
+ strategy :
105
+ matrix : ${{ fromJson(needs.prepare-packages.outputs.matrix) }}
106
+ fail-fast : false # Continue publishing other packages even if one fails
107
+ steps :
108
+ - name : Harden Runner
109
+ uses : step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
110
+ with :
111
+ egress-policy : audit
112
+
113
+ - name : Checkout repository
114
+ uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
115
+
94
116
- name : Set up pnpm
95
117
uses : pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
96
118
with :
97
119
cache : true
98
120
99
121
- name : Setup Node.js
100
- if : github.event_name == 'workflow_dispatch' || steps.check_changes.outputs.changed == 'true'
101
122
uses : actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
102
123
with :
103
124
node-version-file : ' .nvmrc'
104
125
registry-url : ' https://registry.npmjs.org'
105
126
cache : pnpm
106
127
107
128
- name : Publish
108
- if : github.event_name == 'workflow_dispatch' || steps.check_changes.outputs.changed == 'true'
109
129
working-directory : packages/${{ matrix.package }}
110
130
env :
111
131
NPM_TOKEN : ${{ secrets.NPM_TOKEN }}
112
- run : >
132
+ run : |
133
+ # Create a unique version using the commit SHA as a prerelease identifier
134
+ # This ensures we can publish multiple times from the same codebase with unique versions
113
135
npm version --no-git-tag-version 0.0.0-$COMMIT_SHA
136
+ # Publish the package to the npm registry with public access flag
114
137
pnpm publish --access public
138
+
139
+ - name : Notify on Manual Release
140
+ if : ${{ github.event_name == 'workflow_dispatch' }}
141
+ uses : rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # 2.3.3
142
+ env :
143
+ SLACK_COLOR : ' #43853D'
144
+ SLACK_ICON : https://github.com/nodejs.png?size=48
145
+ SLACK_TITLE : ' :rocket: Package Published: ${{ matrix.package }}'
146
+ SLACK_MESSAGE : |
147
+ :package: *Package*: `${{ matrix.package }}` (<https://www.npmjs.com/package/${{ steps.package-info.outputs.name }}|View on npm>)
148
+ :bust_in_silhouette: *Published by*: ${{ github.triggering_actor }}
149
+ :octocat: *Commit*: <https://github.com/${{ github.repository }}/commit/${{ env.COMMIT_SHA }}|${{ env.COMMIT_SHA }}>
150
+ SLACK_USERNAME : nodejs-bot
151
+ SLACK_WEBHOOK : ${{ secrets.SLACK_WEBHOOK }}
0 commit comments