Skip to content

Commit 3e00ac8

Browse files
committed
feat: swift package release workflow
1 parent 6778dfd commit 3e00ac8

12 files changed

Lines changed: 524 additions & 108 deletions

File tree

.github/workflows/npm_release.yml

Lines changed: 232 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,57 @@ env:
99
NPM_TAG: "next"
1010
XCODE_VERSION: "^15.0"
1111

12+
# The runtime xcframeworks are no longer shipped inside the npm packages. Each
13+
# build publishes them as GitHub Release assets and points the SwiftPM manifest
14+
# (github.com/NativeScript/ios-spm) at them via binaryTarget(url:checksum:). The
15+
# slim npm package only carries the Xcode project template + metadata generator.
16+
#
17+
# Flow: setup → build (ios, visionos) → test → github-release (assets) →
18+
# spm-update (stamp + tag ios-spm) → publish (npm) → verify-spm
1219
jobs:
20+
setup:
21+
name: Resolve version
22+
runs-on: ubuntu-latest
23+
outputs:
24+
npm_version: ${{ steps.out.outputs.NPM_VERSION }}
25+
npm_tag: ${{ steps.out.outputs.NPM_TAG }}
26+
steps:
27+
- name: Harden the runner (Audit all outbound calls)
28+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
29+
with:
30+
egress-policy: audit
31+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
32+
with:
33+
fetch-depth: 0
34+
persist-credentials: false
35+
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
36+
with:
37+
node-version: 22
38+
- name: Install deps for version scripts
39+
run: npm install --no-audit --no-fund
40+
- name: Compute version and tag
41+
id: out
42+
run: |
43+
NPM_VERSION=$(node -e "console.log(require('./package.json').version);")
44+
if [ "${GITHUB_REF#refs/tags/}" = "$GITHUB_REF" ]; then
45+
# not a tag build -> dev/prerelease version
46+
NPM_VERSION=$(node ./scripts/get-next-version.js)
47+
fi
48+
NPM_TAG=$(NPM_VERSION=$NPM_VERSION node ./scripts/get-npm-tag.js)
49+
echo "NPM_VERSION=$NPM_VERSION" >> $GITHUB_OUTPUT
50+
echo "NPM_TAG=$NPM_TAG" >> $GITHUB_OUTPUT
51+
echo "Resolved $NPM_VERSION (tag: $NPM_TAG)"
52+
1353
build:
14-
name: Build
54+
name: Build ${{ matrix.target }}
1555
runs-on: macos-14
16-
outputs:
17-
npm_version: ${{ steps.npm_version_output.outputs.NPM_VERSION }}
18-
npm_tag: ${{ steps.npm_version_output.outputs.NPM_TAG }}
56+
needs: setup
57+
strategy:
58+
fail-fast: false
59+
matrix:
60+
target: [ios, visionos]
61+
env:
62+
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
1963
steps:
2064
- name: Harden the runner (Audit all outbound calls)
2165
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
@@ -55,34 +99,26 @@ jobs:
5599
sudo mkdir -p /usr/local/bin
56100
sudo ln -sf "$(command -v cmake)" /usr/local/bin/cmake
57101
fi
58-
- name: Get Current Version
59-
run: |
60-
NPM_VERSION=$(node -e "console.log(require('./package.json').version);")
61-
echo NPM_VERSION=$NPM_VERSION >> $GITHUB_ENV
62-
- name: Bump version for dev release
63-
if: ${{ !contains(github.ref, 'refs/tags/') }}
64-
run: |
65-
NPM_VERSION=$(node ./scripts/get-next-version.js)
66-
echo NPM_VERSION=$NPM_VERSION >> $GITHUB_ENV
67-
npm version $NPM_VERSION --no-git-tag-version
68-
- name: Output NPM Version and tag
69-
id: npm_version_output
70-
run: |
71-
NPM_TAG=$(node ./scripts/get-npm-tag.js)
72-
echo NPM_VERSION=$NPM_VERSION >> $GITHUB_OUTPUT
73-
echo NPM_TAG=$NPM_TAG >> $GITHUB_OUTPUT
74-
- name: Build
75-
run: npm run build-ios
102+
- name: Set package version
103+
run: npm version $NPM_VERSION --no-git-tag-version --allow-same-version
104+
- name: Build (${{ matrix.target }})
105+
run: npm run build-${{ matrix.target }}
76106
- name: Upload npm package artifact
77107
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
78108
with:
79-
name: npm-package
80-
path: dist/nativescript-ios-${{steps.npm_version_output.outputs.NPM_VERSION}}.tgz
109+
name: npm-package-${{ matrix.target }}
110+
path: dist/nativescript-*-${{ env.NPM_VERSION }}.tgz
111+
- name: Upload SwiftPM artifacts (xcframework zips + checksums)
112+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
113+
with:
114+
name: spm-artifacts-${{ matrix.target }}
115+
path: dist/artifacts/*
81116
- name: Upload dSYMs artifact
82117
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
83118
with:
84-
name: NativeScript-dSYMs
119+
name: dSYMs-${{ matrix.target }}
85120
path: dist/dSYMs
121+
86122
test:
87123
name: Test
88124
runs-on: macos-14
@@ -153,18 +189,144 @@ jobs:
153189
with:
154190
name: test-results
155191
path: ${{env.TEST_FOLDER}}/test_results.xcresult
192+
193+
# Publish the xcframework zips + dSYMs as a GitHub Release asset for EVERY
194+
# version (prerelease unless 'latest'), because the SwiftPM binaryTarget url
195+
# must resolve for `next`/`pr` consumers too — not only tagged releases.
196+
github-release:
197+
name: GitHub Release (SPM assets)
198+
runs-on: ubuntu-latest
199+
permissions:
200+
contents: write
201+
needs:
202+
- setup
203+
- build
204+
- test
205+
env:
206+
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
207+
NPM_TAG: ${{ needs.setup.outputs.npm_tag }}
208+
steps:
209+
- name: Harden the runner (Audit all outbound calls)
210+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
211+
with:
212+
egress-policy: audit
213+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
214+
with:
215+
fetch-depth: 0
216+
persist-credentials: false
217+
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
218+
with:
219+
node-version: 22
220+
- name: Setup
221+
run: npm install --no-audit --no-fund
222+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
223+
with:
224+
pattern: spm-artifacts-*
225+
path: spm-artifacts
226+
merge-multiple: true
227+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
228+
with:
229+
pattern: dSYMs-*
230+
path: dist/dSYMs
231+
merge-multiple: true
232+
- name: Zip dSYMs
233+
working-directory: dist/dSYMs
234+
run: find . -maxdepth 1 -name '*.dSYM' -print | xargs -I@ zip -r @.zip @ || true
235+
- name: Partial Changelog
236+
run: npx conventional-changelog -p angular -r2 > body.md
237+
- name: Create / update release with SPM artifacts
238+
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0
239+
with:
240+
tag: "v${{ env.NPM_VERSION }}"
241+
name: "v${{ env.NPM_VERSION }}"
242+
commit: ${{ github.sha }}
243+
artifacts: "spm-artifacts/*.xcframework.zip,dist/dSYMs/*.zip"
244+
bodyFile: "body.md"
245+
prerelease: ${{ needs.setup.outputs.npm_tag != 'latest' }}
246+
allowUpdates: true
247+
248+
# Stamp + tag github.com/NativeScript/ios-spm to point at this release's assets.
249+
# Requires a cross-repo token (GITHUB_TOKEN cannot write to ios-spm). Enable by
250+
# setting repo variable ENABLE_SPM_UPDATE=true and secret IOS_SPM_TOKEN.
251+
spm-update:
252+
name: Update ios-spm manifest
253+
runs-on: ubuntu-latest
254+
if: ${{ vars.ENABLE_SPM_UPDATE == 'true' }}
255+
needs:
256+
- setup
257+
- build
258+
- test
259+
- github-release
260+
env:
261+
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
262+
NPM_TAG: ${{ needs.setup.outputs.npm_tag }}
263+
steps:
264+
- name: Harden the runner (Audit all outbound calls)
265+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
266+
with:
267+
egress-policy: audit
268+
- name: Checkout ios (for stamping script)
269+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
270+
with:
271+
path: ios
272+
persist-credentials: false
273+
- name: Checkout ios-spm
274+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
275+
with:
276+
repository: NativeScript/ios-spm
277+
token: ${{ secrets.IOS_SPM_TOKEN }}
278+
path: ios-spm
279+
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
280+
with:
281+
node-version: 22
282+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
283+
with:
284+
pattern: spm-artifacts-*
285+
path: spm-artifacts
286+
merge-multiple: true
287+
- name: Stamp Package.swift
288+
run: |
289+
STRICT=""
290+
if [ "$NPM_TAG" = "latest" ]; then STRICT="--strict"; fi
291+
CHECKSUM_ARGS=""
292+
for f in spm-artifacts/checksums-*.env; do
293+
[ -f "$f" ] && CHECKSUM_ARGS="$CHECKSUM_ARGS --checksums $f"
294+
done
295+
echo "Using checksum files:$CHECKSUM_ARGS"
296+
if [ -z "$CHECKSUM_ARGS" ]; then echo "No checksum files found" >&2; exit 1; fi
297+
node ios/scripts/stamp-spm-release.mjs \
298+
--package ios-spm/Package.swift \
299+
--version "$NPM_VERSION" \
300+
$CHECKSUM_ARGS $STRICT
301+
- name: Commit and tag ios-spm
302+
working-directory: ios-spm
303+
run: |
304+
git config user.name "NativeScript Bot"
305+
git config user.email "oss@nativescript.org"
306+
git add Package.swift
307+
git commit -m "release: $NPM_VERSION" || echo "no changes to commit"
308+
git tag -f "$NPM_VERSION"
309+
git push origin HEAD:main
310+
git push -f origin "refs/tags/$NPM_VERSION"
311+
156312
publish:
313+
name: Publish ${{ matrix.target }}
157314
runs-on: ubuntu-latest
158315
environment: npm-publish
159316
needs:
317+
- setup
160318
- build
161319
- test
162320
permissions:
163321
contents: read
164322
id-token: write
323+
strategy:
324+
fail-fast: false
325+
matrix:
326+
target: [ios, visionos]
165327
env:
166-
NPM_VERSION: ${{needs.build.outputs.npm_version}}
167-
NPM_TAG: ${{needs.build.outputs.npm_tag}}
328+
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
329+
NPM_TAG: ${{ needs.setup.outputs.npm_tag }}
168330
steps:
169331
- name: Harden the runner (Audit all outbound calls)
170332
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
@@ -176,73 +338,77 @@ jobs:
176338
registry-url: "https://registry.npmjs.org"
177339
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
178340
with:
179-
name: npm-package
341+
name: npm-package-${{ matrix.target }}
180342
path: dist
181343
- name: Update npm (required for OIDC trusted publishing)
182344
run: |
183345
corepack enable npm
184346
corepack install -g npm@11.5.1
185347
test "$(npm --version)" = "11.5.1"
186348
test "$(npx --version)" = "11.5.1"
349+
- name: Resolve package name
350+
run: |
351+
if [ "${{ matrix.target }}" = "ios" ]; then echo "PKG=ios" >> $GITHUB_ENV; else echo "PKG=visionos" >> $GITHUB_ENV; fi
187352
- name: Publish package (OIDC trusted publishing)
188353
if: ${{ vars.USE_NPM_TOKEN != 'true' }}
189354
run: |
190-
echo "Publishing @nativescript/ios@$NPM_VERSION to NPM with tag $NPM_TAG via OIDC trusted publishing..."
355+
echo "Publishing @nativescript/$PKG@$NPM_VERSION to NPM with tag $NPM_TAG via OIDC trusted publishing..."
191356
unset NODE_AUTH_TOKEN
192357
if [ -n "${NPM_CONFIG_USERCONFIG:-}" ]; then
193358
rm -f "$NPM_CONFIG_USERCONFIG"
194359
fi
195-
npm publish ./dist/nativescript-ios-${{env.NPM_VERSION}}.tgz --tag $NPM_TAG --access public --provenance
360+
npm publish ./dist/nativescript-${PKG}-${NPM_VERSION}.tgz --tag $NPM_TAG --access public --provenance
196361
env:
197362
NODE_AUTH_TOKEN: ""
198-
199363
- name: Publish package (granular token)
200364
if: ${{ vars.USE_NPM_TOKEN == 'true' }}
201365
run: |
202-
echo "Publishing @nativescript/ios@$NPM_VERSION to NPM with tag $NPM_TAG via granular token..."
203-
npm publish ./dist/nativescript-ios-${{env.NPM_VERSION}}.tgz --tag $NPM_TAG --access public --provenance
366+
echo "Publishing @nativescript/$PKG@$NPM_VERSION to NPM with tag $NPM_TAG via granular token..."
367+
npm publish ./dist/nativescript-${PKG}-${NPM_VERSION}.tgz --tag $NPM_TAG --access public --provenance
204368
env:
205369
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
206-
github-release:
207-
runs-on: ubuntu-latest
208-
# only runs on tagged commits
209-
if: ${{ contains(github.ref, 'refs/tags/') }}
210-
permissions:
211-
contents: write
370+
371+
# Post-publish smoke test: resolve ios-spm at the released tag and verify the
372+
# iOS binary artifact downloads + checksum-validates against the real Release.
373+
verify-spm:
374+
name: Verify SPM resolution
375+
runs-on: macos-14
376+
if: ${{ vars.ENABLE_SPM_UPDATE == 'true' }}
212377
needs:
213-
- build
214-
- test
378+
- setup
379+
- spm-update
215380
env:
216-
NPM_VERSION: ${{needs.build.outputs.npm_version}}
381+
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
217382
steps:
218383
- name: Harden the runner (Audit all outbound calls)
219384
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
220385
with:
221386
egress-policy: audit
222-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
223-
with:
224-
fetch-depth: 0
225387
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
226388
with:
227389
node-version: 22
228-
- name: Setup
229-
run: npm install
230-
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
231-
with:
232-
name: npm-package
233-
path: dist
234-
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
235-
with:
236-
name: NativeScript-dSYMs
237-
path: dist/dSYMs
238-
- name: Zip dSYMs
239-
working-directory: dist/dSYMs
240-
run: find . -maxdepth 1 -name '*.dSYM' -print | xargs -I@ zip -r @.zip @
241-
- name: Partial Changelog
242-
run: npx conventional-changelog -p angular -r2 > body.md
243-
- uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0
244-
with:
245-
artifacts: "dist/nativescript-ios-*.tgz,dist/dSYMs/*.zip"
246-
bodyFile: "body.md"
247-
prerelease: ${{needs.build.outputs.npm_tag != 'latest'}}
248-
allowUpdates: true
390+
- name: Generate a probe package that depends on ios-spm
391+
run: |
392+
node -e '
393+
const fs = require("fs");
394+
const v = process.env.NPM_VERSION;
395+
fs.mkdirSync("spmverify/Sources/Probe", { recursive: true });
396+
fs.writeFileSync("spmverify/Package.swift",
397+
"// swift-tools-version: 5.10\n" +
398+
"import PackageDescription\n" +
399+
"let package = Package(\n" +
400+
" name: \"Probe\",\n" +
401+
" platforms: [.iOS(.v13)],\n" +
402+
" dependencies: [.package(url: \"https://github.com/NativeScript/ios-spm.git\", exact: \"" + v + "\")],\n" +
403+
" targets: [.target(name: \"Probe\", dependencies: [.product(name: \"NativeScript\", package: \"ios-spm\")])]\n" +
404+
")\n");
405+
fs.writeFileSync("spmverify/Sources/Probe/Probe.swift", "// probe\n");
406+
'
407+
- name: Resolve (downloads the xcframework zip and verifies its checksum)
408+
working-directory: spmverify
409+
run: |
410+
# Resolution fetches the binary artifacts for the NativeScript product
411+
# and verifies each checksum against the stamped manifest; a mismatch or
412+
# a missing release asset fails the release here.
413+
swift package resolve
414+
echo "ios-spm@$NPM_VERSION resolved and checksum-verified."

build_all_ios.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ rm -rf ./dist
77
./build_nativescript.sh --no-vision
88
./build_tklivesync.sh --no-vision
99
./prepare_dSYMs.sh
10+
./build_spm_artifacts.sh ios
1011
./build_npm_ios.sh

build_all_vision.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ rm -rf ./dist
77
./build_nativescript.sh --no-catalyst --no-iphone --no-sim
88
./build_tklivesync.sh --no-catalyst --no-iphone --no-sim
99
./prepare_dSYMs.sh
10+
./build_spm_artifacts.sh visionos
1011
./build_npm_vision.sh

0 commit comments

Comments
 (0)