Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
308 commits
Select commit Hold shift + click to select a range
ad4c069
chore: port graph cache witness to bun
f0rr0 Jun 26, 2026
eb1d370
chore: remove inline python from github workflows
f0rr0 Jun 26, 2026
9769221
chore: list cargo packages with bun
f0rr0 Jun 26, 2026
2ab433b
chore: merge artifact checksums with bun
f0rr0 Jun 26, 2026
1c149d2
chore: validate coverage baseline with bun
f0rr0 Jun 26, 2026
89e3dce
chore: validate wasix dependency invariants with bun
f0rr0 Jun 26, 2026
4560a0c
fix: resolve split native tools in deno
f0rr0 Jun 26, 2026
abb0b14
fix: align deno server native package resolution
f0rr0 Jun 26, 2026
5bf85a7
chore: remove python from tool launchers
f0rr0 Jun 26, 2026
787a37e
chore: remove python from tool bootstrap
f0rr0 Jun 26, 2026
6a6f604
chore: remove inline python from node direct packaging
f0rr0 Jun 26, 2026
6aa36b3
fix: harden wasix tools artifact split
f0rr0 Jun 26, 2026
e47be6f
fix: tighten split tools sdk validation
f0rr0 Jun 26, 2026
674b682
docs: record package surface verification
f0rr0 Jun 26, 2026
d06c909
fix: align react native open mode surface
f0rr0 Jun 26, 2026
4e342a7
fix: validate wasix tools aot manifests
f0rr0 Jun 26, 2026
edcdc0e
fix: exercise wasix example tool smoke
f0rr0 Jun 26, 2026
6912d9c
fix: align release checksum tooling
f0rr0 Jun 26, 2026
241373b
chore: port extension contract check to bun
f0rr0 Jun 26, 2026
23b2797
chore: port extension tree checker to bun
f0rr0 Jun 26, 2026
223cd07
fix: install example deps from local registries
f0rr0 Jun 26, 2026
cb2980d
test: guard split tool runtime artifacts
f0rr0 Jun 26, 2026
db9dfb0
test: compile wasix split tools feature
f0rr0 Jun 26, 2026
1a11a9b
chore: guard python tooling inventory
f0rr0 Jun 26, 2026
5485530
docs: record rust tooling inventory
f0rr0 Jun 26, 2026
49a0397
chore: port release fixture generators to bun
f0rr0 Jun 26, 2026
289d5b4
chore: port helper asset validators to bun
f0rr0 Jun 26, 2026
0e49ced
chore: port shared fixture matrix checker to bun
f0rr0 Jun 26, 2026
8de11ba
chore: port release pr coverage check to bun
f0rr0 Jun 26, 2026
83d190f
chore: port native boundary policy to bun
f0rr0 Jun 26, 2026
3cd9156
chore: port wasm preflight manifest check to bun
f0rr0 Jun 26, 2026
5c2ad65
chore: port rust sdk artifact patch helper to bun
f0rr0 Jun 26, 2026
9927fef
chore: port sdk crate filename helper to bun
f0rr0 Jun 26, 2026
a20f25f
fix: split native client tools into release assets
f0rr0 Jun 26, 2026
85ae3e9
fix: harden split tool package validation
f0rr0 Jun 26, 2026
9d2c90c
fix: derive wasix package validation from manifest
f0rr0 Jun 26, 2026
08e388d
fix: align sdk artifact validation semantics
f0rr0 Jun 26, 2026
aed770b
fix: exercise wasix tools in release check
f0rr0 Jun 26, 2026
6ced470
fix: keep wasix tools out of root crates
f0rr0 Jun 26, 2026
d41d97a
fix: validate explicit mobile runtime extensions
f0rr0 Jun 26, 2026
3b1bf38
docs: record split tools validation
f0rr0 Jun 26, 2026
bf63ee5
fix: derive sdk artifact handoff from release metadata
f0rr0 Jun 26, 2026
88cffc7
fix: tighten wasix tools artifact guards
f0rr0 Jun 26, 2026
0acb1c7
fix: validate js extensions against catalog
f0rr0 Jun 26, 2026
17ff3b8
fix: validate react native extensions against catalog
f0rr0 Jun 26, 2026
91e2d41
chore: move react native extension path lookup to bun
f0rr0 Jun 26, 2026
33d3347
chore: expose rust release source preparation cli
f0rr0 Jun 26, 2026
960c8b5
chore: move wasix third-party toml reads to bun
f0rr0 Jun 26, 2026
38bfb8d
chore: move wasix extension asset packaging to bun
f0rr0 Jun 26, 2026
76155de
chore: move github release asset uploads to bun
f0rr0 Jun 26, 2026
5ba51ec
chore: move native binary stripping to bun
f0rr0 Jun 26, 2026
bd3717a
chore: move broker cargo artifact packaging to bun
f0rr0 Jun 26, 2026
a3476e2
chore: move product version reads to bun
f0rr0 Jun 26, 2026
895ed8d
chore: move moon affected helper to bun
f0rr0 Jun 26, 2026
ec5df2a
docs: record fresh example e2e validation
f0rr0 Jun 26, 2026
c549de2
test: enforce native tools package split
f0rr0 Jun 26, 2026
1f35982
chore: port source architecture check to bun
f0rr0 Jun 26, 2026
82c1051
chore: port coverage tooling to bun
f0rr0 Jun 26, 2026
df03354
test: cover js registry asset resolution
f0rr0 Jun 26, 2026
0b4a83e
test: validate js extension payloads
f0rr0 Jun 26, 2026
de33332
fix: publish js runtime caches atomically
f0rr0 Jun 26, 2026
2cd7e18
test: cover mobile runtime feature validation
f0rr0 Jun 26, 2026
cdd08a6
test: enforce split tools parity checks
f0rr0 Jun 26, 2026
1abb7aa
fix: harden local cargo registry artifact shape
f0rr0 Jun 26, 2026
80ffd09
fix: validate js explicit extension runtimes
f0rr0 Jun 26, 2026
466a4fa
chore: port swiftpm tag publisher to bun
f0rr0 Jun 26, 2026
7f02906
chore: port maven artifact manifest to bun
f0rr0 Jun 26, 2026
4a8b43b
chore: port swiftpm renderer to bun
f0rr0 Jun 26, 2026
a2372a1
docs: record split tool crate contract
f0rr0 Jun 26, 2026
d0fa271
chore: port release attestation verifier to bun
f0rr0 Jun 26, 2026
489580d
chore: port native runtime lock to bun
f0rr0 Jun 26, 2026
219b722
chore: port registry publication checks to bun
f0rr0 Jun 26, 2026
fad1fc7
chore: port github release asset check to bun
f0rr0 Jun 26, 2026
084ba72
chore: port native payload optimizer to bun
f0rr0 Jun 26, 2026
a45efae
chore: port release version check to bun
f0rr0 Jun 26, 2026
941bbeb
chore: share bun release graph planner
f0rr0 Jun 27, 2026
a261abc
chore: retire python release planner
f0rr0 Jun 27, 2026
5358ed3
chore: port artifact target matrix to bun
f0rr0 Jun 27, 2026
cc01888
feat: split native tools into cargo facade
f0rr0 Jun 27, 2026
f4f946c
chore: port ci planner to bun
f0rr0 Jun 27, 2026
d55eaf9
chore: port graph tool to bun
f0rr0 Jun 27, 2026
5ac6dc5
chore: port liboliphaunt asset check to bun
f0rr0 Jun 27, 2026
bfbc87b
chore: port release pr sync to bun
f0rr0 Jun 27, 2026
4195b0d
chore: port extension artifact staging to bun
f0rr0 Jun 27, 2026
9fa76d3
chore: port staged artifact check to bun
f0rr0 Jun 27, 2026
aa76bfb
chore: port native cargo artifact packager to bun
f0rr0 Jun 27, 2026
cf41a7b
chore: route extension targets through bun graph
f0rr0 Jun 27, 2026
56b9037
chore: route artifact targets through bun graph
f0rr0 Jun 27, 2026
8a801c3
chore: codify split wasix tools contract
f0rr0 Jun 27, 2026
fce6b4f
chore: route product metadata through bun graph
f0rr0 Jun 27, 2026
ea5d451
test: guard react-native runtime feature rejection
f0rr0 Jun 27, 2026
17d523d
chore: expose python tooling inventory
f0rr0 Jun 27, 2026
409ae4d
test: guard wasix split tools feature surface
f0rr0 Jun 27, 2026
80f2ab7
chore: remove retired perf matrix wrapper
f0rr0 Jun 27, 2026
a32d0a7
chore: inventory rust helper crates
f0rr0 Jun 27, 2026
b1f4127
test: refresh wasix example registry locks
f0rr0 Jun 27, 2026
35c1dea
chore: port example policy check to bun
f0rr0 Jun 27, 2026
b3e72d9
test: cover example bun tooling in policy gate
f0rr0 Jun 27, 2026
99f789c
chore: remove retired helper wrappers
f0rr0 Jun 27, 2026
d6cc3f6
chore: add helper reference sweep tooling
f0rr0 Jun 27, 2026
92d1020
chore: expose python helper inventory sizes
f0rr0 Jun 27, 2026
63877e7
test: parse sdk manifest contract
f0rr0 Jun 27, 2026
660f753
docs: retire stale release helper references
f0rr0 Jun 27, 2026
df39644
chore: add source dead-code candidate scan
f0rr0 Jun 27, 2026
f0a1ade
fix: harden local registry tool asset staging
f0rr0 Jun 27, 2026
b0f1018
chore: classify helper migration inventory
f0rr0 Jun 27, 2026
ac10c9b
fix: align local registry cargo validation
f0rr0 Jun 27, 2026
104adb4
fix: deduplicate local extension manifests
f0rr0 Jun 27, 2026
3f93459
chore: harden helper reference scan
f0rr0 Jun 27, 2026
71407e4
chore: retire product metadata version cli
f0rr0 Jun 27, 2026
86798e6
docs: refresh local ci validation evidence
f0rr0 Jun 27, 2026
1f8a584
refactor: derive python release metadata from bun graph
f0rr0 Jun 27, 2026
5a7322b
fix: isolate registry-backed examples
f0rr0 Jun 27, 2026
488ccf8
docs: record example registry validation
f0rr0 Jun 27, 2026
3bb42cc
refactor: derive wasix cargo packager contract
f0rr0 Jun 27, 2026
646d44f
refactor: centralize compatibility version metadata
f0rr0 Jun 27, 2026
12585ae
refactor: share extension release metadata
f0rr0 Jun 27, 2026
47dbf2f
refactor: query product versions from release graph
f0rr0 Jun 27, 2026
cf0d165
fix: stage native client tools separately
f0rr0 Jun 27, 2026
5384de4
refactor: remove dead product metadata version specs
f0rr0 Jun 27, 2026
63a311d
refactor: retire compatibility version metadata adapters
f0rr0 Jun 27, 2026
33c818d
refactor: share typescript optional runtime selection
f0rr0 Jun 27, 2026
7011c16
refactor: share sdk package selection
f0rr0 Jun 27, 2026
134cfae
refactor: clarify split tool artifact packaging
f0rr0 Jun 27, 2026
2c4911d
refactor: centralize local publish artifact preset
f0rr0 Jun 27, 2026
4f1232a
refactor: centralize expected release assets
f0rr0 Jun 27, 2026
36f5798
refactor: centralize registry package selection
f0rr0 Jun 27, 2026
14656c5
refactor: centralize extension product discovery
f0rr0 Jun 27, 2026
4674cfe
refactor: reuse extension product selector
f0rr0 Jun 27, 2026
1fed3b1
refactor: centralize wasix extension package names
f0rr0 Jun 27, 2026
453daf6
refactor: centralize product config metadata
f0rr0 Jun 27, 2026
0d7a7e2
refactor: centralize moon release metadata
f0rr0 Jun 27, 2026
6a1006d
refactor: centralize moon project policy rows
f0rr0 Jun 27, 2026
d865feb
refactor: query legacy artifact target rows
f0rr0 Jun 27, 2026
f88ee60
refactor: drop metadata graph handoff
f0rr0 Jun 27, 2026
3924c74
chore: drop stale python release packager requirements
f0rr0 Jun 27, 2026
0c49438
chore: use bun release planner in ci
f0rr0 Jun 27, 2026
7e6d7b4
chore: query release ci artifacts with bun
f0rr0 Jun 27, 2026
45b9ff1
chore: use bun planner for release pr coverage
f0rr0 Jun 27, 2026
b995c2c
docs: record release validation batch
f0rr0 Jun 27, 2026
ad65e7c
docs: record sdk parity validation
f0rr0 Jun 27, 2026
9bf0255
fix: dedupe react native ios resource bundles
f0rr0 Jun 27, 2026
e9a7067
test: assert ci artifact families
f0rr0 Jun 27, 2026
ac3e3f3
fix: validate kotlin extension ids against catalog
f0rr0 Jun 27, 2026
8bd2c8f
fix: tighten source input policy exceptions
f0rr0 Jun 27, 2026
27f7892
chore: record helper migration inventory
f0rr0 Jun 27, 2026
f0f6b34
chore: port helper wrappers to bun
f0rr0 Jun 27, 2026
df05cdc
docs: correct helper migration inventory
f0rr0 Jun 27, 2026
748895c
chore: keep python bytecode out of source tools
f0rr0 Jun 27, 2026
06d631a
chore: port android disk reclaim helper to bun
f0rr0 Jun 27, 2026
065ae0c
chore: port native ci target wrapper to bun
f0rr0 Jun 27, 2026
770baff
chore: retire native smoke compatibility wrappers
f0rr0 Jun 27, 2026
23c4601
chore: port release artifact downloader to bun
f0rr0 Jun 27, 2026
a8d76d6
chore: port wasix runtime artifact downloader to bun
f0rr0 Jun 27, 2026
e5d417b
chore: allowlist intentional helper entrypoints
f0rr0 Jun 27, 2026
f7ef215
chore: move publish coverage to release graph
f0rr0 Jun 27, 2026
c1c8f79
chore: remove duplicate release metadata readers
f0rr0 Jun 27, 2026
e89867f
chore: query release policy metadata via bun
f0rr0 Jun 27, 2026
277033f
chore: query artifact targets via bun
f0rr0 Jun 27, 2026
dd0e2d4
chore: query consumer shape metadata via bun
f0rr0 Jun 27, 2026
f85a048
chore: query extension metadata via bun
f0rr0 Jun 27, 2026
3a87860
chore: query local registry metadata via bun
f0rr0 Jun 27, 2026
0c8e20c
chore: query wasix cargo packager metadata via bun
f0rr0 Jun 27, 2026
8ea498d
chore: query release metadata checks via bun
f0rr0 Jun 27, 2026
d4c5677
chore: query release cli metadata via bun
f0rr0 Jun 27, 2026
889e6a0
chore: remove stale release metadata inputs
f0rr0 Jun 27, 2026
575eba7
chore: remove release metadata compatibility module
f0rr0 Jun 27, 2026
af614dd
chore: remove release ci compatibility commands
f0rr0 Jun 27, 2026
dcbfc8e
chore: remove release plan compatibility command
f0rr0 Jun 27, 2026
5fa3b8d
chore: port sdk artifact builder to bun
f0rr0 Jun 27, 2026
10138bf
chore: port wasix cargo packager to bun
f0rr0 Jun 27, 2026
125c47f
chore: port release policy check to bun
f0rr0 Jun 27, 2026
3222cbe
chore: port artifact target check to bun
f0rr0 Jun 27, 2026
43d2230
chore: move local registry metadata queries to bun
f0rr0 Jun 27, 2026
ce216bd
chore: route local registry metadata through bun
f0rr0 Jun 27, 2026
21ca590
chore: move release check orchestration to bun
f0rr0 Jun 27, 2026
2ad60d4
chore: move release gate wrappers to bun
f0rr0 Jun 27, 2026
b0d00c9
chore: move rust publish source prep to bun
f0rr0 Jun 27, 2026
3b3e616
chore: add bun local registry entrypoint
f0rr0 Jun 27, 2026
46df66d
chore: port local registry status to bun
f0rr0 Jun 28, 2026
513ccd0
chore: port local registry download to bun
f0rr0 Jun 28, 2026
4d3b148
chore: port local registry maven and swift publish to bun
f0rr0 Jun 28, 2026
aa71446
chore: port local registry cargo dry run to bun
f0rr0 Jun 28, 2026
c2b3bab
chore: port local registry npm dry run to bun
f0rr0 Jun 28, 2026
7ca9775
chore: port prebuilt npm local registry publish to bun
f0rr0 Jun 28, 2026
fd8ff26
chore: keep local registry status in bun
f0rr0 Jun 28, 2026
5871ee0
chore: keep local registry help in bun
f0rr0 Jun 28, 2026
f86e956
chore: remove local registry catch-all python fallback
f0rr0 Jun 28, 2026
7ec5374
chore: port prebuilt cargo local registry publish to bun
f0rr0 Jun 28, 2026
24d6b6b
chore: keep cargo local registry staging in bun
f0rr0 Jun 28, 2026
11c489d
chore: keep npm release asset staging in bun
f0rr0 Jun 28, 2026
b8754d2
chore: keep extension npm staging in bun
f0rr0 Jun 28, 2026
a17e683
chore: finish local registry publisher bun port
f0rr0 Jun 28, 2026
4cbd310
chore: remove dead python release helpers
f0rr0 Jun 28, 2026
028cc61
chore: route extension model checks through bun
f0rr0 Jun 28, 2026
129ca3e
chore: route release validators through bun
f0rr0 Jun 28, 2026
87c2895
chore: route release publish through bun
f0rr0 Jun 28, 2026
418ae42
chore: route root release check through bun
f0rr0 Jun 28, 2026
260203f
chore: route moon release metadata through bun
f0rr0 Jun 28, 2026
30cd914
chore: retire release check compatibility commands
f0rr0 Jun 28, 2026
d2275b5
chore: run no-product release dry-run in bun
f0rr0 Jun 28, 2026
2f40cac
chore: refine helper dead-code scanner
f0rr0 Jun 28, 2026
bd57e5d
docs: route release dry-run through bun
f0rr0 Jun 28, 2026
8b69c3c
chore: route extension model guidance through bun
f0rr0 Jun 28, 2026
c3398f9
chore: expose rust helper inventory as json
f0rr0 Jun 28, 2026
37f7da7
docs: clarify bun release command surface
f0rr0 Jun 28, 2026
bfda090
chore: keep no-product dry runs in bun
f0rr0 Jun 28, 2026
23ac210
docs: record release dry-run bun evidence
f0rr0 Jun 28, 2026
6ce3c87
chore: move sdk dry runs into bun
f0rr0 Jun 28, 2026
82ddb5d
chore: extend sdk dry runs to swift kotlin
f0rr0 Jun 28, 2026
678f3f4
chore: move rust sdk dry run into bun
f0rr0 Jun 28, 2026
2b39574
chore: move wasix rust dry run into bun
f0rr0 Jun 28, 2026
2ac04a3
chore: move node-direct dry run into bun
f0rr0 Jun 28, 2026
7122d26
chore: align node-direct dry run guard
f0rr0 Jun 28, 2026
f4a8039
chore: track node-direct dry run inputs
f0rr0 Jun 28, 2026
7c4acbe
chore: move broker dry run into bun
f0rr0 Jun 28, 2026
c0e3d70
chore: move extension dry runs into bun
f0rr0 Jun 28, 2026
45863ac
chore: move wasix runtime dry run into bun
f0rr0 Jun 28, 2026
b7bc7c9
chore: route wasm publish dry run through bun
f0rr0 Jun 28, 2026
d5f99a8
fix: keep wasix tools out of root artifact staging
f0rr0 Jun 28, 2026
c10b0f4
fix: validate split native tools by target
f0rr0 Jun 28, 2026
d406cd7
fix: run native publish dry run in bun
f0rr0 Jun 28, 2026
35634ba
chore: remove react native release.py task inputs
f0rr0 Jun 28, 2026
ebbdf1a
fix: keep product dry runs in bun
f0rr0 Jun 28, 2026
392f951
fix: publish staged github assets in bun
f0rr0 Jun 28, 2026
54e53c4
fix: publish extension github assets in bun
f0rr0 Jun 28, 2026
00c4d35
fix: publish extension maven artifacts in bun
f0rr0 Jun 28, 2026
c6de76a
fix: publish runtime maven artifacts in bun
f0rr0 Jun 28, 2026
52b6160
fix: publish node direct npm artifacts in bun
f0rr0 Jun 28, 2026
a934bcd
fix: publish broker npm artifacts in bun
f0rr0 Jun 28, 2026
d1916b5
fix: publish liboliphaunt npm artifacts in bun
f0rr0 Jun 28, 2026
ac6e93d
fix: publish react native npm artifacts in bun
f0rr0 Jun 28, 2026
0590365
fix: publish broker cargo artifacts in bun
f0rr0 Jun 28, 2026
43727c9
fix: publish wasix cargo artifacts in bun
f0rr0 Jun 28, 2026
08b529b
fix: publish native cargo artifacts in bun
f0rr0 Jun 28, 2026
630ca9e
fix: publish rust sdk crates in bun
f0rr0 Jun 28, 2026
d55d4c6
refactor: remove rust sdk publish fallback from release py
f0rr0 Jun 28, 2026
b1e6674
refactor: publish wasix rust sdk crates in bun
f0rr0 Jun 28, 2026
02c6dba
refactor: publish typescript sdk packages in bun
f0rr0 Jun 28, 2026
22c32e5
refactor: publish swift release in bun
f0rr0 Jun 28, 2026
625ce96
refactor: publish kotlin maven artifacts in bun
f0rr0 Jun 28, 2026
22e6fd9
refactor: remove release publish python fallback
f0rr0 Jun 28, 2026
f25e399
refactor: retire python release publish entrypoint
f0rr0 Jun 28, 2026
797d79c
fix: harden native and wasix packaging checks
f0rr0 Jun 29, 2026
aa4f14e
fix: stabilize remaining ci packaging checks
f0rr0 Jun 29, 2026
cd1b92f
fix: avoid nested moon query in release graph
f0rr0 Jun 29, 2026
a93db46
fix: stabilize release graph project discovery
f0rr0 Jun 29, 2026
586a474
fix: resolve pinned bun on windows checks
f0rr0 Jun 29, 2026
e35e012
fix: run native stripper through active bun
f0rr0 Jun 29, 2026
ea1ba7b
fix: fetch libiconv from gnu mirror
f0rr0 Jun 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/actions/collect-ci-summary/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ runs:
echo
echo "- Moon projects: \`moon query projects\`"
echo "- Moon tasks: \`moon query tasks\`"
echo "- Release plan: \`tools/release/release.py plan --from-product-tags --head-ref <release-ref>\`"
echo "- Release plan: \`tools/dev/bun.sh tools/release/release_plan.mjs --from-product-tags --head-ref <release-ref>\`"
} >> "$GITHUB_STEP_SUMMARY"
10 changes: 2 additions & 8 deletions .github/actions/setup-deno/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,8 @@ runs:
--connect-timeout 20 \
--output "$tmp/deno.zip" \
"$url"
python3 - "$tmp/deno.zip" "$DENO_CACHE_DIR" <<'PY'
import sys
import zipfile

