-
Notifications
You must be signed in to change notification settings - Fork 0
143 lines (132 loc) · 6.47 KB
/
Copy pathrelease.yml
File metadata and controls
143 lines (132 loc) · 6.47 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
# SPDX-License-Identifier: MIT
# Automated changesets release. Adapted from the pattern in
# thecodedrift/firebot-script-music-to-my-ears and hardened for npm publishing.
#
# Security model (why it is split into two jobs):
#
# `version` reads contributor-authored changesets (UNTRUSTED text) and opens
# the "Version Packages" PR. It has NO npm credential and NO OIDC
# identity, so a crafted changeset / PR body has nothing to steal
# or escape into. The changeset TEXT is fully consumed here (into
# the CHANGELOG + PR body) and never reaches the credentialed job.
#
# `publish` runs only when main's version is not yet on npm — i.e. right
# after the Version Packages PR merges. By then there are no
# changesets left, so this job sees no untrusted PR/changeset text;
# it builds from reviewed, merged source only. It authenticates to
# npm with a SHORT-LIVED token minted via GitHub OIDC (npm trusted
# publishing) — there is NO stored NPM_TOKEN anywhere to exfiltrate.
#
# Residual perimeter, stated honestly: the publish job builds merged repo code,
# so "what can merge to main" is the real boundary. That is enforced by branch
# protection (review required) on main. `--ignore-scripts` keeps dependency
# lifecycle hooks from running while the OIDC identity is available; only our
# own build runs. No `pull_request_target` and no `${{ }}` interpolation of
# untrusted text into any `run:` — the two classic token-exfiltration footguns.
#
# Action refs are pinned to commit SHAs (supply-chain hardening); the trailing
# comment records the human-readable tag.
name: Release
on:
push:
branches: [main]
# Serialize releases so two pushes can't race the version PR / publish.
concurrency: release-${{ github.ref }}
# No workflow-wide grants; each job requests exactly what it needs.
permissions: {}
jobs:
version:
name: Version Packages PR
runs-on: ubuntu-latest
permissions:
contents: write # push the changeset-release/main branch
pull-requests: write # open/update the Version Packages PR
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: pnpm
- run: pnpm install --frozen-lockfile --ignore-scripts
# `version: pnpm bump` runs `changeset version` AND `sync-skill-versions`,
# so the bumped version is propagated into skills/recipes in the same PR.
# No `publish:` input — this job can never publish.
- uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1
with:
version: pnpm bump
commit: "chore: version packages"
title: "chore: version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Decides whether main's version needs publishing — with NO credential and NO
# OIDC identity. Runs on every push, but the credentialed publish job below
# only starts when this reports a new version, so ordinary non-release pushes
# never instantiate an OIDC-capable job or touch the npm-production environment.
check:
name: Check for a new version
runs-on: ubuntu-latest
permissions:
contents: read # checkout + read package.json
outputs:
publish: ${{ steps.check.outputs.publish }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false # no git writes here; don't leave the token in git config
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
# Publish only a version npm has never seen. On an ordinary feature merge
# the version is unchanged (already on npm) so this is false; it flips true
# only on the merge of the Version Packages PR. Reads package.json (repo
# source) via `node -p` — no untrusted interpolation into the shell.
- id: check
run: |
name=$(node -p "require('./packages/cli/package.json').name")
version=$(node -p "require('./packages/cli/package.json').version")
if npm view "$name@$version" version >/dev/null 2>&1; then
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "$name@$version already published — nothing to do."
else
echo "publish=true" >> "$GITHUB_OUTPUT"
echo "Will publish $name@$version."
fi
publish:
name: Publish to npm
# Gate on the credential-free check: this job — and therefore the OIDC
# identity + npm-production environment — only exists for an actual release.
needs: check
if: needs.check.outputs.publish == 'true'
runs-on: ubuntu-latest
# Environment is the scoping/audit boundary for the release and where the
# npm trusted-publisher is bound. No required reviewers (fully automatic
# once the Version Packages PR merges), by design.
environment: npm-production
permissions:
contents: read # checkout only
id-token: write # OIDC → short-lived npm auth + build provenance
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false # publish authenticates via OIDC/npm, not git creds
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
cache: pnpm
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile --ignore-scripts
# OIDC trusted publishing + provenance need npm >= 11.5.1. Pin the version
# (not @latest) so the release is deterministic and a new npm release
# can't change publish behavior unreviewed; bump this intentionally.
# --ignore-scripts: no lifecycle code runs while the OIDC identity exists.
- run: npm install -g npm@12.0.1 --ignore-scripts
- run: pnpm --filter @taskless/cli build
# OIDC handshake happens here (id-token: write + registry-url + a
# registered trusted publisher). No token in env. `--provenance` attaches
# a signed build-provenance attestation.
- run: npm publish --provenance --access public
working-directory: packages/cli