Skip to content

Commit 4d46547

Browse files
committed
ci: doctor workflow
1 parent e8e83e4 commit 4d46547

2 files changed

Lines changed: 208 additions & 18 deletions

File tree

.github/workflows/npm_release_doctor.yml

Lines changed: 205 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,238 @@ name: '@nativescript/doctor -> npm'
33
on:
44
push:
55
branches: [ 'main' ]
6+
tags:
7+
- '@nativescript/doctor@*'
68
paths:
79
- 'packages/doctor/**'
810
workflow_dispatch:
11+
inputs:
12+
release_type:
13+
description: >-
14+
Release to cut. Leave empty for a rolling "next" prerelease (no version
15+
bump). "dev" publishes a -dev prerelease (no bump). A semver keyword
16+
(patch/minor/major) or an explicit version (e.g. 2.1.0, 2.1.0-alpha.1)
17+
bumps packages/doctor/package.json, commits + tags
18+
@nativescript/doctor@<version>, then publishes a stable release. A
19+
prerelease version publishes under the dist-tag matching its prerelease
20+
id (alpha/beta/rc); a plain version publishes under "latest".
21+
type: string
22+
required: false
23+
default: ''
924

10-
defaults:
11-
run:
12-
working-directory: packages/doctor
25+
permissions: read-all
1326

1427
env:
1528
NPM_TAG: 'next'
1629

17-
permissions:
18-
contents: read
19-
2030
jobs:
21-
release:
31+
build:
32+
name: Build
2233
runs-on: ubuntu-latest
34+
permissions:
35+
contents: write
36+
defaults:
37+
run:
38+
working-directory: packages/doctor
39+
outputs:
40+
npm_version: ${{ steps.npm_version_output.outputs.NPM_VERSION }}
41+
npm_tag: ${{ steps.npm_version_output.outputs.NPM_TAG }}
42+
is_release: ${{ steps.npm_version_output.outputs.IS_RELEASE }}
2343

2444
steps:
25-
2645
- name: Harden the runner (Audit all outbound calls)
2746
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
2847
with:
2948
egress-policy: audit
3049

3150
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
51+
with:
52+
fetch-depth: 0
53+
token: ${{ secrets.GITHUB_TOKEN }}
54+
55+
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
56+
with:
57+
node-version: 22.14.0
58+
registry-url: "https://registry.npmjs.org"
3259

3360
- name: Setup
3461
run: npm install
3562