archive, output = sys.argv[1], sys.argv[2]
with zipfile.ZipFile(archive) as zip_file:
zip_file.extractall(output)
PY
mkdir -p "$DENO_CACHE_DIR"
unzip -oq "$tmp/deno.zip" -d "$DENO_CACHE_DIR"
chmod +x "$DENO_BINARY"
echo "$DENO_CACHE_DIR" >> "$GITHUB_PATH"

Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/check-release-intent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ EOF
exit 1
fi

release_plan="$(tools/release/release.py plan --base-ref "${base_ref}" --head-ref "${head_ref}" --format json)"
release_plan="$(tools/dev/bun.sh tools/release/release_plan.mjs --base-ref "${base_ref}" --head-ref "${head_ref}" --format json)"
release_products="$(
bun -e 'const data = JSON.parse(await Bun.stdin.text()); console.log((data.releaseProducts ?? []).join("\n"));' <<< "${release_plan}"
)"
Expand Down
293 changes: 293 additions & 0 deletions .github/scripts/download-build-artifacts.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
#!/usr/bin/env bun
import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import {
chmodSync,
copyFileSync,
createReadStream,
existsSync,
mkdirSync,
mkdtempSync,
readdirSync,
rmSync,
statSync,
utimesSync,
} from "node:fs";
import os from "node:os";
import path from "node:path";
import process from "node:process";

