Skip to content

Commit 99368b7

Browse files
committed
ci: attestations
1 parent a9de817 commit 99368b7

6 files changed

Lines changed: 262 additions & 49 deletions

File tree

.github/workflows/build-and-release.yaml

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ on:
1616
publish-binary:
1717
type: boolean
1818
default: false
19-
description: Publish binary?
19+
description: Force publish binary?
2020
required: true
2121

2222
env:
@@ -26,7 +26,6 @@ env:
2626
# https://www.electronjs.org/docs/latest/tutorial/installation#cache
2727
electron_config_cache: ~/.cache/electron
2828
NODE_LIBCURL_GITHUB_TOKEN: ${{ secrets.NODE_LIBCURL_GITHUB_TOKEN }}
29-
PUBLISH_BINARY: ${{ inputs.publish-binary }}
3029

3130
concurrency:
3231
group: build-and-release-${{ github.ref_name }}
@@ -66,6 +65,11 @@ jobs:
6665
run: pnpm build:dist
6766

6867
build-and-release:
68+
permissions:
69+
id-token: write
70+
attestations: write
71+
contents: write
72+
packages: write
6973
runs-on: ${{ matrix.os == 'alpine' && 'ubuntu-22.04' || matrix.os }}
7074
container: ${{ matrix.os == 'alpine' && format('node:{0}-alpine3.21', matrix.node) || '' }}
7175
needs:
@@ -78,26 +82,26 @@ jobs:
7882
electron-version:
7983
- ''
8084
os:
81-
# - macos-15
82-
# - ubuntu-22.04
85+
- macos-15
86+
- ubuntu-22.04
8387
# - ubuntu-24.04-arm
8488
- alpine
85-
# - windows-2025
89+
- windows-2025
8690
libcurl-release:
8791
- ${{ needs.set-params.outputs.latest-libcurl-release }}
8892
node:
8993
- 24
9094
- 22
91-
# include:
92-
# # electron builds
93-
# - os: ubuntu-22.04
94-
# libcurl-release: ${{ needs.set-params.outputs.latest-libcurl-release }}
95-
# node: 24
96-
# electron-version: 38.1.2
97-
# - os: macos-15
98-
# libcurl-release: ${{ needs.set-params.outputs.latest-libcurl-release }}
99-
# node: 24
100-
# electron-version: 38.1.2
95+
include:
96+
# electron builds
97+
- os: ubuntu-22.04
98+
libcurl-release: ${{ needs.set-params.outputs.latest-libcurl-release }}
99+
node: 24
100+
electron-version: 38.1.2
101+
- os: macos-15
102+
libcurl-release: ${{ needs.set-params.outputs.latest-libcurl-release }}
103+
node: 24
104+
electron-version: 38.1.2
101105
env:
102106
LIBCURL_RELEASE: ${{ matrix.libcurl-release }}
103107
LATEST_LIBCURL_RELEASE: ${{ matrix.libcurl-release }}
@@ -121,7 +125,14 @@ jobs:
121125

122126
- if: runner.os == 'Linux' && matrix.os != 'alpine'
123127
name: Install Needed packages on Linux
124-
run: sudo apt-get install -y cmake groff
128+
run: sudo apt-get update && sudo apt-get install -y cmake groff
129+
130+
- name: Export Electron npm_config envs
131+
if: matrix.electron-version
132+
run: |
133+
echo "npm_config_runtime=electron" >> $GITHUB_ENV
134+
echo "npm_config_dist_url=https://electronjs.org/headers" >> $GITHUB_ENV
135+
echo "npm_config_target=${{ matrix.electron-version }}" >> $GITHUB_ENV
125136
126137
- name: Checkout
127138
uses: actions/checkout@v5
@@ -168,12 +179,14 @@ jobs:
168179
uses: mxschmitt/action-tmate@v3
169180
if: matrix.enable-debugging
170181

171-
- name: 'Publish Binary'
182+
- name: 'Build and Package Binary'
172183
if: runner.os != 'Windows'
173-
run: |
174-
GIT_COMMIT=${{ github.sha }} \
175-
GIT_REF_NAME=${{ github.ref_name}} \
176-
./scripts/ci/build.sh
184+
env:
185+
# this is false because we publish as a separate step
186+
PUBLISH_BINARY: false
187+
GIT_COMMIT: ${{ github.sha }}
188+
GIT_REF_NAME: ${{ github.ref_name }}
189+
run: ./scripts/ci/build.sh
177190

178191
- name: 'Check if fully installed and built'
179192
id: built-and-installed
@@ -192,15 +205,42 @@ jobs:
192205
path: |
193206
~/.node-gyp
194207
~/deps
195-
key: v4-${{ runner.os }}-libcurl-deps-cache-${{ matrix.electron-version && 'electron' || 'node' }}-${{ matrix.electron-version || matrix.node }}
208+
key: v4-${{ matrix.os }}-libcurl-deps-cache-${{ matrix.electron-version && 'electron' || 'node' }}-${{ matrix.electron-version || matrix.node }}
196209

