From d6516607aea81ca4c599d28fb763869c7efe7b73 Mon Sep 17 00:00:00 2001 From: zackees Date: Tue, 25 Aug 2026 01:43:07 -0700 Subject: [PATCH 1/6] fix(release): restore 2.5.21 cross builds --- .github/workflows/release-auto.yml | 4 +++ .github/workflows/template_native_build.yml | 33 +++++++++---------- crates/fbuild-python/tests/pyo3_policy.rs | 35 +++++++++++++++++++++ python/fbuild/__init__.py | 3 +- 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release-auto.yml b/.github/workflows/release-auto.yml index 9eb0fc8f4..02c1226ea 100644 --- a/.github/workflows/release-auto.yml +++ b/.github/workflows/release-auto.yml @@ -6,6 +6,10 @@ on: paths: - Cargo.toml - pyproject.toml + # An incomplete release must be retried when its release machinery is + # repaired without forcing another public version bump. + - .github/workflows/release-auto.yml + - .github/workflows/template_native_build.yml workflow_dispatch: inputs: publish: diff --git a/.github/workflows/template_native_build.yml b/.github/workflows/template_native_build.yml index 686eedce1..709af06fe 100644 --- a/.github/workflows/template_native_build.yml +++ b/.github/workflows/template_native_build.yml @@ -47,6 +47,10 @@ env: # GitHub Actions owns the outer deadline instead of soldr killing the # build early. SOLDR_CARGO_WAIT_TIMEOUT_SECS: "2700" + # Keep every soldr invocation in this reusable job on the same catalogue. + # soldr >= 0.9.5 consumes its multipart v2 assets, including Apple SDKs + # whose legacy direct-LFS URLs are no longer anonymously downloadable. + SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain # cc-rs env vars for the xwin lanes. ring's build.rs uses cc-rs; # without these, cc-rs invokes plain `clang` and the curve25519.c # compile fails with `error: no such file or directory: '/imsvc'` @@ -93,15 +97,13 @@ jobs: # v0.7.85/v0.7.87 were broken releases that shipped silent # binaries (no output at all), which setup-soldr@v0.9.63 # surfaced as a version-JSON parse failure. - # Pin the latest published soldr release; setup-soldr's current - # default can briefly lead a release that has not been published yet. - # soldr v0.8.0 is the - # known-good floor for the Apple SDK URL fix, soldr-clang-shim - # in every release archive, cargo:rustc-env wrapper forwarding, - # and managed cmake/ninja. + # Pin a fully published soldr release. v0.9.5 is the first version + # that consumes catalogue-v2 multipart assets; v0.8.23 still probes + # the retired direct-LFS Apple SDK URLs even though the SDKs are + # available through the v2 catalogue. uses: zackees/setup-soldr@v0 with: - version: 0.8.23 + version: 0.9.6 cache: true build-cache: true target-cache: true @@ -226,20 +228,12 @@ jobs: # cargo invocation so the SDKROOT export is debuggable independent # of the build itself. # - # SOLDR_TOOLCHAIN_ORIGIN redirects soldr's toolchain catalogue - # fetch away from the retired `manifest` branch of - # zackees/soldr (soldr#988 Phase 5 / soldr#992, merged - # 2026-06-27) at the new soldr-toolchain origin. soldr v0.7.66 - # honors this env var via the Phase 2 catalogue-first - # fetch_once (soldr#989); older versions ignore it (and fail - # against the retired manifest branch, which is what we're - # avoiding here). + # SOLDR_TOOLCHAIN_ORIGIN is job-scoped above so this prepare step and + # both following build steps resolve the exact same catalogue. - name: Prepare Apple SDK (Linux → mac cross) if: inputs.mac_cross_linux timeout-minutes: 15 shell: bash - env: - SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain run: soldr prepare --target ${{ inputs.target }} - name: Build release binaries @@ -301,7 +295,10 @@ jobs: # soldr's bin cache, which has been serving a corrupted # cargo-zigbuild binary (`Syntax error: ")" unexpected` at # line 10) and blocking PyPI publishes. See #331. - cargo zigbuild --release --target ${{ inputs.target }} \ + # mimalloc-pprof's vendored diagnostic banner expands + # __DATE__/__TIME__. Zig promotes that one warning to an error; + # demote only that diagnostic for this C dependency build. + CFLAGS="-Wno-error=date-time" cargo zigbuild --release --target ${{ inputs.target }} \ -p fbuild-cli \ -p fbuild-daemon else diff --git a/crates/fbuild-python/tests/pyo3_policy.rs b/crates/fbuild-python/tests/pyo3_policy.rs index dc5d84529..070e43307 100644 --- a/crates/fbuild-python/tests/pyo3_policy.rs +++ b/crates/fbuild-python/tests/pyo3_policy.rs @@ -93,3 +93,38 @@ fn pyo3_029_policy_stays_target_python_independent() { ); } } + +#[test] +fn native_release_workflow_uses_current_cross_toolchains() { + let root = repo_root(); + let workflow = + fs::read_to_string(root.join(".github/workflows/template_native_build.yml")).unwrap(); + let release_workflow = + fs::read_to_string(root.join(".github/workflows/release-auto.yml")).unwrap(); + + assert!( + workflow.contains("version: 0.9.6"), + "native release builds need soldr >= 0.9.5 for catalogue-v2 Apple SDK assets" + ); + assert!( + workflow.contains( + "SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain" + ), + "Apple SDK prepare and build steps must share the catalogue origin" + ); + assert!( + workflow.contains( + "CFLAGS=\"-Wno-error=date-time\" cargo zigbuild --release --target" + ), + "musl release builds must demote zig's date-time error for mimalloc-pprof" + ); + for release_input in [ + "- .github/workflows/release-auto.yml", + "- .github/workflows/template_native_build.yml", + ] { + assert!( + release_workflow.contains(release_input), + "release workflow fixes must retrigger an incomplete publication: {release_input}" + ); + } +} diff --git a/python/fbuild/__init__.py b/python/fbuild/__init__.py index 77fef08fa..beb1c9317 100644 --- a/python/fbuild/__init__.py +++ b/python/fbuild/__init__.py @@ -19,12 +19,13 @@ find_firmware, ) + __all__ = [ - "__version__", "AsyncDaemon", "AsyncDaemonConnection", "Daemon", "DaemonConnection", + "__version__", "connect_daemon", "connect_daemon_async", "find_firmware", From b8381969fb48e5868c7818edce8cffe37821e425 Mon Sep 17 00:00:00 2001 From: zackees Date: Tue, 25 Aug 2026 01:48:52 -0700 Subject: [PATCH 2/6] style(test): apply rustfmt to release policy --- crates/fbuild-python/tests/pyo3_policy.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/fbuild-python/tests/pyo3_policy.rs b/crates/fbuild-python/tests/pyo3_policy.rs index 070e43307..97e35d7a1 100644 --- a/crates/fbuild-python/tests/pyo3_policy.rs +++ b/crates/fbuild-python/tests/pyo3_policy.rs @@ -107,15 +107,11 @@ fn native_release_workflow_uses_current_cross_toolchains() { "native release builds need soldr >= 0.9.5 for catalogue-v2 Apple SDK assets" ); assert!( - workflow.contains( - "SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain" - ), + workflow.contains("SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain"), "Apple SDK prepare and build steps must share the catalogue origin" ); assert!( - workflow.contains( - "CFLAGS=\"-Wno-error=date-time\" cargo zigbuild --release --target" - ), + workflow.contains("CFLAGS=\"-Wno-error=date-time\" cargo zigbuild --release --target"), "musl release builds must demote zig's date-time error for mimalloc-pprof" ); for release_input in [ From 8c06e0f3f86cf0ea9b4f085949e604c8df758415 Mon Sep 17 00:00:00 2001 From: zackees Date: Tue, 25 Aug 2026 01:54:37 -0700 Subject: [PATCH 3/6] fix(release): bound cold cross-build concurrency --- .github/workflows/template_native_build.yml | 5 +++++ crates/fbuild-python/tests/pyo3_policy.rs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/template_native_build.yml b/.github/workflows/template_native_build.yml index 709af06fe..8c6e42923 100644 --- a/.github/workflows/template_native_build.yml +++ b/.github/workflows/template_native_build.yml @@ -47,6 +47,11 @@ env: # GitHub Actions owns the outer deadline instead of soldr killing the # build early. SOLDR_CARGO_WAIT_TIMEOUT_SECS: "2700" + # Six cold release lanes run concurrently on two-core hosted runners. + # Bound rustc and soldr concurrency so cross-target codegen stays within + # the runner memory limit instead of being killed by the host. + CARGO_BUILD_JOBS: "2" + SOLDR_JOBS: "2" # Keep every soldr invocation in this reusable job on the same catalogue. # soldr >= 0.9.5 consumes its multipart v2 assets, including Apple SDKs # whose legacy direct-LFS URLs are no longer anonymously downloadable. diff --git a/crates/fbuild-python/tests/pyo3_policy.rs b/crates/fbuild-python/tests/pyo3_policy.rs index 97e35d7a1..d198d5626 100644 --- a/crates/fbuild-python/tests/pyo3_policy.rs +++ b/crates/fbuild-python/tests/pyo3_policy.rs @@ -114,6 +114,12 @@ fn native_release_workflow_uses_current_cross_toolchains() { workflow.contains("CFLAGS=\"-Wno-error=date-time\" cargo zigbuild --release --target"), "musl release builds must demote zig's date-time error for mimalloc-pprof" ); + for job_limit in ["CARGO_BUILD_JOBS: \"2\"", "SOLDR_JOBS: \"2\""] { + assert!( + workflow.contains(job_limit), + "native release lanes must stay below hosted-runner memory limits: {job_limit}" + ); + } for release_input in [ "- .github/workflows/release-auto.yml", "- .github/workflows/template_native_build.yml", From a241404ce8ab086750f529d54a8c2f9f383b0f69 Mon Sep 17 00:00:00 2001 From: zackees Date: Tue, 25 Aug 2026 02:02:07 -0700 Subject: [PATCH 4/6] fix(release): serialize cold cross compilation --- .github/workflows/template_native_build.yml | 4 ++-- crates/fbuild-python/tests/pyo3_policy.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/template_native_build.yml b/.github/workflows/template_native_build.yml index 8c6e42923..90929409a 100644 --- a/.github/workflows/template_native_build.yml +++ b/.github/workflows/template_native_build.yml @@ -50,8 +50,8 @@ env: # Six cold release lanes run concurrently on two-core hosted runners. # Bound rustc and soldr concurrency so cross-target codegen stays within # the runner memory limit instead of being killed by the host. - CARGO_BUILD_JOBS: "2" - SOLDR_JOBS: "2" + CARGO_BUILD_JOBS: "1" + SOLDR_JOBS: "1" # Keep every soldr invocation in this reusable job on the same catalogue. # soldr >= 0.9.5 consumes its multipart v2 assets, including Apple SDKs # whose legacy direct-LFS URLs are no longer anonymously downloadable. diff --git a/crates/fbuild-python/tests/pyo3_policy.rs b/crates/fbuild-python/tests/pyo3_policy.rs index d198d5626..f820481f8 100644 --- a/crates/fbuild-python/tests/pyo3_policy.rs +++ b/crates/fbuild-python/tests/pyo3_policy.rs @@ -114,7 +114,7 @@ fn native_release_workflow_uses_current_cross_toolchains() { workflow.contains("CFLAGS=\"-Wno-error=date-time\" cargo zigbuild --release --target"), "musl release builds must demote zig's date-time error for mimalloc-pprof" ); - for job_limit in ["CARGO_BUILD_JOBS: \"2\"", "SOLDR_JOBS: \"2\""] { + for job_limit in ["CARGO_BUILD_JOBS: \"1\"", "SOLDR_JOBS: \"1\""] { assert!( workflow.contains(job_limit), "native release lanes must stay below hosted-runner memory limits: {job_limit}" From e044151ebad649109f69e1dedc71cfe4237be8c2 Mon Sep 17 00:00:00 2001 From: zackees Date: Tue, 25 Aug 2026 02:14:57 -0700 Subject: [PATCH 5/6] fix(release): preserve zig cross C flags --- .github/workflows/template_native_build.yml | 9 ++++++++- crates/fbuild-python/tests/pyo3_policy.rs | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/template_native_build.yml b/.github/workflows/template_native_build.yml index 90929409a..dbcc2a37d 100644 --- a/.github/workflows/template_native_build.yml +++ b/.github/workflows/template_native_build.yml @@ -52,6 +52,13 @@ env: # the runner memory limit instead of being killed by the host. CARGO_BUILD_JOBS: "1" SOLDR_JOBS: "1" + # cargo-zigbuild clears generic CFLAGS while constructing a cross-target + # environment. Use cc-rs's target-specific variables so the vendored + # mimalloc-pprof build keeps this narrow Zig diagnostic demotion. + CFLAGS_x86_64_unknown_linux_musl: "-Wno-error=date-time" + CFLAGS_aarch64_unknown_linux_musl: "-Wno-error=date-time" + CFLAGS_x86_64_apple_darwin: "-Wno-error=date-time" + CFLAGS_aarch64_apple_darwin: "-Wno-error=date-time" # Keep every soldr invocation in this reusable job on the same catalogue. # soldr >= 0.9.5 consumes its multipart v2 assets, including Apple SDKs # whose legacy direct-LFS URLs are no longer anonymously downloadable. @@ -303,7 +310,7 @@ jobs: # mimalloc-pprof's vendored diagnostic banner expands # __DATE__/__TIME__. Zig promotes that one warning to an error; # demote only that diagnostic for this C dependency build. - CFLAGS="-Wno-error=date-time" cargo zigbuild --release --target ${{ inputs.target }} \ + cargo zigbuild --release --target ${{ inputs.target }} \ -p fbuild-cli \ -p fbuild-daemon else diff --git a/crates/fbuild-python/tests/pyo3_policy.rs b/crates/fbuild-python/tests/pyo3_policy.rs index f820481f8..dabf96b4d 100644 --- a/crates/fbuild-python/tests/pyo3_policy.rs +++ b/crates/fbuild-python/tests/pyo3_policy.rs @@ -110,10 +110,18 @@ fn native_release_workflow_uses_current_cross_toolchains() { workflow.contains("SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain"), "Apple SDK prepare and build steps must share the catalogue origin" ); - assert!( - workflow.contains("CFLAGS=\"-Wno-error=date-time\" cargo zigbuild --release --target"), - "musl release builds must demote zig's date-time error for mimalloc-pprof" - ); + for target in [ + "x86_64_unknown_linux_musl", + "aarch64_unknown_linux_musl", + "x86_64_apple_darwin", + "aarch64_apple_darwin", + ] { + let target_cflags = format!("CFLAGS_{target}: \"-Wno-error=date-time\""); + assert!( + workflow.contains(&target_cflags), + "zig cross builds must demote the mimalloc-pprof date-time error: {target_cflags}" + ); + } for job_limit in ["CARGO_BUILD_JOBS: \"1\"", "SOLDR_JOBS: \"1\""] { assert!( workflow.contains(job_limit), From e62cb9e4fe2b67bce31a18233e2e982c3fceca22 Mon Sep 17 00:00:00 2001 From: zackees Date: Tue, 25 Aug 2026 02:53:48 -0700 Subject: [PATCH 6/6] test(release): assert structured workflow policy --- .github/workflows/template_native_build.yml | 45 +++--- crates/fbuild-python/tests/pyo3_policy.rs | 160 +++++++++++++++++--- 2 files changed, 165 insertions(+), 40 deletions(-) diff --git a/.github/workflows/template_native_build.yml b/.github/workflows/template_native_build.yml index dbcc2a37d..5ceeafa29 100644 --- a/.github/workflows/template_native_build.yml +++ b/.github/workflows/template_native_build.yml @@ -41,28 +41,6 @@ env: CARGO_TERM_COLOR: always RUSTFLAGS: "-D warnings" FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" - # Native Windows release builds can be quiet for longer than soldr's - # default 30 minute cargo diagnostic watchdog while rustc/link.exe are - # still active. Match the release-binary step's 45 minute timeout so - # GitHub Actions owns the outer deadline instead of soldr killing the - # build early. - SOLDR_CARGO_WAIT_TIMEOUT_SECS: "2700" - # Six cold release lanes run concurrently on two-core hosted runners. - # Bound rustc and soldr concurrency so cross-target codegen stays within - # the runner memory limit instead of being killed by the host. - CARGO_BUILD_JOBS: "1" - SOLDR_JOBS: "1" - # cargo-zigbuild clears generic CFLAGS while constructing a cross-target - # environment. Use cc-rs's target-specific variables so the vendored - # mimalloc-pprof build keeps this narrow Zig diagnostic demotion. - CFLAGS_x86_64_unknown_linux_musl: "-Wno-error=date-time" - CFLAGS_aarch64_unknown_linux_musl: "-Wno-error=date-time" - CFLAGS_x86_64_apple_darwin: "-Wno-error=date-time" - CFLAGS_aarch64_apple_darwin: "-Wno-error=date-time" - # Keep every soldr invocation in this reusable job on the same catalogue. - # soldr >= 0.9.5 consumes its multipart v2 assets, including Apple SDKs - # whose legacy direct-LFS URLs are no longer anonymously downloadable. - SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain # cc-rs env vars for the xwin lanes. ring's build.rs uses cc-rs; # without these, cc-rs invokes plain `clang` and the curve25519.c # compile fails with `error: no such file or directory: '/imsvc'` @@ -87,6 +65,29 @@ jobs: name: Build (${{ inputs.target }}) runs-on: ${{ inputs.runner }} timeout-minutes: 90 + env: + # Native Windows release builds can be quiet for longer than soldr's + # default 30 minute cargo diagnostic watchdog while rustc/link.exe are + # still active. Match the release-binary step's 45 minute timeout so + # GitHub Actions owns the outer deadline instead of soldr killing the + # build early. + SOLDR_CARGO_WAIT_TIMEOUT_SECS: "2700" + # Six cold release lanes run concurrently on two-core hosted runners. + # Bound rustc and soldr concurrency so cross-target codegen stays within + # the runner memory limit instead of being killed by the host. + CARGO_BUILD_JOBS: "1" + SOLDR_JOBS: "1" + # cargo-zigbuild clears generic CFLAGS while constructing a cross-target + # environment. Use cc-rs's target-specific variables so the vendored + # mimalloc-pprof build keeps this narrow Zig diagnostic demotion. + CFLAGS_x86_64_unknown_linux_musl: "-Wno-error=date-time" + CFLAGS_aarch64_unknown_linux_musl: "-Wno-error=date-time" + CFLAGS_x86_64_apple_darwin: "-Wno-error=date-time" + CFLAGS_aarch64_apple_darwin: "-Wno-error=date-time" + # Keep every soldr invocation in this reusable job on the same catalogue. + # soldr >= 0.9.5 consumes its multipart v2 assets, including Apple SDKs + # whose legacy direct-LFS URLs are no longer anonymously downloadable. + SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain steps: - uses: actions/checkout@v6 diff --git a/crates/fbuild-python/tests/pyo3_policy.rs b/crates/fbuild-python/tests/pyo3_policy.rs index dabf96b4d..79d671a31 100644 --- a/crates/fbuild-python/tests/pyo3_policy.rs +++ b/crates/fbuild-python/tests/pyo3_policy.rs @@ -1,4 +1,4 @@ -use std::fs; +use std::{fs, ops::Range}; use fbuild_core::path::NormalizedPath; @@ -13,6 +13,116 @@ fn repo_root() -> NormalizedPath { ) } +fn yaml_indent(line: &str) -> usize { + line.len() - line.trim_start().len() +} + +fn yaml_mapping_entry(line: &str) -> Option<(&str, &str)> { + let content = line.trim_start(); + if content.is_empty() || content.starts_with(['#', '-']) { + return None; + } + let (key, value) = content.split_once(':')?; + Some((key.trim(), value.trim())) +} + +fn yaml_scalar(value: &str) -> &str { + value + .strip_prefix('"') + .and_then(|value| value.strip_suffix('"')) + .unwrap_or(value) +} + +fn yaml_significant(line: &str) -> bool { + let content = line.trim_start(); + !content.is_empty() && !content.starts_with('#') +} + +fn yaml_mapping_block_in_scope( + lines: &[&str], + mut scope: Range, + mut entry_indent: usize, + path: &[&str], +) -> Option<(Range, usize)> { + for key in path { + let entry = scope.clone().find(|&index| { + yaml_indent(lines[index]) == entry_indent + && yaml_mapping_entry(lines[index]) == Some((*key, "")) + })?; + let end = ((entry + 1)..scope.end) + .find(|&index| { + yaml_significant(lines[index]) && yaml_indent(lines[index]) <= entry_indent + }) + .unwrap_or(scope.end); + scope = (entry + 1)..end; + entry_indent += 2; + } + + Some((scope, entry_indent)) +} + +fn yaml_mapping_block(lines: &[&str], path: &[&str]) -> Option<(Range, usize)> { + yaml_mapping_block_in_scope(lines, 0..lines.len(), 0, path) +} + +fn yaml_mapping_value_in_scope<'a>( + lines: &[&'a str], + scope: Range, + entry_indent: usize, + path: &[&str], +) -> Option<&'a str> { + let (key, parent_path) = path.split_last()?; + let (scope, entry_indent) = + yaml_mapping_block_in_scope(lines, scope, entry_indent, parent_path)?; + scope + .filter_map(|index| { + (yaml_indent(lines[index]) == entry_indent) + .then(|| yaml_mapping_entry(lines[index])) + .flatten() + }) + .find_map(|(candidate, value)| (candidate == *key).then(|| yaml_scalar(value))) +} + +fn yaml_mapping_value<'a>(lines: &[&'a str], path: &[&str]) -> Option<&'a str> { + yaml_mapping_value_in_scope(lines, 0..lines.len(), 0, path) +} + +fn yaml_sequence_values<'a>(lines: &[&'a str], path: &[&str]) -> Option> { + let (scope, item_indent) = yaml_mapping_block(lines, path)?; + Some( + scope + .filter_map(|index| { + (yaml_indent(lines[index]) == item_indent) + .then(|| lines[index].trim_start().strip_prefix("- ")) + .flatten() + .map(yaml_scalar) + }) + .collect(), + ) +} + +fn yaml_step_mapping_value<'a>( + lines: &[&'a str], + step_name: &str, + path: &[&str], +) -> Option<&'a str> { + let (steps, item_indent) = yaml_mapping_block(lines, &["jobs", "build", "steps"])?; + let step = steps.clone().find(|&index| { + if yaml_indent(lines[index]) != item_indent { + return false; + } + let Some(item) = lines[index].trim_start().strip_prefix("- ") else { + return false; + }; + yaml_mapping_entry(item) + .is_some_and(|(key, value)| key == "name" && yaml_scalar(value) == step_name) + })?; + let step_end = ((step + 1)..steps.end) + .find(|&index| yaml_significant(lines[index]) && yaml_indent(lines[index]) <= item_indent) + .unwrap_or(steps.end); + yaml_mapping_value_in_scope(lines, (step + 1)..step_end, item_indent + 2, path) +} + #[test] fn pyo3_029_policy_stays_target_python_independent() { // FastLED/fbuild#1025: keep every cross-build branch explicit until @@ -101,14 +211,21 @@ fn native_release_workflow_uses_current_cross_toolchains() { fs::read_to_string(root.join(".github/workflows/template_native_build.yml")).unwrap(); let release_workflow = fs::read_to_string(root.join(".github/workflows/release-auto.yml")).unwrap(); + let workflow_lines = workflow.lines().collect::>(); + let release_workflow_lines = release_workflow.lines().collect::>(); - assert!( - workflow.contains("version: 0.9.6"), - "native release builds need soldr >= 0.9.5 for catalogue-v2 Apple SDK assets" + assert_eq!( + yaml_step_mapping_value(&workflow_lines, "Setup soldr", &["with", "version"]), + Some("0.9.6"), + "the setup-soldr step needs soldr >= 0.9.5 for catalogue-v2 Apple SDK assets" ); - assert!( - workflow.contains("SOLDR_TOOLCHAIN_ORIGIN: https://zackees.github.io/soldr-toolchain"), - "Apple SDK prepare and build steps must share the catalogue origin" + assert_eq!( + yaml_mapping_value( + &workflow_lines, + &["jobs", "build", "env", "SOLDR_TOOLCHAIN_ORIGIN"] + ), + Some("https://zackees.github.io/soldr-toolchain"), + "Apple SDK prepare and build steps must share a job-scoped catalogue origin" ); for target in [ "x86_64_unknown_linux_musl", @@ -116,24 +233,31 @@ fn native_release_workflow_uses_current_cross_toolchains() { "x86_64_apple_darwin", "aarch64_apple_darwin", ] { - let target_cflags = format!("CFLAGS_{target}: \"-Wno-error=date-time\""); - assert!( - workflow.contains(&target_cflags), - "zig cross builds must demote the mimalloc-pprof date-time error: {target_cflags}" + let target_cflags = format!("CFLAGS_{target}"); + assert_eq!( + yaml_mapping_value( + &workflow_lines, + &["jobs", "build", "env", target_cflags.as_str()] + ), + Some("-Wno-error=date-time"), + "zig cross builds need a job-scoped mimalloc-pprof diagnostic override: {target_cflags}" ); } - for job_limit in ["CARGO_BUILD_JOBS: \"1\"", "SOLDR_JOBS: \"1\""] { - assert!( - workflow.contains(job_limit), - "native release lanes must stay below hosted-runner memory limits: {job_limit}" + for job_limit in ["CARGO_BUILD_JOBS", "SOLDR_JOBS"] { + assert_eq!( + yaml_mapping_value(&workflow_lines, &["jobs", "build", "env", job_limit]), + Some("1"), + "native release lanes need a job-scoped hosted-runner memory limit: {job_limit}" ); } + let release_paths = yaml_sequence_values(&release_workflow_lines, &["on", "push", "paths"]) + .expect("release workflow must define on.push.paths"); for release_input in [ - "- .github/workflows/release-auto.yml", - "- .github/workflows/template_native_build.yml", + ".github/workflows/release-auto.yml", + ".github/workflows/template_native_build.yml", ] { assert!( - release_workflow.contains(release_input), + release_paths.contains(&release_input), "release workflow fixes must retrigger an incomplete publication: {release_input}" ); }