const USAGE =
"usage: download-build-artifacts.mjs <workflow> <sha> <destination> [--run-id <id>] [--job <name>] --artifact <name> [--artifact <name>...]";

function fail(message, code = 1) {
console.error(message);
process.exit(code);
}

function parseArgs(argv) {
if (argv.length < 3) {
fail(USAGE, 2);
}
const [workflow, sha, destination, ...rest] = argv;
const args = {
workflow,
sha,
destination,
artifacts: [],
requiredJob: "",
selectedRunId: "",
};
for (let index = 0; index < rest.length; ) {
const arg = rest[index];
if (arg === "--run-id") {
args.selectedRunId = valueAfter(rest, index, "--run-id requires a run id");
index += 2;
} else if (arg === "--job") {
args.requiredJob = valueAfter(rest, index, "--job requires a name");
index += 2;
} else if (arg === "--artifact") {
args.artifacts.push(valueAfter(rest, index, "--artifact requires a name"));
index += 2;
} else {
fail(`unknown argument: ${arg}`, 2);
}
}
if (args.artifacts.length === 0) {
fail("at least one --artifact is required", 2);
}
return args;
}

function valueAfter(argv, index, message) {
const value = argv[index + 1];
if (value === undefined || value.startsWith("--")) {
fail(message, 2);
}
return value;
}

