-
Notifications
You must be signed in to change notification settings - Fork 0
214 lines (201 loc) · 8.59 KB
/
Copy pathinstall.yml
File metadata and controls
214 lines (201 loc) · 8.59 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
name: Install
# `npm i zudb`, on a machine that has nothing else on it.
#
# Every other job in this repository runs on a hosted image, and a
# hosted image is the least representative computer in the world: it
# has a compiler, a Rust toolchain, a git, a Python, and a hundred
# libraries an addon can quietly link to and get away with. The
# failures that only a user's machine sees are the ones nothing here
# looks for. A file left out of `files` and loaded out of the checkout
# instead. An addon linked against a symbol version the build image had
# and a slim image has not. A platform package whose `os`, `cpu` or
# `libc` says the wrong thing, so npm installs it where it cannot load
# and skips it where it would have worked.
#
# So this builds the two Linux binaries a release would build and
# installs what a release would publish, in a container that holds a
# runtime, a package manager and nothing else. tools/install.mjs is the
# program it runs there, which is the same one the release workflow
# runs and for the same reason: it packs the tarballs npm would fetch,
# installs them into an empty project outside the checkout, and runs a
# statement through both module formats.
#
# Nightly rather than on every push, because what it catches is drift
# in things outside this repository. A base image whose glibc moved, a
# musl that went forward, an npm that changed how it reads `libc`. None
# of that is in a diff anybody here writes, and all of it arrives on its
# own schedule.
on:
schedule:
# Late enough that the day's merges are in, and not on the hour,
# where every scheduled job on the service is queued behind every
# other one.
- cron: "41 5 * * *"
workflow_dispatch:
# The workflow and the program it runs are exercised on the pull
# request that changes them, since a nightly that broke is a nightly
# nobody reads for a week.
pull_request:
paths:
- .github/workflows/install.yml
- tools/install.mjs
- tools/platforms.mjs
- package.json
- npm/**
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
# The Node the two build images do not carry. Pinned, like every other
# toolchain here, because a build whose tools float is a build nobody
# can reproduce on the day it breaks.
NODE_VERSION: v24.19.0
jobs:
# The two Linux binaries a container can load, built the way the
# release builds them and for the same reasons: manylinux_2_28 is the
# glibc floor, and an addon linked against the runner's own glibc is
# the failure that gets reported as "works on my machine". Release and
# not debug, because what is being installed has to be what would be
# published.
binary:
name: ${{ matrix.libc }}
runs-on: ubuntu-latest
container: ${{ matrix.container }}
strategy:
fail-fast: false
matrix:
include:
- libc: gnu
target: x86_64-unknown-linux-gnu
container: quay.io/pypa/manylinux_2_28_x86_64:2026.08.15-1
node: linux-x64
- libc: musl
target: x86_64-unknown-linux-musl
alpine: alpine:3.24.1
steps:
- uses: actions/checkout@v7
# The manylinux image carries a compiler for wheels and nothing
# for this. The tarball rather than a package, because the image
# is RHEL 8 and its Node is older than this package's floor.
- name: Node and rustup, inside the image
if: matrix.container != ''
run: |
set -eu
curl -fsSL "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-${{ matrix.node }}.tar.xz" \
| tar -xJ -C /usr/local --strip-components=1
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --no-modify-path --profile minimal
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- uses: Swatinem/rust-cache@v2
if: matrix.alpine == ''
with:
key: ${{ matrix.target }}
- name: Build
if: matrix.alpine == ''
run: |
set -eu
npm ci
npx napi build --platform --release --js binding.cjs --dts binding.d.cts
# Alpine builds its own architecture natively, so there is no
# target flag. The crt-static override is what makes a shared
# object possible at all on musl, whose default is to link the C
# runtime statically into everything.
- name: Build, inside Alpine
if: matrix.alpine != ''
run: |
docker run --rm -v "$PWD":/work -w /work \
-e RUSTFLAGS="-C target-feature=-crt-static" \
-e CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=gcc \
${{ matrix.alpine }} sh -c '
set -eu
apk add --no-cache nodejs npm rustup build-base
rustup-init -y --no-modify-path --profile minimal
export PATH="$HOME/.cargo/bin:$PATH"
npm ci
npx napi build --platform --release --js binding.cjs --dts binding.d.cts
'
# One file, named after the platform it runs on, which is what the
# loader looks for and what the platform package publishes.
- run: node tools/binary.mjs ${{ matrix.target }}
- uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.libc }}
path: ./*.node
if-no-files-found: error
# And the install, in a container that is the whole point of the job.
#
# docker run rather than a job container, because a job container has
# the runner's own Node mounted into it and half the reason to use a
# slim image is that nothing is mounted into it. This way the only
# things inside are the image and this checkout.
#
# Three rows: the version this package requires, the current release,
# and musl, which is where a platform package with the wrong `libc` in
# it installs anyway and then fails at the require.
clean:
name: ${{ matrix.image }}
needs: binary
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- image: node:24-slim
libc: gnu
dir: linux-x64-gnu
- image: node:26-slim
libc: gnu
dir: linux-x64-gnu
- image: node:24-alpine
libc: musl
dir: linux-x64-musl
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v4
with:
name: binary-${{ matrix.libc }}
# Where a release puts it and where npm would fetch it from. The
# manifest beside it is committed, so this is the only piece that
# is not already in the checkout.
- run: mv zudb.${{ matrix.dir }}.node npm/${{ matrix.dir }}/
- name: An install, on a machine with only the language runtime
run: |
docker run --rm -v "$PWD":/work -w /work \
-e npm_config_offline=true \
${{ matrix.image }} sh -c '
set -eu
# What the image is claimed to be, checked rather than
# believed, because the day a base image starts shipping a
# compiler is the day this job silently stops being about
# anything. node-gyp is named too: an install that falls
# back to building from source is an install this package
# promises never happens.
for tool in cc gcc clang rustc cargo make git python3 node-gyp; do
if command -v "$tool" >/dev/null 2>&1; then
echo "this image has $tool on it, so it is not the machine this job is about"
exit 1
fi
done
node tools/install.mjs
'
# The gate is validated the only way a gate can be: the failure it
# exists to catch has to fail it. The binary taken back out is what
# a platform package that did not build, did not upload or did not
# install looks like from in here. It is also the check that this
# job is running the program at all, since a bind mount pointing at
# nothing and a container whose exit code went nowhere both look
# exactly like success.
- name: The failure the job is meant to catch, caught
run: |
mv npm/${{ matrix.dir }}/zudb.${{ matrix.dir }}.node /tmp/
set +e
docker run --rm -v "$PWD":/work -w /work \
-e npm_config_offline=true \
${{ matrix.image }} node tools/install.mjs
rc=$?
set -e
mv /tmp/zudb.${{ matrix.dir }}.node npm/${{ matrix.dir }}/
test $rc -ne 0 || { echo "the install passed with no binary in the platform package"; exit 1; }