197-
- name: 'Publish Binary Windows'
210+
- name: 'Build and Package Binary Windows'
198211
if: runner.os == 'Windows'
199212
shell: pwsh
213+
env:
214+
# this is false because we publish as a separate step
215+
PUBLISH_BINARY: false
216+
GIT_COMMIT: ${{ github.sha }}
217+
GIT_REF_NAME: ${{ github.ref_name }}
218+
run: ./scripts/ci/windows/build.ps1
219+
220+
- name: Create attestations
221+
uses: actions/attest-build-provenance@v3
222+
if: inputs.publish-binary
223+
with:
224+
subject-path: 'build/**/node_libcurl-*.tar.gz'
225+
226+
- name: 'Publish Binary [macos arm64]'
227+
if: runner.os == 'macOS' && inputs.publish-binary
228+
env:
229+
npm_config_target_arch: arm64
230+
run: |
231+
node scripts/module-packaging.js --publish "$(pnpm --silent pregyp reveal staged_tarball --silent)"
232+
233+
- name: 'Publish Binary [macos x64]'
234+
if: runner.os == 'macOS' && inputs.publish-binary
235+
env:
236+
npm_config_target_arch: x64
237+
run: |
238+
node scripts/module-packaging.js --publish "$(pnpm --silent pregyp reveal staged_tarball --silent)"
239+
240+
- name: 'Publish Binary [non macos]'
241+
if: runner.os != 'macOS' && inputs.publish-binary
200242
run: |
201-
$env:GIT_COMMIT = '${{ github.sha }}'
202-
$env:GIT_REF_NAME = '${{ github.ref_name }}'
203-
./scripts/ci/windows/build.ps1
243+
node scripts/module-packaging.js --publish "$(pnpm --silent pregyp reveal staged_tarball --silent)"
204244
205245
- name: Upload artifacts
206246
if: always() && runner.os != 'Windows'

.github/workflows/publish.yml

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
name: publish
2+
3+
defaults:
4+
run:
5+
shell: bash
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
enable-debugging:
11+
type: boolean
12+
description: Enable tmate session
13+
required: false
14+
default: false
15+
version:
16+
type: string
17+
description: Version to release, takes precedence over release-type
18+
required: false
19+
default: ''
20+
release-type:
21+
type: choice
22+
description: Kind of release
23+
required: true
24+
default: patch
25+
options:
26+
- prerelease
27+
- premajor
28+
- preminor
29+
- prepatch
30+
- major
31+
- minor
32+
- patch
33+
34+
concurrency:
35+
group: release
36+
cancel-in-progress: false
37+
38+
env:
39+
NODE_VERSION: 24
40+
41+
jobs:
42+
release:
43+
runs-on: ubuntu-22.04
44+
environment: release
45+
permissions:
46+
id-token: write
47+
attestations: write
48+
contents: write
49+
packages: write
50+
steps:
51+
- uses: actions/create-github-app-token@v2
52+
id: app-token
53+
with:
54+
app-id: ${{ vars.APP_ID }}
55+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
56+
57+
- name: Checkout
58+
uses: actions/checkout@v5
59+
with:
60+
fetch-depth: 0
61+
token: ${{ steps.app-token.outputs.token }}
62+
63+
- name: Get GitHub App User ID
64+
id: get-user-id
65+
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
66+
env:
67+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
68+
69+
- run: |
70+
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
71+
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
72+
73+
# Node.js / PNPM
74+
- uses: pnpm/action-setup@v4
75+
- name: Set up Node ${{ env.NODE_VERSION }}
76+
uses: actions/setup-node@v5
77+
with:
78+
node-version: '${{ env.NODE_VERSION }}'
79+
cache: 'pnpm'
80+
package-manager-cache: 'pnpm'
81+
82+
- name: Setup tmate session
83+
uses: mxschmitt/action-tmate@v3
84+
if: inputs.enable-debugging
85+
86+
- name: Install
87+
run: pnpm install --frozen-lockfile
88+
89+
- uses: actions/github-script@v8
90+
id: version-check
91+
with:
92+
result-encoding: json
93+
script: |
94+
const semver = require('semver');
95+
const refName = '${{ github.ref_name }}';
96+
let version = '${{ inputs.version }}'.trim();
97+
const currentVersion = require('./package.json').version;
98+
99+
if (version) {
100+
const isValid = semver.valid(version);
101+
if (!isValid) {
102+
throw new Error('Version is not a valid semver');
103+
}
104+
105+
// must be greater than current version
106+
const isGt = semver.gt(version, currentVersion);
107+
if (!isGt) {
108+
throw new Error('Version is not greater than current version');
109+
}
110+
} else {
111+
version = semver.inc(currentVersion, '${{ inputs.release-type }}');
112+
if (!version) {
113+
throw new Error(`Failed to increment version with release type ${{ inputs.release-type }} for current version ${currentVersion}`);
114+
}
115+
}
116+
117+
const preRelease = semver.prerelease(version);
118+
const isPreRelease = preRelease !== null;
119+
120+
if (refName !== 'master' && !isPreRelease) {
121+
throw new Error('Version is not a prerelease and not on the main branch.');
122+
}
123+
124+
return { version, isPreRelease };
125+
126+
- name: Run lint
127+
run: pnpm lint
128+
- name: Run tsc
129+
run: pnpm build:dist
130+
131+
- name: Bump version (and run hooks) and create commit
132+
run: pnpm version ${{ fromJson(steps.version-check.outputs.result).version }}
133+
134+
- name: Push Commit and Tags
135+
run: git push origin --follow-tags
136+
137+
# Use https://github.com/changesets/changesets eventually
138+
- name: Create release
139+
env:
140+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
141+
run: |
142+
VERSION=${{ fromJson(steps.version-check.outputs.result).version }}
143+
IS_PRE_RELEASE=${{ fromJson(steps.version-check.outputs.result).isPreRelease }}
144+
IS_LATEST=${{ fromJson(steps.version-check.outputs.result).isPreRelease == false }}
145+
gh release create v$VERSION \
146+
--fail-on-no-commits \
147+
--verify-tag \
148+
--prerelease=$IS_PRE_RELEASE \
149+
--latest=$IS_LATEST \
150+
--title=v$VERSION \
151+
--draft
152+
153+
- name: Ensure clean git state
154+
run: git diff --exit-code
155+
156+
- name: Sleep for 1 minute
157+
run: sleep 60
158+
159+
- name: Publish to NPM
160+
env:
161+
NPM_CONFIG_PROVENANCE: true
162+
run: |
163+
pnpm publish \
164+
--access public \
165+
--no-git-checks \
166+
--tag ${{ fromJson(steps.version-check.outputs.result).isPreRelease && 'next' || 'latest' }}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
"np": "10.2.0",
116116
"prettier": "3.6.2",
117117
"progress": "2.0.3",
118+
"semver": "7.7.2",
118119
"sort-package-json": "3.4.0",
119120
"tsx": "4.20.6",
120121
"typedoc": "0.28.13",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/ci/build.sh

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -435,30 +435,35 @@ if [ "$RUN_TESTS" == "true" ]; then
435435
fi
436436
fi
437437