function requireEnv(name) {
const value = process.env[name];
if (value === undefined || value === "") {
fail(`${name} is required`);
}
return value;
}

function run(command, args, { capture = false } = {}) {
const result = spawnSync(command, args, {
encoding: "utf8",
stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
env: process.env,
});
if (result.error) {
throw result.error;
}
if (result.status !== 0) {
if (capture) {
process.stderr.write(result.stderr ?? "");
process.stderr.write(result.stdout ?? "");
}
throw new Error(`${[command, ...args].join(" ")} exited with status ${result.status}`);
}
return result.stdout ?? "";
}

function gh(args, options) {
return run("gh", args, options);
}

function artifactNames(repo, runId) {
try {
return gh(
[
"api",
`repos/${repo}/actions/runs/${runId}/artifacts?per_page=100`,
"--paginate",
"--jq",
".artifacts[].name",
],
{ capture: true },
)
.split(/\r?\n/u)
.filter(Boolean);
} catch (error) {
fail(`failed to list artifacts for run ${runId}: ${error.message}`);
}
}

function artifactPresent(repo, runId, artifact) {
return artifactNames(repo, runId).includes(artifact);
}

function requiredJobSuccess(repo, runId, requiredJob) {
if (requiredJob === "") {
return true;
}
let data;
try {
data = JSON.parse(gh(["run", "view", runId, "--repo", repo, "--json", "jobs"], { capture: true }));
} catch {
return false;
}
const job = (data.jobs ?? []).find((candidate) => candidate?.name === requiredJob);
return job?.conclusion === "success";
}

