Repository navigation
158 lines (143 loc) · 5.57 KB
/
Copy pathnpm-publish.yml
File metadata and controls
158 lines (143 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
name: npm-publish
# Publishing is deliberate and manual. Like every other privileged workflow here, it
# refuses to run against anything but the exact current tip of refs/heads/main, and it
# checks out the statically trusted github.sha rather than an indirect ref.
on:
workflow_dispatch:
inputs:
expected_sha:
description: Exact 40-character commit SHA currently at refs/heads/main
required: true
type: string
dry_run:
description: Pack and verify without publishing
required: false
default: true
type: boolean
permissions:
contents: read
concurrency:
group: npm-publish-main
cancel-in-progress: false
jobs:
ref-proof:
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
sha: ${{ steps.proof.outputs.sha }}
steps:
- name: Checkout current main without retaining credentials
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: refs/heads/main
fetch-depth: 1
persist-credentials: false
- name: Prove exact current main commit
id: proof
shell: bash
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
EVENT_REF: ${{ github.ref }}
EVENT_SHA: ${{ github.sha }}
EXPECTED_SHA: ${{ inputs.expected_sha }}
run: |
set -Eeuo pipefail
test "$DEFAULT_BRANCH" = "main"
test "$EVENT_REF" = "refs/heads/main"
test "${#EVENT_SHA}" -eq 40
test "${#EXPECTED_SHA}" -eq 40
case "$EVENT_SHA" in (*[!0-9a-f]*) exit 1 ;; esac
case "$EXPECTED_SHA" in (*[!0-9a-f]*) exit 1 ;; esac
test "$EXPECTED_SHA" = "$EVENT_SHA"
test "$(git rev-parse --verify HEAD^{commit})" = "$EVENT_SHA"
printf 'sha=%s\n' "$EVENT_SHA" >> "$GITHUB_OUTPUT"
publish:
needs: ref-proof
runs-on: ubuntu-24.04
timeout-minutes: 15
environment: npm-production
permissions:
contents: read
id-token: write
attestations: write
defaults:
run:
working-directory: clients/node
steps:
- name: Checkout proved commit
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# ref-proof has already asserted that github.sha is the exact refs/heads/main
# tip and equals the supplied expected_sha. Use github.sha here rather than the
# job output: it is the identical commit by construction, but it is a statically
# trusted ref, so a privileged run can never execute an unproved tree. The step
# below still cross-checks HEAD against the proved SHA.
ref: ${{ github.sha }}
persist-credentials: false
- name: Verify exact checkout
shell: bash
working-directory: .
env:
TRUSTED_SHA: ${{ needs.ref-proof.outputs.sha }}
run: test "$(git rev-parse --verify HEAD^{commit})" = "$TRUSTED_SHA"
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
registry-url: https://registry.npmjs.org
- name: Refuse runtime dependencies
shell: bash
run: |
set -Eeuo pipefail
deps="$(node -p "JSON.stringify(require('./package.json').dependencies || {})")"
test "$deps" = "{}"
- name: Unit tests
run: npm test
- name: Type declarations match the implementation
run: |
npm install --no-save --no-audit --no-fund typescript@5.9.3
npx tsc --noEmit -p tsconfig.json
# Both earlier publish attempts died on credentials, not on the package: first an
# OTP demand, then a 404 on PUT because a granular token cannot create a new package.
# Prove the credential before doing any work, so a bad token fails here with a clear
# message instead of after the tests and pack. This also gives the dry run something
# real to assert rather than never touching the registry at all.
- name: Verify registry credential
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
shell: bash
run: |
set -Eeuo pipefail
who="$(npm whoami)"
test -n "$who"
echo "authenticated to https://registry.npmjs.org as $who"
- name: Pack
id: pack
shell: bash
run: |
set -Eeuo pipefail
rm -rf node_modules
tarball="$(npm pack --silent)"
printf 'tarball=%s\n' "$PWD/$tarball" >> "$GITHUB_OUTPUT"
printf 'version=%s\n' "$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
sha256sum "$tarball"
- name: Attest package provenance
if: ${{ !inputs.dry_run }}
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: ${{ steps.pack.outputs.tarball }}
- name: Publish to npm
if: ${{ !inputs.dry_run }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# --tag is mandatory for a prerelease version, and latest is deliberate: it keeps
# "npm install aether-browser" resolving to the current candidate.
run: >-
npm publish "${{ steps.pack.outputs.tarball }}"
--access public --provenance --tag latest
- name: Dry run summary
if: ${{ inputs.dry_run }}
shell: bash
run: |
set -Eeuo pipefail
echo "Dry run only. Packed aether-browser ${{ steps.pack.outputs.version }}."
echo "Re-run with dry_run=false to publish."