Repository navigation
789 lines (746 loc) · 35.9 KB
/
Copy pathrelease-rehearsal.yml
File metadata and controls
789 lines (746 loc) · 35.9 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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
name: Rehearse RegistryStack Release
run-name: >-
${{ inputs.advisory_evidence &&
format('Collect advisory evidence v{0} ({1})', inputs.version, inputs.request_id) ||
format('Rehearse {0} {1} v{2} ({3})', inputs.unit, inputs.release_id, inputs.version, inputs.request_id) }}
on:
workflow_dispatch:
inputs:
version:
description: Canonical semantic version without the v prefix
required: true
type: string
unit:
description: Release unit to rehearse or collect evidence for
required: true
default: stack
type: choice
options:
- stack
- discovery
release_id:
description: Prepared release train identity for an ordinary rehearsal; omit for advisory evidence
required: false
default: ""
type: string
request_id:
description: Unique operator-generated identity used to correlate this rehearsal
required: true
type: string
advisory_evidence:
description: Collect review-only image advisory evidence
required: false
default: false
type: boolean
permissions:
contents: read
jobs:
validate:
name: Validate release image onboarding before builds
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Validate rehearsal mode and branch
env:
REHEARSAL_ADVISORY_EVIDENCE: ${{ inputs.advisory_evidence }}
REHEARSAL_RELEASE_ID: ${{ inputs.release_id }}
REHEARSAL_UNIT: ${{ inputs.unit }}
shell: bash
run: |
set -euo pipefail
if [[ "${GITHUB_REF}" != refs/heads/* ]]; then
echo "release rehearsal and advisory evidence collection require a branch ref" >&2
exit 1
fi
case "${REHEARSAL_UNIT}" in
stack) ;;
discovery) ;;
*) echo "unit must be stack or discovery" >&2; exit 2 ;;
esac
case "${REHEARSAL_ADVISORY_EVIDENCE}" in
true)
if [[ "${REHEARSAL_UNIT}" != stack ]]; then
echo "Discovery rehearsal does not collect the Stack advisory evidence set" >&2
exit 2
fi
if [[ -n "${REHEARSAL_RELEASE_ID}" ]]; then
echo "advisory evidence collection does not accept release_id" >&2
exit 2
fi
;;
false)
if [[ -z "${REHEARSAL_RELEASE_ID}" ]]; then
echo "ordinary release rehearsal requires release_id" >&2
exit 2
fi
;;
*) echo "advisory_evidence must be a typed boolean" >&2; exit 2 ;;
esac
- name: Checkout dispatched branch
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
submodules: false
- name: Check complete release image onboarding
env:
REHEARSAL_ADVISORY_EVIDENCE: ${{ inputs.advisory_evidence }}
REHEARSAL_UNIT: ${{ inputs.unit }}
REHEARSAL_VERSION: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
onboarding=(
python3 release/scripts/release_candidate.py check-image-onboarding
--version "${REHEARSAL_VERSION}"
--unit "${REHEARSAL_UNIT}"
--root .
)
case "${REHEARSAL_ADVISORY_EVIDENCE}" in
true) onboarding+=(--allow-missing-baseline) ;;
false) ;;
*) echo "advisory_evidence must be a typed boolean" >&2; exit 2 ;;
esac
"${onboarding[@]}"
rehearse:
name: Exercise future-tag release paths on Ubuntu
if: ${{ !inputs.advisory_evidence }}
needs: validate
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Checkout prepared branch
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
submodules: false
- name: Refresh protected-main comparison ref
run: >-
git fetch --no-tags --force origin
refs/heads/main:refs/remotes/origin/main
- name: Require reviewed image baselines for release rehearsal
env:
REHEARSAL_UNIT: ${{ inputs.unit }}
REHEARSAL_VERSION: ${{ inputs.version }}
run: |
python3 release/scripts/release_candidate.py check-image-onboarding \
--version "${REHEARSAL_VERSION}" \
--unit "${REHEARSAL_UNIT}" \
--root .
- name: Setup Node
if: ${{ inputs.unit == 'stack' }}
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24.11.0
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Install exact docs dependencies
if: ${{ inputs.unit == 'stack' }}
working-directory: docs/site
# The rehearsal runs no Vale check, and Vale is the only package with
# an install script this job would use. Its postinstall fetches through
# the unauthenticated GitHub API, which refuses shared runner addresses
# once their hourly limit is spent, so install scripts are skipped.
run: npm ci --ignore-scripts
- name: Install uv
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
version: "0.11.16"
- name: Rehearse prepared release without publishing
env:
REHEARSAL_VERSION: ${{ inputs.version }}
REHEARSAL_RELEASE_ID: ${{ inputs.release_id }}
REHEARSAL_UNIT: ${{ inputs.unit }}
run: >-
uv run --no-project --with PyYAML==6.0.2 release/scripts/rehearse-release
--version "${REHEARSAL_VERSION}"
--release-id "${REHEARSAL_RELEASE_ID}"
--unit "${REHEARSAL_UNIT}"
--base-ref origin/main
canonical-linux-binaries:
name: Build canonical Linux ${{ matrix.group }} binary shard
needs: validate
runs-on: ubuntu-24.04
timeout-minutes: 90
permissions:
actions: read
contents: read
env:
RELEASE_BUILDER_IMAGE: rust:1.95-trixie@sha256:f49565f188ee00bc2a18dd418183f2c5f23ef7d6e691890517ed341a598f67c3
strategy:
fail-fast: false
matrix:
group: ${{ inputs.unit == 'discovery' && fromJSON('["discovery"]') || fromJSON('["core","breg","casework","scheduling","messaging"]') }}
steps:
- name: Checkout exact dispatched source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
fetch-depth: 1
persist-credentials: false
submodules: false
- name: Restore reusable Cargo cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
.cargo-home/registry
.cargo-home/git
target
key: registry-stack-release-${{ runner.os }}-${{ matrix.group }}-${{ hashFiles('rust-toolchain.toml', 'release/scripts/build-release-binaries.sh', 'release/docker/Dockerfile.builder', 'release/requirements/ziglang-0.12.1.txt', 'release/glibc-floor.env', 'release/scripts/zig-glibc-compiler', 'release/scripts/zig-glibc-toolchain.sh') }}-${{ hashFiles('Cargo.lock') }}
restore-keys: |
registry-stack-release-${{ runner.os }}-${{ matrix.group }}-${{ hashFiles('rust-toolchain.toml', 'release/scripts/build-release-binaries.sh', 'release/docker/Dockerfile.builder', 'release/requirements/ziglang-0.12.1.txt', 'release/glibc-floor.env', 'release/scripts/zig-glibc-compiler', 'release/scripts/zig-glibc-toolchain.sh') }}-
- name: Build canonical Linux binary shard
env:
RELEASE_SOURCE_SHA: ${{ github.sha }}
REHEARSAL_VERSION: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
release/scripts/build-release-binaries.sh \
--group "${{ matrix.group }}" \
"${REHEARSAL_VERSION}"
- name: Stage canonical Linux binary shard
run: |
set -euo pipefail
mkdir binary-shard
cp -R dist/bin binary-shard/bin
cp dist/RELEASE_BINARY_SHARD dist/RELEASE_BUILDER_IMAGE binary-shard/
- name: Upload canonical Linux binary shard
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-rehearsal-binaries-${{ matrix.group }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
path: binary-shard
if-no-files-found: error
retention-days: 2
canonical-linux:
name: Prove the canonical Linux payload
needs:
- validate
- canonical-linux-binaries
runs-on: ubuntu-24.04
timeout-minutes: 90
permissions:
actions: read
contents: read
env:
RELEASE_BUILDER_IMAGE: rust:1.95-trixie@sha256:f49565f188ee00bc2a18dd418183f2c5f23ef7d6e691890517ed341a598f67c3
RELEASE_BUILDX_VERSION: v0.33.0
RELEASE_BUILDKIT_IMAGE: ghcr.io/registrystack/buildkit:v0.31.2@sha256:2f5adac4ecd194d9f8c10b7b5d7bceb5186853db1b26e5abd3a657af0b7e26ec
REHEARSAL_REGISTRY_IMAGE: registry:3.1.1@sha256:1be55279f18a2fe1a74edf2664cac61c1bea305b7b4642dab412e7affdcb3e33
SYFT_VERSION: v1.45.1
SYFT_LINUX_AMD64_SHA256: 20c84195e24927f50a3b2269946be51f4c4abc9d2f145fee7388b4199149f716
GRYPE_VERSION: v0.114.0
GRYPE_LINUX_AMD64_SHA256: edda0968d8827daab01d32b3cd7de192ae0915005e7bbfcfef9e68e79bc43343
CRANE_VERSION: v0.21.2
CRANE_LINUX_AMD64_SHA256: 897e7c342db072ba76531246fc18fbf3e8e298688b6ecf98916770984b263866
ORAS_VERSION: v1.3.2
ORAS_LINUX_AMD64_SHA256: 9229ccc6d17bb282039ad4a69abb16dcb887a5bce567c075d731d9b3c7ad8eaf
steps:
- name: Checkout exact dispatched source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
fetch-depth: 1
persist-credentials: false
submodules: false
- name: Download canonical Linux core binary shard
if: ${{ inputs.unit == 'stack' }}
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-rehearsal-binaries-core-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
path: binary-shards/core
- name: Download canonical Linux breg binary shard
if: ${{ inputs.unit == 'stack' }}
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-rehearsal-binaries-breg-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
path: binary-shards/breg
- name: Download canonical Linux Casework binary shard
if: ${{ inputs.unit == 'stack' }}
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-rehearsal-binaries-casework-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
path: binary-shards/casework
- name: Download canonical Linux Scheduling binary shard
if: ${{ inputs.unit == 'stack' }}
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-rehearsal-binaries-scheduling-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
path: binary-shards/scheduling
- name: Download canonical Linux Messaging binary shard
if: ${{ inputs.unit == 'stack' }}
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-rehearsal-binaries-messaging-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
path: binary-shards/messaging
- name: Download canonical Discovery binary shard
if: ${{ inputs.unit == 'discovery' }}
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: release-rehearsal-binaries-discovery-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
path: binary-shards/discovery
- name: Merge and smoke the Discovery payload
if: ${{ inputs.unit == 'discovery' }}
env:
REHEARSAL_VERSION: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
python3 release/scripts/unit_release_pipeline.py merge-shard \
--unit discovery \
--version "${REHEARSAL_VERSION}" \
--source-sha "${{ github.sha }}" \
--shard binary-shards/discovery \
--output dist \
--builder-image "${RELEASE_BUILDER_IMAGE}"
for binary in discovery discoveryctl; do
test "$("dist/bin/${binary}-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"${binary} ${REHEARSAL_VERSION}"
done
- name: Stage and smoke the Discovery installer pair
if: ${{ inputs.unit == 'discovery' }}
env:
REHEARSAL_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
python3 release/scripts/unit_release_pipeline.py stage-payload \
--unit discovery \
--version "${REHEARSAL_VERSION}" \
--binaries dist/bin \
--output discovery-payload
- name: Merge and smoke the canonical Linux payload
if: ${{ inputs.unit == 'stack' }}
env:
REHEARSAL_VERSION: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
release/scripts/merge-release-binary-shards.py \
--version "${REHEARSAL_VERSION}" \
--source-sha "${{ github.sha }}" \
--core binary-shards/core \
--breg binary-shards/breg \
--casework binary-shards/casework \
--scheduling binary-shards/scheduling \
--messaging binary-shards/messaging \
--output dist \
--builder-image "${RELEASE_BUILDER_IMAGE}"
test "$("dist/bin/breg-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"breg ${REHEARSAL_VERSION}"
test "$("dist/bin/bregctl-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"bregctl ${REHEARSAL_VERSION}"
IFS=. read -r release_major release_minor _release_patch \
<<<"${REHEARSAL_VERSION}"
discoveryctl_in_release="$(python3 release/scripts/release_roster.py \
discoveryctl-in-release "${REHEARSAL_VERSION}")"
if [[ "${discoveryctl_in_release}" == true ]]; then
test "$("dist/bin/discoveryctl-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"discoveryctl ${REHEARSAL_VERSION}"
fi
scheduling_binary_in_release="$(python3 release/scripts/release_roster.py \
scheduling-binary-in-release "${REHEARSAL_VERSION}")"
if [[ "${scheduling_binary_in_release}" == true ]]; then
test "$("dist/bin/scheduling-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"scheduling ${REHEARSAL_VERSION}"
fi
breg_services_in_release="$(python3 release/scripts/release_roster.py \
breg-services-in-release "${REHEARSAL_VERSION}")"
if [[ "${breg_services_in_release}" == true ]]; then
for breg_service in breg-mcp breg-review; do
test "$("dist/bin/${breg_service}-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"${breg_service} ${REHEARSAL_VERSION}"
test "$("dist/image-bin/${breg_service}" --version)" = \
"${breg_service} ${REHEARSAL_VERSION}"
done
fi
if (( release_major > 0 || release_minor >= 30 )); then
test "$("dist/bin/casework-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"casework ${REHEARSAL_VERSION}"
test "$("dist/bin/caseworkctl-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"caseworkctl ${REHEARSAL_VERSION}"
fi
if (( release_major > 0 || release_minor >= 33 )); then
test "$(dist/image-bin/scheduling --version)" = \
"scheduling ${REHEARSAL_VERSION}"
fi
if (( release_major > 0 || release_minor >= 36 )); then
test "$("dist/bin/schedulingctl-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"schedulingctl ${REHEARSAL_VERSION}"
for operator_tool in bregctl caseworkctl schedulingctl; do
test "$("dist/image-bin/${operator_tool}" --version)" = \
"${operator_tool} ${REHEARSAL_VERSION}"
done
fi
messaging_in_release="$(python3 release/scripts/release_roster.py \
messaging-in-release "${REHEARSAL_VERSION}")"
if [[ "${messaging_in_release}" == true ]]; then
test "$("dist/bin/messaging-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"messaging ${REHEARSAL_VERSION}"
test "$("dist/bin/messagingctl-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"messagingctl ${REHEARSAL_VERSION}"
test "$(dist/image-bin/messagingctl --version)" = \
"messagingctl ${REHEARSAL_VERSION}"
fi
coordinator_in_release="$(python3 release/scripts/release_roster.py \
coordinator-in-release "${REHEARSAL_VERSION}")"
if [[ "${coordinator_in_release}" == true ]]; then
test "$("dist/bin/coordinator-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"coordinator ${REHEARSAL_VERSION}"
test "$("dist/bin/coordinatorctl-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"coordinatorctl ${REHEARSAL_VERSION}"
test "$(dist/image-bin/coordinator --version)" = \
"coordinator ${REHEARSAL_VERSION}"
test "$(dist/image-bin/coordinatorctl --version)" = \
"coordinatorctl ${REHEARSAL_VERSION}"
coordinatorctl_binary="${GITHUB_WORKSPACE}/dist/bin/coordinatorctl-v${REHEARSAL_VERSION}-linux-amd64"
coordinator_project="${RUNNER_TEMP}/release-coordinator-project"
"${coordinatorctl_binary}" init --directory "${coordinator_project}"
"${coordinatorctl_binary}" check --project "${coordinator_project}"
fi
adapter_in_release="$(python3 release/scripts/release_roster.py \
adapter-in-release "${REHEARSAL_VERSION}")"
if [[ "${adapter_in_release}" == true ]]; then
for adapter_binary in adapter adapterctl; do
test "$("dist/bin/${adapter_binary}-v${REHEARSAL_VERSION}-linux-amd64" --version)" = \
"${adapter_binary} ${REHEARSAL_VERSION}"
done
"dist/bin/adapterctl-v${REHEARSAL_VERSION}-linux-amd64" check products/adapter/examples/dhis2-tracker/project.yaml \
--profile products/adapter/profiles/breg-records/profile.yaml \
--pack products/adapter/packs/dhis2-tracker/pack.yaml >/dev/null
fi
render_in_release="$(python3 release/scripts/release_roster.py \
render-in-release "${REHEARSAL_VERSION}")"
if [[ "${render_in_release}" == true ]]; then
render_binary="${GITHUB_WORKSPACE}/dist/bin/registry-render-v${REHEARSAL_VERSION}-linux-amd64"
typst_pin="$(sed -n 's/^pub const TYPST_PIN: &str = "\([^" ]*\)";$/\1/p' crates/registry-render/src/lib.rs)"
test -n "${typst_pin}"
expected_render_version="registry-render ${REHEARSAL_VERSION} (typst ${typst_pin})"
test "$("${render_binary}" --version)" = "${expected_render_version}"
test "$(dist/image-bin/registry-render --version)" = "${expected_render_version}"
render_project="${RUNNER_TEMP}/release-render-project"
render_package="${RUNNER_TEMP}/release-render-package"
"${render_binary}" init "${render_project}"
"${render_binary}" check --bundle "${render_project}"
"${render_binary}" package --bundle "${render_project}" --output "${render_package}"
"${render_binary}" compile --bundle "${render_package}" --type letter \
--data "${render_project}/fixtures/data.json" --issued-at 2026-01-01T00:00:00Z \
--strict --output "${RUNNER_TEMP}/release-render-letter.pdf"
test -s "${RUNNER_TEMP}/release-render-letter.pdf"
fi
oid4vci_image_in_release="$(python3 release/scripts/release_roster.py \
evidence-oid4vci-image-in-release "${REHEARSAL_VERSION}")"
if [[ "${oid4vci_image_in_release}" == true ]]; then
test "$(dist/image-bin/evidence-oid4vci --version)" = \
"evidence-oid4vci ${REHEARSAL_VERSION}"
fi
- name: Set up Docker Buildx for canonical image or advisory evidence
if: ${{ inputs.unit == 'discovery' || inputs.advisory_evidence }}
id: advisory-buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
with:
version: ${{ env.RELEASE_BUILDX_VERSION }}
driver-opts: image=${{ env.RELEASE_BUILDKIT_IMAGE }}
- name: Build the canonical Discovery image
if: ${{ inputs.unit == 'discovery' }}
env:
REHEARSAL_VERSION: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
mkdir -p dist/images
layout="${GITHUB_WORKSPACE}/dist/images/discovery.oci"
metadata="dist/images/discovery.metadata.json"
RELEASE_BUILDX_BUILDER="${{ steps.advisory-buildx.outputs.name }}" \
RELEASE_IMAGE_OCI_LAYOUT="${layout}" \
release/scripts/build-release-image.sh \
discovery \
"registry.invalid/registry-discovery-rehearsal:v${REHEARSAL_VERSION}" \
"https://github.com/${GITHUB_REPOSITORY}" \
"${{ github.sha }}" \
"${REHEARSAL_VERSION}" \
"${metadata}"
test -d "${layout}"
jq -e '."containerimage.digest" | test("^sha256:[0-9a-f]{64}$")' \
"${metadata}"
- name: Install pinned advisory evidence tools
if: ${{ inputs.unit == 'stack' && inputs.advisory_evidence }}
env:
STEP_CRANE_SHA256: ${{ env.CRANE_LINUX_AMD64_SHA256 }}
STEP_CRANE_VERSION: ${{ env.CRANE_VERSION }}
STEP_GRYPE_SHA256: ${{ env.GRYPE_LINUX_AMD64_SHA256 }}
STEP_GRYPE_VERSION: ${{ env.GRYPE_VERSION }}
STEP_ORAS_SHA256: ${{ env.ORAS_LINUX_AMD64_SHA256 }}
STEP_ORAS_VERSION: ${{ env.ORAS_VERSION }}
STEP_SYFT_SHA256: ${{ env.SYFT_LINUX_AMD64_SHA256 }}
STEP_SYFT_VERSION: ${{ env.SYFT_VERSION }}
shell: bash
run: |
set -euo pipefail
mkdir -p "${HOME}/.local/bin" "${RUNNER_TEMP}/tools"
syft_version="${STEP_SYFT_VERSION#v}"
grype_version="${STEP_GRYPE_VERSION#v}"
oras_version="${STEP_ORAS_VERSION#v}"
curl -sSfL \
"https://github.com/anchore/syft/releases/download/${STEP_SYFT_VERSION}/syft_${syft_version}_linux_amd64.tar.gz" \
-o "${RUNNER_TEMP}/tools/syft.tar.gz"
echo "${STEP_SYFT_SHA256} ${RUNNER_TEMP}/tools/syft.tar.gz" \
| sha256sum --check --strict
tar -xzf "${RUNNER_TEMP}/tools/syft.tar.gz" \
-C "${HOME}/.local/bin" syft
curl -sSfL \
"https://github.com/anchore/grype/releases/download/${STEP_GRYPE_VERSION}/grype_${grype_version}_linux_amd64.tar.gz" \
-o "${RUNNER_TEMP}/tools/grype.tar.gz"
echo "${STEP_GRYPE_SHA256} ${RUNNER_TEMP}/tools/grype.tar.gz" \
| sha256sum --check --strict
tar -xzf "${RUNNER_TEMP}/tools/grype.tar.gz" \
-C "${HOME}/.local/bin" grype
curl -sSfL \
"https://github.com/google/go-containerregistry/releases/download/${STEP_CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" \
-o "${RUNNER_TEMP}/tools/crane.tar.gz"
echo "${STEP_CRANE_SHA256} ${RUNNER_TEMP}/tools/crane.tar.gz" \
| sha256sum --check --strict
tar -xzf "${RUNNER_TEMP}/tools/crane.tar.gz" \
-C "${HOME}/.local/bin" crane
curl -sSfL \
"https://github.com/oras-project/oras/releases/download/${STEP_ORAS_VERSION}/oras_${oras_version}_linux_amd64.tar.gz" \
-o "${RUNNER_TEMP}/tools/oras.tar.gz"
echo "${STEP_ORAS_SHA256} ${RUNNER_TEMP}/tools/oras.tar.gz" \
| sha256sum --check --strict
tar -xzf "${RUNNER_TEMP}/tools/oras.tar.gz" \
-C "${HOME}/.local/bin" oras
echo "${HOME}/.local/bin" >> "${GITHUB_PATH}"
- name: Collect exact advisory review evidence
if: ${{ inputs.unit == 'stack' && inputs.advisory_evidence }}
env:
REHEARSAL_VERSION: ${{ inputs.version }}
REHEARSAL_SOURCE: https://github.com/${{ github.repository }}
REHEARSAL_REVISION: ${{ github.sha }}
REHEARSAL_BUILDX_BUILDER: ${{ steps.advisory-buildx.outputs.name }}
shell: bash
run: |
set -euo pipefail
release/scripts/collect-rehearsal-advisory-evidence.py \
--version "${REHEARSAL_VERSION}" \
--source "${REHEARSAL_SOURCE}" \
--revision "${REHEARSAL_REVISION}" \
--buildx-builder "${REHEARSAL_BUILDX_BUILDER}" \
--registry-image "${REHEARSAL_REGISTRY_IMAGE}" \
--output "${RUNNER_TEMP}/release-advisory-evidence"
- name: Upload advisory review evidence
if: ${{ inputs.unit == 'stack' && inputs.advisory_evidence }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-advisory-evidence-${{ inputs.version }}-${{ inputs.request_id }}
path: ${{ runner.temp }}/release-advisory-evidence
if-no-files-found: error
retention-days: 8
node-clients:
name: Prove Linux Node clients for ${{ matrix.asset }}
if: ${{ inputs.unit == 'stack' && !inputs.advisory_evidence }}
needs: validate
runs-on: ${{ matrix.runner }}
timeout-minutes: 40
env:
CLIENT_VERSION: ${{ inputs.version }}
RUSTUP_TOOLCHAIN: "1.95.0"
NODE_RUNTIME_SMOKE_IMAGE: registry.access.redhat.com/ubi8/nodejs-24-minimal:1-1791463611@sha256:dc01310904907c73f7e5faddc0bc062e6d24561a01716bbc6701ba2072e18e08
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
asset: linux-amd64-glibc
target: x86_64-unknown-linux-gnu
napi_platform: linux-x64-gnu
- runner: ubuntu-24.04-arm
asset: linux-arm64-glibc
target: aarch64-unknown-linux-gnu
napi_platform: linux-arm64-gnu
steps:
- name: Checkout prepared branch
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
submodules: false
- name: Install Go for the AWS-LC-FIPS source build
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.2
cache: false
- name: Setup minimum Node runtime
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24.11.0
package-manager-cache: false
- name: Retain minimum Node runtime for package smokes
shell: bash
run: |
minimum_node_bin="$(command -v node)"
[[ "${minimum_node_bin}" == /* ]]
test "$("${minimum_node_bin}" --version)" = v24.11.0
printf 'MINIMUM_NODE_BIN=%s\n' "${minimum_node_bin}" >> "${GITHUB_ENV}"
- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24.21.0
cache: npm
cache-dependency-path: |
crates/registry-discovery-client-node/package-lock.json
crates/registry-evidence-client-node/package-lock.json
crates/registry-breg-client-node/package-lock.json
crates/registry-casework-client-node/package-lock.json
crates/registry-messaging-client-node/package-lock.json
crates/registry-scheduling-client-node/package-lock.json
crates/registry-coordinator-client-node/package-lock.json
crates/registry-stack-client-node/package-lock.json
- name: Install exact Zig toolchain
shell: bash
run: |
set -euo pipefail
rustup toolchain install 1.95.0 --profile minimal
python3 -m venv "${RUNNER_TEMP}/maturin"
"${RUNNER_TEMP}/maturin/bin/pip" install --quiet \
--require-hashes --only-binary=:all: \
--requirement "${GITHUB_WORKSPACE}/release/requirements/maturin-1.9.6.txt"
test "$("${RUNNER_TEMP}/maturin/bin/python" -m ziglang version)" = 0.12.1
- name: Build, package, and smoke Linux Node clients
shell: bash
run: |
set -euo pipefail
IFS=. read -r major minor patch <<<"${CLIENT_VERSION}"
unified_client=0
if (( major > 0 || minor > 26 || (minor == 26 && patch >= 1) )); then
unified_client=1
fi
clients=(discovery evidence)
if [[ "${unified_client}" -eq 1 ]]; then
clients+=(breg casework)
fi
messaging_in_release="$(python3 release/scripts/release_roster.py \
messaging-in-release "${CLIENT_VERSION}")"
if [[ "${messaging_in_release}" == true ]]; then
clients+=(messaging)
fi
scheduling_in_release="$(python3 release/scripts/release_roster.py \
scheduling-client-in-release "${CLIENT_VERSION}")"
if [[ "${scheduling_in_release}" == true ]]; then
clients+=(scheduling)
fi
coordinator_in_release="$(python3 release/scripts/release_roster.py \
coordinator-client-in-release "${CLIENT_VERSION}")"
if [[ "${coordinator_in_release}" == true ]]; then
clients+=(coordinator)
fi
for client in "${clients[@]}"; do
client_dir="${GITHUB_WORKSPACE}/crates/registry-${client}-client-node"
(cd "${client_dir}" && npm ci)
test "$(node -p "require('${client_dir}/package.json').version")" = \
"${CLIENT_VERSION}"
release/scripts/build-linux-node-client \
--client "${client}" \
--target "${{ matrix.target }}" \
--napi-platform "${{ matrix.napi_platform }}" \
--zig-python "${RUNNER_TEMP}/maturin/bin/python"
if [[ "${unified_client}" -eq 1 ]]; then
continue
fi
platform_dir="${client_dir}/npm/${{ matrix.napi_platform }}"
test -f "${platform_dir}/package.json"
cp "${client_dir}/LICENSE" "${platform_dir}/LICENSE"
cp "${client_dir}/${client}-client.${{ matrix.napi_platform }}.node" \
"${platform_dir}/"
(cd "${client_dir}" && npm pack \
"./npm/${{ matrix.napi_platform }}" \
--ignore-scripts --pack-destination "${RUNNER_TEMP}")
platform_package="${RUNNER_TEMP}/registrystack-${client}-client-${{ matrix.napi_platform }}-${CLIENT_VERSION}.tgz"
test -f "${platform_package}"
smoke="${RUNNER_TEMP}/node-rehearsal-${client}"
mkdir -p "${smoke}"
root_stage="${RUNNER_TEMP}/node-root-${client}"
mkdir -p "${root_stage}"
for source in \
LICENSE README.md client.js client.d.ts index.js index.d.ts package.json; do
cp "${client_dir}/${source}" "${root_stage}/${source}"
done
python3 release/scripts/client_registry.py bind-optional-deps \
--package-json "${root_stage}/package.json" \
--version "${CLIENT_VERSION}" \
--client "${client}"
(cd "${root_stage}" && npm pack \
--ignore-scripts --pack-destination "${smoke}")
root_package="${smoke}/registrystack-${client}-client-${CLIENT_VERSION}.tgz"
test -f "${root_package}"
(
cd "${smoke}"
npm init --yes >/dev/null
npm install --no-audit --no-fund --ignore-scripts \
"${root_package}" "${platform_package}"
test -z "$(find "node_modules/@registrystack/${client}-client" \
-maxdepth 1 -name '*.node' -print -quit)"
test -f \
"node_modules/@registrystack/${client}-client-${{ matrix.napi_platform }}/${client}-client.${{ matrix.napi_platform }}.node"
cp "${GITHUB_WORKSPACE}/release/scripts/smoke-${client}-client-package.js" .
node "smoke-${client}-client-package.js"
"${MINIMUM_NODE_BIN}" "smoke-${client}-client-package.js"
docker run --rm --network none \
--volume "${smoke}:${smoke}:ro" \
--workdir "${smoke}" \
"${NODE_RUNTIME_SMOKE_IMAGE}" \
node "smoke-${client}-client-package.js"
)
done
if [[ "${unified_client}" -eq 1 ]]; then
client_dir="${GITHUB_WORKSPACE}/crates/registry-stack-client-node"
platform_dir="${client_dir}/npm/${{ matrix.napi_platform }}"
for client in "${clients[@]}"; do
cp "${GITHUB_WORKSPACE}/crates/registry-${client}-client-node/${client}-client.${{ matrix.napi_platform }}.node" \
"${platform_dir}/"
done
smoke="${RUNNER_TEMP}/node-rehearsal-registry"
root_stage="${RUNNER_TEMP}/node-root-registry"
mkdir -p "${smoke}" "${root_stage}"
cp -R "${client_dir}/." "${root_stage}/"
python3 release/scripts/client_registry.py bind-optional-deps \
--package-json "${root_stage}/package.json" \
--version "${CLIENT_VERSION}" \
--client stack
(cd "${root_stage}" && npm pack \
"./npm/${{ matrix.napi_platform }}" \
--ignore-scripts --pack-destination "${RUNNER_TEMP}")
platform_package="${RUNNER_TEMP}/registrystack-client-${{ matrix.napi_platform }}-${CLIENT_VERSION}.tgz"
test -f "${platform_package}"
(cd "${root_stage}" && npm pack \
--ignore-scripts --pack-destination "${smoke}")
root_package="${smoke}/registrystack-client-${CLIENT_VERSION}.tgz"
test -f "${root_package}"
(
cd "${smoke}"
npm init --yes >/dev/null
npm install --no-audit --no-fund --ignore-scripts \
"${root_package}" "${platform_package}"
test -z "$(find node_modules/@registrystack/client \
-maxdepth 1 -name '*.node' -print -quit)"
test "$(find node_modules/@registrystack/client-${{ matrix.napi_platform }} \
-maxdepth 1 -name '*.node' | wc -l)" -eq "${#clients[@]}"
cp "${GITHUB_WORKSPACE}/release/scripts/smoke-registry-client-package.js" \
"${GITHUB_WORKSPACE}/release/scripts/smoke-registry-client-package.mjs" .
node smoke-registry-client-package.js
"${MINIMUM_NODE_BIN}" smoke-registry-client-package.js
node smoke-registry-client-package.mjs
"${MINIMUM_NODE_BIN}" smoke-registry-client-package.mjs
docker run --rm --network none \
--volume "${smoke}:${smoke}:ro" \
--workdir "${smoke}" \
"${NODE_RUNTIME_SMOKE_IMAGE}" \
node smoke-registry-client-package.js
docker run --rm --network none \
--volume "${smoke}:${smoke}:ro" \
--workdir "${smoke}" \
"${NODE_RUNTIME_SMOKE_IMAGE}" \
node smoke-registry-client-package.mjs
)
fi