function candidateRunIds(repo, workflow, sha, requiredJob) {
const runs = JSON.parse(
gh(
[
"run",
"list",
"--repo",
repo,
"--workflow",
workflow,
"--commit",
sha,
"--limit",
"20",
"--json",
"databaseId,status,conclusion,event,createdAt",
],
{ capture: true },
),
);
return runs
.filter((run) => requiredJob !== "" || (run.status === "completed" && run.conclusion === "success"))
.map((run) => String(run.databaseId))
.filter(Boolean);
}

function sortedFiles(root) {
const files = [];
function visit(directory) {
const entries = readdirSync(directory, { withFileTypes: true }).sort((left, right) =>
left.name.localeCompare(right.name),
);
for (const entry of entries) {
const entryPath = path.join(directory, entry.name);
if (entry.isDirectory()) {
visit(entryPath);
} else if (entry.isFile()) {
files.push(entryPath);
}
}
}
visit(root);
return files;
}

function fileSha256(file) {
return new Promise((resolve, reject) => {
const hash = createHash("sha256");
const stream = createReadStream(file);
stream.on("error", reject);
stream.on("data", (chunk) => hash.update(chunk));
stream.on("end", () => resolve(hash.digest("hex")));
});
}

async function filesEqual(left, right) {
const leftStat = statSync(left);
const rightStat = statSync(right);
return leftStat.size === rightStat.size && (await fileSha256(left)) === (await fileSha256(right));
}

