Skip to content

Commit 41de009

Browse files
authored
fix(ci): harden npm publish workflow
Gate standalone npm publishes to reviewed release sources, validate dispatch input safely, verify release/tag/source version alignment before publishing, and avoid installing mutable npm@latest during release.
1 parent d952d56 commit 41de009

1 file changed

Lines changed: 59 additions & 17 deletions

File tree

.github/workflows/npm-publish.yml

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,40 +30,52 @@ concurrency:
3030
jobs:
3131
npm-publish:
3232
if: >-
33-
github.event_name == 'workflow_dispatch' ||
33+
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') ||
3434
(github.event_name == 'workflow_run' &&
3535
github.event.workflow_run.conclusion == 'success' &&
3636
github.event.workflow_run.head_branch == 'main')
3737
runs-on: ubuntu-latest
3838
name: Publish coven-code to npm
3939

4040
steps:
41+
- name: Resolve requested version
42+
if: github.event_name == 'workflow_dispatch'
43+
id: requested_version
44+
env:
45+
REQUESTED_VERSION: ${{ inputs.version }}
46+
run: |
47+
set -euo pipefail
48+
VERSION="${REQUESTED_VERSION#v}"
49+
50+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
51+
echo "::error::Invalid npm publish version: $VERSION"
52+
exit 1
53+
fi
54+
55+
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
56+
echo "tag_ref=refs/tags/v$VERSION" >> "$GITHUB_OUTPUT"
57+
4158
- uses: actions/checkout@v4
4259
if: github.event_name == 'workflow_dispatch'
60+
with:
61+
ref: ${{ steps.requested_version.outputs.tag_ref }}
62+
fetch-depth: 0
4363

4464
- uses: actions/checkout@v4
4565
if: github.event_name == 'workflow_run'
4666
with:
4767
ref: ${{ github.event.workflow_run.head_sha }}
48-
49-
- name: Set up Node.js
50-
uses: actions/setup-node@v6
51-
with:
52-
node-version: '24'
53-
package-manager-cache: false
54-
55-
- name: Set up npm
56-
run: |
57-
npm install -g npm@latest
58-
npm --version
68+
fetch-depth: 0
5969

6070
- name: Resolve version
6171
id: version
72+
env:
73+
EVENT_NAME: ${{ github.event_name }}
74+
REQUESTED_TAG: ${{ steps.requested_version.outputs.tag }}
6275
run: |
6376
set -euo pipefail
64-
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
65-
VERSION="${{ inputs.version }}"
66-
VERSION="${VERSION#v}"
77+
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
78+
VERSION="${REQUESTED_TAG#v}"
6779
else
6880
VERSION="$(grep '^version' src-rust/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')"
6981
fi
@@ -77,10 +89,40 @@ jobs:
7789
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
7890
echo "Publishing coven-code@$VERSION"
7991
80-
- name: Verify GitHub Release exists
92+
- name: Set up Node.js
93+
uses: actions/setup-node@v6
94+
with:
95+
node-version: '24'
96+
package-manager-cache: false
97+
98+
- name: Verify npm
99+
run: npm --version
100+
101+
- name: Verify GitHub Release and source version
81102
env:
82103
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
83-
run: gh release view "${{ steps.version.outputs.tag }}" --repo "${{ github.repository }}" >/dev/null
104+
TAG: ${{ steps.version.outputs.tag }}
105+
VERSION: ${{ steps.version.outputs.version }}
106+
run: |
107+
set -euo pipefail
108+
TAG_REF="refs/tags/$TAG"
109+
110+
gh release view "$TAG" --repo "${{ github.repository }}" >/dev/null
111+
112+
TAG_COMMIT="$(git rev-list -n 1 "$TAG_REF")"
113+
HEAD_COMMIT="$(git rev-parse HEAD)"
114+
if [[ "$TAG_COMMIT" != "$HEAD_COMMIT" ]]; then
115+
echo "::error::Checked-out source ($HEAD_COMMIT) does not match $TAG ($TAG_COMMIT)."
116+
exit 1
117+
fi
118+
119+
CARGO_VERSION="$(grep '^version' src-rust/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')"
120+
if [[ "$CARGO_VERSION" != "$VERSION" ]]; then
121+
echo "::error::npm publish version ($VERSION) does not match Cargo.toml ($CARGO_VERSION)."
122+
exit 1
123+
fi
124+
125+
echo "Release source verified for $TAG at $HEAD_COMMIT."
84126
85127
- name: Verify trusted publisher identity
86128
working-directory: npm

0 commit comments

Comments
 (0)