Repository navigation
369 lines (368 loc) · 18.4 KB
/
Copy pathrelease.yml
File metadata and controls
369 lines (368 loc) · 18.4 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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
name: Native Linux release
on:
push:
tags: ['v*']
concurrency:
group: native-release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
jobs:
# Owner provisioning is intentionally required. No environment, key, or
# reviewed tooling commit is fabricated by this workflow.
configuration:
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
tooling: ${{ steps.validate.outputs.tooling }}
environment: ${{ steps.validate.outputs.environment }}
steps:
- id: validate
name: Require approved signing configuration and repository identity
env:
SIGNING_ENVIRONMENT: ${{ vars.NATIVE_RELEASE_SIGNING_ENVIRONMENT }}
TOOLING_COMMIT: ${{ vars.NATIVE_RELEASE_TOOLING_COMMIT }}
REPOSITORY_ID: ${{ github.repository_id }}
OWNER_ID: ${{ github.repository_owner_id }}
run: |
node --input-type=module <<'NODE'
import assert from 'node:assert/strict';
import { appendFileSync } from 'node:fs';
const env = process.env;
assert.equal(env.GITHUB_REPOSITORY, 'ShadowfetchStudio/ShadowCode');
assert.equal(env.REPOSITORY_ID, '1377099349');
assert.equal(env.OWNER_ID, '209457103');
assert.equal(env.GITHUB_EVENT_NAME, 'push');
assert.equal(env.GITHUB_REF_TYPE, 'tag');
assert.match(env.GITHUB_REF_NAME, /^v(0|[1-9][0-9]{0,8})\.(0|[1-9][0-9]{0,8})\.(0|[1-9][0-9]{0,8})$/);
assert.match(env.TOOLING_COMMIT || '', /^[a-f0-9]{40}$/, 'Provision a reviewed immutable tooling commit');
assert.match(env.SIGNING_ENVIRONMENT || '', /^[A-Za-z0-9][A-Za-z0-9 _.-]{0,254}$/, 'Provision an existing protected signing environment');
appendFileSync(env.GITHUB_OUTPUT, `tooling=${env.TOOLING_COMMIT}\nenvironment=${env.SIGNING_ENVIRONMENT}\n`);
NODE
build:
needs: configuration
runs-on: ubuntu-24.04
timeout-minutes: 150
env:
RUSTUP_TOOLCHAIN: '1.95.0'
SHADOWCODE_BUILD_JOBS: '4'
# The Debian package must pass lintian (errors fail; warnings are reported).
SHADOWCODE_REQUIRE_LINTIAN: '1'
outputs:
artifact-id: ${{ steps.unsigned.outputs.artifact-id }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
cache: npm
cache-dependency-path: ui/package-lock.json
- name: Native package dependencies
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev libayatana-appindicator3-dev libasound2-dev patchelf webkit2gtk-driver xvfb appstream dbus-daemon dbus-bin lintian desktop-file-utils
- name: Test release staging and immutable retry behavior
run: node scripts/native-release-verification.mjs run release-tests
- name: Verify publisher authentication and private release snapshots
run: node scripts/native-release-verification.mjs run release-auth
- name: Validate native release tag
run: node scripts/native-release-verification.mjs run release-tag
- name: Managed llama.cpp build dependencies
run: sudo apt-get install -y cmake ninja-build libssl-dev libvulkan-dev glslc spirv-headers
- name: Install the pinned Rust toolchain
run: rustup toolchain install 1.95.0 --profile minimal --component rustfmt --component clippy
- name: Build and test the embedded interface
run: node scripts/native-release-verification.mjs run interface
- name: Verify the exact GLib backport and optimized iterator behavior
run: node scripts/native-release-verification.mjs run glib-backport
- name: Verify native source
run: node scripts/native-release-verification.mjs run native-source
- name: Qualify cleanup over actual built UI dependencies and output
run: node scripts/native-release-verification.mjs run built-project-cleanup
- name: Preserve built-project cleanup diagnostics
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: built-project-cleanup-diagnostics
path: artifacts/built-project-cleanup
- name: Exercise native application behavior
run: node scripts/native-release-verification.mjs run native-behavior
- name: Exercise the native desktop window
run: node scripts/native-release-verification.mjs run native-window
- name: Qualify 100 sequential tasks through the native window
run: node scripts/native-release-verification.mjs run native-endurance
- name: Preserve native window and Markdown worker diagnostics
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: native-window-diagnostics
path: |
artifacts/native
artifacts/native-markdown
artifacts/native-endurance
artifacts/native-x11
- name: Build the managed llama.cpp runtime (Vulkan + all CPU variants)
run: node scripts/native-release-verification.mjs run managed-runtime
- name: Build and inspect release packages
run: node scripts/native-release-verification.mjs run packages
- name: Install and first-launch both release packages in an offline clean host
run: node scripts/native-release-verification.mjs run clean-host-packages
- name: Exercise the packaged application
run: node scripts/native-release-verification.mjs run packaged-behavior
- name: Preserve final X11 session diagnostics
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: native-x11-session-diagnostics
path: artifacts/native-x11
- name: Create release checksums
run: cp artifacts/native-package/SHA256SUMS target/release/bundle/SHA256SUMS
- name: Verify installer replacement and checksum refusal
run: node scripts/native-release-verification.mjs run installer
- name: Preserve exact release verification receipts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: native-release-verification
path: artifacts/release-verification
- name: Prepare immutable unsigned handoff with all required receipts
run: node scripts/prepare-native-release.mjs "$RUNNER_TEMP/unsigned-release"
- id: unsigned
name: Retain this run's exact unsigned handoff
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: unsigned-release-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/unsigned-release
if-no-files-found: error
compression-level: 0
# aarch64 packages are built and qualified natively on GitHub's Arm runner
# with the same required gates and handed off unsigned exactly like x86_64.
# The signer authenticates each target's bundle; its release metadata carries
# an -aarch64 suffix so both targets publish into one release.
build-aarch64:
needs: configuration
runs-on: ubuntu-24.04-arm
timeout-minutes: 180
env:
RUSTUP_TOOLCHAIN: '1.95.0'
SHADOWCODE_BUILD_JOBS: '4'
SHADOWCODE_REQUIRE_LINTIAN: '1'
outputs:
artifact-id: ${{ steps.unsigned.outputs.artifact-id }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
cache: npm
cache-dependency-path: ui/package-lock.json
- name: Native package dependencies
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev libayatana-appindicator3-dev libasound2-dev patchelf webkit2gtk-driver xvfb appstream dbus-daemon dbus-bin lintian desktop-file-utils xdg-utils cmake ninja-build libssl-dev libvulkan-dev glslc spirv-headers gcc-14 g++-14
- name: Install the pinned Rust toolchain
run: rustup toolchain install 1.95.0 --profile minimal --component rustfmt --component clippy
- name: Run every required release gate in order on aarch64
run: |
set -euo pipefail
gates=$(node --input-type=module -e "const { REQUIRED_GATES } = await import('./scripts/native-release-verification.mjs'); console.log(REQUIRED_GATES.join(' '))")
for gate in $gates; do
# The installer gate verifies the checksums the packages gate wrote.
if [ "$gate" = installer ]; then cp artifacts/native-package/SHA256SUMS target/release/bundle/SHA256SUMS; fi
node scripts/native-release-verification.mjs run "$gate"
done
- name: Preserve aarch64 qualification diagnostics
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: native-release-aarch64-diagnostics
path: |
artifacts/release-verification
artifacts/built-project-cleanup
artifacts/native
artifacts/native-markdown
artifacts/native-endurance
artifacts/native-x11
artifacts/native-package
- name: Prepare immutable unsigned aarch64 handoff with all required receipts
run: node scripts/prepare-native-release.mjs "$RUNNER_TEMP/unsigned-release"
- id: unsigned
name: Retain this run's exact unsigned aarch64 handoff
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: unsigned-release-aarch64-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/unsigned-release
if-no-files-found: error
compression-level: 0
sign:
needs: [configuration, build, build-aarch64]
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
actions: read
environment: ${{ needs.configuration.outputs.environment }}
outputs:
artifact-id: ${{ steps.signed.outputs.artifact-id }}
steps:
- name: Checkout independently approved signing tooling and public trust
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.configuration.outputs.tooling }}
path: trusted
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
- name: Require one build artifact identity per target
env:
RELEASE_ARTIFACT_ID: ${{ needs.build.outputs.artifact-id }}
AARCH64_ARTIFACT_ID: ${{ needs.build-aarch64.outputs.artifact-id }}
run: node -e "const assert = require('node:assert/strict'); for (const id of [process.env.RELEASE_ARTIFACT_ID, process.env.AARCH64_ARTIFACT_ID]) assert.match(id || '', /^[1-9][0-9]*$/); assert.notEqual(process.env.RELEASE_ARTIFACT_ID, process.env.AARCH64_ARTIFACT_ID)"
- name: Download only the immutable artifact ID produced by this build
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}
path: ${{ runner.temp }}/unsigned-download
merge-multiple: true
- name: Download only the immutable artifact ID produced by the aarch64 build
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
artifact-ids: ${{ needs.build-aarch64.outputs.artifact-id }}
path: ${{ runner.temp }}/unsigned-download-aarch64
merge-multiple: true
- name: Sign and reverify each target's qualified data with the protected key
working-directory: trusted
env:
NATIVE_RELEASE_SIGNING_KEY_PEM: ${{ secrets.NATIVE_RELEASE_SIGNING_KEY_PEM }}
run: |
mkdir -m 700 "$RUNNER_TEMP/signed-release"
node scripts/sign-native-release.mjs "$RUNNER_TEMP/unsigned-download" release/trust "$RUNNER_TEMP/signed-release/x86_64"
node scripts/sign-native-release.mjs "$RUNNER_TEMP/unsigned-download-aarch64" release/trust "$RUNNER_TEMP/signed-release/aarch64" aarch64-unknown-linux-gnu
- id: signed
name: Retain only authenticated public release data
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: signed-release-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/signed-release
if-no-files-found: error
compression-level: 0
publish:
needs: [configuration, build, sign]
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: write
# actions: write starts the macOS and Flatpak workflows after publishing.
actions: write
steps:
- name: Checkout independently approved publication tooling and public trust
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.configuration.outputs.tooling }}
path: trusted
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
- name: Require one signed artifact identity
env:
RELEASE_ARTIFACT_ID: ${{ needs.sign.outputs.artifact-id }}
run: node -e "require('node:assert/strict').match(process.env.RELEASE_ARTIFACT_ID || '', /^[1-9][0-9]*$/)"
- name: Download only this signing job's immutable public bundle
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
artifact-ids: ${{ needs.sign.outputs.artifact-id }}
path: ${{ runner.temp }}/signed-download
merge-multiple: true
- name: Authenticate private snapshots before any release operation
working-directory: trusted
env:
GH_TOKEN: ${{ github.token }}
run: node scripts/publish-native-release.mjs "$RUNNER_TEMP/signed-download/x86_64" release/trust "$RUNNER_TEMP/signed-download/aarch64"
# A release this job publishes with GITHUB_TOKEN does not trigger other
# workflows' `release: published`; a dispatch does. They build from the
# tag and attach the macOS images and Flatpak bundles.
- name: Start the macOS and Flatpak builds for this release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh workflow run release-macos.yml --repo "$GITHUB_REPOSITORY" --ref "$GITHUB_REF_NAME" -f tag="$GITHUB_REF_NAME"
gh workflow run release-flatpak.yml --repo "$GITHUB_REPOSITORY" --ref "$GITHUB_REF_NAME" -f tag="$GITHUB_REF_NAME"
# Signed APT repository on GitHub Pages (gh-pages branch, path apt/). Only
# the .deb that passed publisher authentication is indexed; the repository
# signing key is a separate OpenPGP key held in the protected environment.
apt-repository:
needs: [configuration, sign, publish]
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: write
environment: ${{ needs.configuration.outputs.environment }}
steps:
- name: Checkout independently approved tooling and public trust
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.configuration.outputs.tooling }}
path: trusted
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
- name: Require a pinned APT signing fingerprint
env:
APT_FPR: ${{ vars.NATIVE_APT_SIGNING_FINGERPRINT }}
run: node -e "require('node:assert/strict').match(process.env.APT_FPR || '', /^[0-9A-F]{40}$/, 'Provision NATIVE_APT_SIGNING_FINGERPRINT')"
- name: Download only this signing job's immutable public bundle
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
artifact-ids: ${{ needs.sign.outputs.artifact-id }}
path: ${{ runner.temp }}/signed-download
merge-multiple: true
- name: Authenticate each target's .deb against the tracked trust bundle
working-directory: trusted
run: |
version="${GITHUB_REF_NAME#v}"
mkdir -m 700 "$RUNNER_TEMP/verified-deb"
for pair in x86_64:amd64 aarch64:arm64; do
bash scripts/verify-native-release.sh \
--bundle-dir "$RUNNER_TEMP/signed-download/${pair%%:*}" \
--trust-dir release/trust \
--artifact "ShadowCode_${version}_${pair#*:}.deb" \
--stage-dir "$RUNNER_TEMP/verified-deb/${pair#*:}" \
--expect-version "$version"
done
- name: Checkout the published Pages branch
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
continue-on-error: true
id: pages
with:
ref: gh-pages
path: pages
- name: Build and sign the repository, keeping earlier pool packages
working-directory: trusted
env:
APT_REPO_SIGNING_KEY: ${{ secrets.NATIVE_APT_SIGNING_KEY }}
APT_FPR: ${{ vars.NATIVE_APT_SIGNING_FINGERPRINT }}
run: |
previous=()
[ -d "$GITHUB_WORKSPACE/pages/apt" ] && previous=(--previous "$GITHUB_WORKSPACE/pages/apt")
node scripts/build-apt-repo.mjs --out "$RUNNER_TEMP/apt" --fingerprint "$APT_FPR" "${previous[@]}" "$RUNNER_TEMP"/verified-deb/*/*.deb
gpgv --keyring "$RUNNER_TEMP/apt/shadowcode-archive-keyring.gpg" "$RUNNER_TEMP/apt/dists/stable/InRelease"
- name: Publish to gh-pages
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${{ steps.pages.outcome }}" != success ]; then
rm -rf pages && mkdir pages && cd pages
git init -q -b gh-pages
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
touch .nojekyll
else
cd pages
fi
rm -rf apt && cp -a "$RUNNER_TEMP/apt" apt
git add -A
git -c user.name=github-actions -c user.email=github-actions@users.noreply.github.com \
commit -q -m "APT repository for ${GITHUB_REF_NAME}"
git push origin HEAD:gh-pages