function copyPreserve(source, target) {
const sourceStat = statSync(source);
copyFileSync(source, target);
chmodSync(target, sourceStat.mode);
utimesSync(target, sourceStat.atime, sourceStat.mtime);
}

function mergeChecksumManifest(existing, incoming) {
const result = spawnSync("bun", [".github/scripts/merge-checksum-manifest.mjs", existing, incoming], {
stdio: "inherit",
env: process.env,
});
return !result.error && result.status === 0;
}

async function mergeDownloadedArtifact(artifact, sourceDir, destination) {
for (const source of sortedFiles(sourceDir)) {
const relativePath = path.relative(sourceDir, source);
const target = path.join(destination, relativePath);
mkdirSync(path.dirname(target), { recursive: true });
if (existsSync(target)) {
if (statSync(target).isFile() && (await filesEqual(source, target))) {
continue;
}
if (
statSync(target).isFile() &&
statSync(source).isFile() &&
path.basename(target).endsWith("-release-assets.sha256")
) {
if (!mergeChecksumManifest(target, source)) {
return false;
}
continue;
}
console.error(`artifact ${artifact} would overwrite ${relativePath} with different bytes`);
return false;
}
copyPreserve(source, target);
}
return true;
}

function selectRunId(repo, args) {
if (args.selectedRunId !== "") {
const runId = args.selectedRunId;
if (!requiredJobSuccess(repo, runId, args.requiredJob)) {
fail(`${args.workflow} run ${runId} does not satisfy required job ${args.requiredJob || "<none>"}`);
}
for (const artifact of args.artifacts) {
if (!artifactPresent(repo, runId, artifact)) {
fail(`${args.workflow} run ${runId} is missing required artifact ${artifact}`);
}
}
return runId;
}

for (const candidate of candidateRunIds(repo, args.workflow, args.sha, args.requiredJob)) {
if (!requiredJobSuccess(repo, candidate, args.requiredJob)) {
continue;
}
if (args.artifacts.every((artifact) => artifactPresent(repo, candidate, artifact))) {
return candidate;
}
}
fail(
`no ${args.workflow} workflow run found for ${args.sha} with required job/artifacts: ${args.requiredJob || "<workflow-success>"} / ${args.artifacts.join(" ")}`,
);
}

async function main() {
const args = parseArgs(Bun.argv.slice(2));
requireEnv("GH_TOKEN");
const repo = requireEnv("GH_REPO");
const runId = selectRunId(repo, args);
mkdirSync(args.destination, { recursive: true });

for (const artifact of args.artifacts) {
console.log(`Downloading ${args.workflow} artifact ${artifact} from run ${runId}`);
const artifactDir = mkdtempSync(path.join(os.tmpdir(), "oliphaunt-artifact-"));
try {
gh(["run", "download", runId, "--repo", repo, "--name", artifact, "--dir", artifactDir]);
if (!(await mergeDownloadedArtifact(artifact, artifactDir, args.destination))) {
process.exit(1);
}
} finally {
rmSync(artifactDir, { recursive: true, force: true });
}
}
}

try {
await main();
} catch (error) {
fail(error instanceof Error ? error.message : String(error));
}
Loading
Loading