438+
# Create the tarballs
439+
if [[ "$MACOS_UNIVERSAL_BUILD" == "true" ]]; then
440+
# Need to publish two binaries when doing a universal build.
441+
#
442+
# Could also publish the universal build twice instead, but it might not
443+
# play well with electron-builder which will try to lipo native add-ons
444+
# for different architectures.
445+
# --
446+
lipo build/Release/node_libcurl.node -thin x86_64 -output lib/binding/node_libcurl.node
447+
lipo build/Release/node_libcurl.node -thin arm64 -output lib/binding/node_libcurl.node
448+
449+
npm_config_target_arch=arm64 pnpm pregyp package testpackage --verbose
450+
npm_config_target_arch=x64 pnpm pregyp package testpackage --verbose
451+
else
452+
pnpm pregyp package testpackage --verbose
453+
fi
454+
438455
# If we are here, it means the addon worked
439456
# Check if we need to publish the binaries
457+
# Notice, this is only useful if publishing locally, as the CI has a separate
458+
# step defined on the workflow itself, which uses attestations.
440459
if [[ $PUBLISH_BINARY == true && $LIBCURL_RELEASE == $LATEST_LIBCURL_RELEASE ]]; then
441-
echo "Publish binary is true - Testing and publishing package with pregyp"
460+
echo "Publish binary is true - Publishing package with pregyp"
442461
if [[ "$MACOS_UNIVERSAL_BUILD" == "true" ]]; then
443-
# Need to publish two binaries when doing a universal build.
444-
#
445-
# Could also publish the universal build twice instead, but it might not
446-
# play well with electron-builder which will try to lipo native add-ons
447-
# for different architectures.
448-
# --
449-
# Build and publish x64 package
450-
lipo build/Release/node_libcurl.node -thin x86_64 -output lib/binding/node_libcurl.node
451-
npm_config_target_arch=x64 pnpm pregyp package testpackage --verbose
452-
npm_config_target_arch=x64 node scripts/module-packaging.js --publish \
462+
node scripts/module-packaging.js --publish \
453463
"$(npm_config_target_arch=x64 pnpm --silent pregyp reveal staged_tarball --silent)"
454-
455-
# Build and publish arm64 package.
456-
lipo build/Release/node_libcurl.node -thin arm64 -output lib/binding/node_libcurl.node
457-
npm_config_target_arch=arm64 pnpm pregyp package --verbose # Can't testpackage for arm64 yet.
458-
npm_config_target_arch=arm64 node scripts/module-packaging.js --publish \
464+
node scripts/module-packaging.js --publish \
459465
"$(npm_config_target_arch=arm64 pnpm --silent pregyp reveal staged_tarball --silent)"
460466
else
461-
pnpm pregyp package testpackage --verbose
462467
node scripts/module-packaging.js --publish "$(pnpm --silent pregyp reveal staged_tarball --silent)"
463468
fi
464469
fi

0 commit comments

Comments
 (0)