36-
- name: Generate Version
63+
- name: Get Current Version
64+
run: |
65+
NPM_VERSION=$(node -e "console.log(require('./package.json').version);")
66+
echo NPM_VERSION=$NPM_VERSION >> $GITHUB_ENV
67+
68+
- name: Bump, commit and tag stable release (manual dispatch)
69+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.release_type != '' && inputs.release_type != 'dev' }}
70+
env:
71+
# env indirection keeps the free-text dispatch input out of shell interpolation
72+
RELEASE_INPUT: ${{ inputs.release_type }}
3773
run: |
38-
echo NPM_VERSION=$(node -e "console.log(require('./package.json').version);")-$NPM_TAG-$(date +"%m-%d-%Y")-$GITHUB_RUN_ID >> $GITHUB_ENV
74+
git config user.name "github-actions[bot]"
75+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
76+
# npm version accepts a semver keyword (patch/minor/major) or an explicit
77+
# version; strip an optional leading "v" so v2.1.0 and 2.1.0 both work.
78+
# The tag is written by hand: npm's own v<version> tag namespace belongs
79+
# to the CLI package at the repo root.
80+
npm version "${RELEASE_INPUT#v}" --no-git-tag-version
81+
NPM_VERSION=$(node -e "console.log(require('./package.json').version);")
82+
echo NPM_VERSION=$NPM_VERSION >> $GITHUB_ENV
83+
git add package.json package-lock.json
84+
git commit -m "chore(doctor): release @nativescript/doctor@$NPM_VERSION"
85+
git tag -a "@nativescript/doctor@$NPM_VERSION" -m "@nativescript/doctor@$NPM_VERSION"
86+
git push origin HEAD:${GITHUB_REF_NAME} --follow-tags
3987
40-
- name: Bump Version
41-
run: npm --no-git-tag-version version $NPM_VERSION
88+
- name: Bump version for dev release
89+
if: ${{ !contains(github.ref, 'refs/tags/') && (github.event_name != 'workflow_dispatch' || inputs.release_type == '' || inputs.release_type == 'dev') }}
90+
env:
91+
NPM_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_type == 'dev' && 'dev' || 'next' }}
92+
# the shared version scripts live at the repo root, whose dependencies
93+
# are not installed here — semver resolves out of this package instead
94+
NODE_PATH: ${{ github.workspace }}/packages/doctor/node_modules
95+
run: |
96+
LAST_TAG=$(git describe --tags --abbrev=0 --match='@nativescript/doctor@*' 2>/dev/null || true)
97+
LAST_TAGGED_VERSION=${LAST_TAG##*@}
98+
# before the first scoped tag exists, the published version in
99+
# package.json is the only baseline to bump past
100+
export LAST_TAGGED_VERSION=${LAST_TAGGED_VERSION:-$NPM_VERSION}
101+
NPM_VERSION=$(node "$GITHUB_WORKSPACE/scripts/get-next-version.js")
102+
echo NPM_VERSION=$NPM_VERSION >> $GITHUB_ENV
103+
npm version $NPM_VERSION --no-git-tag-version
104+
105+
- name: Output NPM Version and tag
106+
id: npm_version_output
107+
env:
108+
# true only for a manual dispatch that cut a real (bumped) release — not
109+
# the empty "next" build or the "dev" channel. Computed in the GitHub
110+
# expression context so the free-text input never reaches the shell.
111+
IS_DISPATCH_RELEASE: ${{ github.event_name == 'workflow_dispatch' && inputs.release_type != '' && inputs.release_type != 'dev' }}
112+
NODE_PATH: ${{ github.workspace }}/packages/doctor/node_modules
113+
run: |
114+
NPM_TAG=$(node "$GITHUB_WORKSPACE/scripts/get-npm-tag.js")
115+
if [[ "${GITHUB_REF}" == refs/tags/* ]] || [[ "$IS_DISPATCH_RELEASE" == "true" ]]; then
116+
IS_RELEASE=true
117+
else
118+
IS_RELEASE=false
119+
fi
120+
echo NPM_VERSION=$NPM_VERSION >> $GITHUB_OUTPUT
121+
echo NPM_TAG=$NPM_TAG >> $GITHUB_OUTPUT
122+
echo IS_RELEASE=$IS_RELEASE >> $GITHUB_OUTPUT
42123
43124
- name: Build @nativescript/doctor
44125
run: npm pack
45126

46-
- name: Publish @nativescript/doctor
127+
- name: Upload npm package artifact
128+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
129+
with:
130+
name: npm-package-doctor
131+
path: packages/doctor/nativescript-doctor-${{steps.npm_version_output.outputs.NPM_VERSION}}.tgz
132+
133+
publish:
134+
runs-on: ubuntu-latest
135+
environment: npm-publish
136+
needs:
137+
- build
138+
permissions:
139+
contents: read
140+
id-token: write
141+
env:
142+
NPM_VERSION: ${{needs.build.outputs.npm_version}}
143+
NPM_TAG: ${{needs.build.outputs.npm_tag}}
144+
steps:
145+
- name: Harden the runner (Audit all outbound calls)
146+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
147+
with:
148+
egress-policy: audit
149+
150+
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
151+
with:
152+
node-version: 22.14.0
153+
registry-url: "https://registry.npmjs.org"
154+
155+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
156+
with:
157+
name: npm-package-doctor
158+
path: dist
159+
160+
- name: Update npm (required for OIDC trusted publishing)
161+
run: |
162+
npm install -g npm@^11.5.1
163+
npm --version
164+
165+
- name: Publish package (OIDC trusted publishing)
166+
if: ${{ vars.USE_NPM_TOKEN != 'true' }}
167+
run: |
168+
echo "Publishing @nativescript/doctor@$NPM_VERSION to NPM with tag $NPM_TAG via OIDC trusted publishing..."
169+
unset NODE_AUTH_TOKEN
170+
if [ -n "${NPM_CONFIG_USERCONFIG:-}" ]; then
171+
rm -f "$NPM_CONFIG_USERCONFIG"
172+
fi
173+
npm publish ./dist/nativescript-doctor-${{env.NPM_VERSION}}.tgz --tag $NPM_TAG --access public --provenance
47174
env:
48-
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
175+
NODE_AUTH_TOKEN: ""
176+
177+
- name: Publish package (granular token)
178+
if: ${{ vars.USE_NPM_TOKEN == 'true' }}
179+
run: |
180+
echo "Publishing @nativescript/doctor@$NPM_VERSION to NPM with tag $NPM_TAG via granular token..."
181+
npm publish ./dist/nativescript-doctor-${{env.NPM_VERSION}}.tgz --tag $NPM_TAG --access public --provenance
182+
env:
183+
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
184+
185+
github-release:
186+
runs-on: ubuntu-latest
187+
# runs for tag pushes and for manual dispatches that bumped a stable release
188+
if: ${{ needs.build.outputs.is_release == 'true' }}
189+
permissions:
190+
contents: write
191+
needs:
192+
- build
193+
env:
194+
NPM_VERSION: ${{needs.build.outputs.npm_version}}
195+
steps:
196+
- name: Harden the runner (Audit all outbound calls)
197+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
198+
with:
199+
egress-policy: audit
200+
201+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
202+
with:
203+
fetch-depth: 0
204+
ref: 'refs/tags/@nativescript/doctor@${{needs.build.outputs.npm_version}}'
205+
206+
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
207+
with:
208+
node-version: 22.14.0
209+
210+
- name: Setup
211+
working-directory: packages/doctor
212+
run: npm i --ignore-scripts --no-package-lock
213+
214+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
215+
with:
216+
name: npm-package-doctor
217+
path: dist
218+
219+
- name: Generate provenance statement
49220
run: |
50-
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
51-
echo "Publishing @nativescript/doctor@$NPM_VERSION to NPM with tag $NPM_TAG..."
52-
npm publish nativescript-doctor-$NPM_VERSION.tgz --tag $NPM_TAG
221+
TGZ_PATH=$(ls dist/nativescript-doctor-*.tgz | head -n1)
222+
TGZ_NAME=$(basename "$TGZ_PATH")
223+
TGZ_SHA=$(sha256sum "$TGZ_PATH" | awk '{ print $1 }')
224+
PROV_PATH="dist/${TGZ_NAME%.tgz}.intoto.jsonl"
225+
226+
cat > "$PROV_PATH" <<EOF
227+
{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"$TGZ_NAME","digest":{"sha256":"$TGZ_SHA"}}],"predicateType":"https://slsa.dev/provenance/v1"}
228+
EOF
229+
230+
- name: Partial Changelog
231+
working-directory: packages/doctor
232+
run: npx conventional-changelog -p angular -r2 --commit-path . --tag-prefix '@nativescript/doctor@' > "$GITHUB_WORKSPACE/body.md"
233+
234+
- uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
235+
with:
236+
tag: '@nativescript/doctor@${{needs.build.outputs.npm_version}}'
237+
artifacts: "dist/nativescript-doctor-*.tgz,dist/nativescript-doctor-*.intoto.jsonl"
238+
bodyFile: "body.md"
239+
prerelease: ${{needs.build.outputs.npm_tag != 'latest'}}
240+
allowUpdates: true

scripts/get-next-version.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ let lastTagVersion = (
3737
.stdout.toString()
3838
)
3939
.trim()
40-
.substring(1);
40+
// git describe emits the tag verbatim (v9.1.0); LAST_TAGGED_VERSION callers
41+
// pass a bare version, which for scoped packages has no "v" to strip
42+
.replace(/^v/, "");
4143
if (!semver.parse(lastTagVersion)) {
4244
throw new Error("Invalid last tag version");
4345
}

0 commit comments

Comments
 (0)