From 9f4cc4dda4d683a5b2ba82344be221cbdf1615d4 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 23 Sep 2026 04:53:17 -0700 Subject: [PATCH 1/6] ci: select fractional board validation and gate releases --- .github/workflows/README.md | 43 +- .github/workflows/acceptance-205.yml | 21 +- .github/workflows/bench-205.yml | 17 +- .github/workflows/build-apollo3_red.yml | 12 - .../build-apollo3_thing_explorable.yml | 12 - .github/workflows/build-atmega8.yml | 12 - .github/workflows/build-atmega8a.yml | 12 - .github/workflows/build-attiny1604.yml | 12 - .github/workflows/build-attiny1616.yml | 12 - .github/workflows/build-attiny4313.yml | 12 - .github/workflows/build-attiny85.yml | 12 - .github/workflows/build-attiny88.yml | 12 - .github/workflows/build-blackpill.yml | 12 - .github/workflows/build-bluepill.yml | 12 - .github/workflows/build-ch32l103.yml | 12 - .github/workflows/build-ch32v003.yml | 12 - .github/workflows/build-ch32v006.yml | 12 - .github/workflows/build-ch32v103.yml | 12 - .github/workflows/build-ch32v203.yml | 12 - .github/workflows/build-ch32v208.yml | 12 - .github/workflows/build-ch32v303.yml | 12 - .github/workflows/build-ch32v307.yml | 12 - .github/workflows/build-ch32x035.yml | 12 - .github/workflows/build-clearcore.yml | 12 - .github/workflows/build-due.yml | 12 - .github/workflows/build-esp32c2.yml | 12 - .github/workflows/build-esp32c3.yml | 12 - .github/workflows/build-esp32c5.yml | 12 - .github/workflows/build-esp32c6.yml | 12 - .github/workflows/build-esp32dev.yml | 62 -- .github/workflows/build-esp32h2.yml | 12 - .github/workflows/build-esp32p4.yml | 12 - .github/workflows/build-esp32s2.yml | 12 - .github/workflows/build-esp32s3.yml | 12 - .github/workflows/build-esp8266.yml | 12 - .github/workflows/build-giga-r1.yml | 12 - .github/workflows/build-leonardo.yml | 12 - .github/workflows/build-lpc804.yml | 14 - .github/workflows/build-lpc845.yml | 14 - .github/workflows/build-lpc845brk.yml | 14 - .github/workflows/build-lpcxpresso804.yml | 14 - .github/workflows/build-lpcxpresso845max.yml | 14 - .github/workflows/build-matrix_portal_m4.yml | 12 - .github/workflows/build-mgm240.yml | 12 - .github/workflows/build-nano-every.yml | 12 - .github/workflows/build-nano_every.yml | 12 - .../workflows/build-nice_nano_nrf52840.yml | 12 - .github/workflows/build-nrf52840-sense.yml | 12 - .github/workflows/build-nrf52840_dk.yml | 12 - .github/workflows/build-nrfmicro_nrf52840.yml | 12 - .github/workflows/build-nucleo-f429zi.yml | 12 - .github/workflows/build-nucleo-f439zi.yml | 12 - .github/workflows/build-nucleo_f429zi.yml | 12 - .github/workflows/build-nucleo_f439zi.yml | 12 - .github/workflows/build-qtpy_m0.yml | 12 - .github/workflows/build-rp2040.yml | 12 - .github/workflows/build-rp2350.yml | 12 - .github/workflows/build-rpipico.yml | 12 - .github/workflows/build-rpipico2.yml | 12 - .github/workflows/build-sam3x8e_due.yml | 12 - .github/workflows/build-samd21.yml | 12 - .github/workflows/build-samd21_zero.yml | 12 - .github/workflows/build-samd51j.yml | 12 - .github/workflows/build-samd51p.yml | 12 - .github/workflows/build-stm32f103c8.yml | 12 - .github/workflows/build-stm32f103cb.yml | 12 - .github/workflows/build-stm32f103tb.yml | 12 - .github/workflows/build-stm32f411ce.yml | 12 - .github/workflows/build-stm32h747xi.yml | 12 - .../workflows/build-supermini_nrf52840.yml | 12 - .github/workflows/build-teensy30.yml | 12 - .github/workflows/build-teensy31.yml | 12 - .github/workflows/build-teensy32.yml | 12 - .github/workflows/build-teensy35.yml | 12 - .github/workflows/build-teensy36.yml | 12 - .github/workflows/build-teensy40.yml | 12 - .github/workflows/build-teensy41.yml | 62 -- .github/workflows/build-teensylc.yml | 12 - .github/workflows/build-thingplusmatter.yml | 12 - .github/workflows/build-tinystm.yml | 12 - .github/workflows/build-uno-r4-wifi.yml | 12 - .github/workflows/build-uno.yml | 62 -- .github/workflows/build-uno_r4_wifi.yml | 12 - .github/workflows/check-ubuntu.yml | 18 +- .github/workflows/check-windows.yml | 12 +- .github/workflows/ci-full.yml | 631 ++++++++++++++++++ .github/workflows/ci-minimal.yml | 53 ++ .github/workflows/ci-test.yml | 106 +++ .github/workflows/crate-gate.yml | 10 +- .github/workflows/docs.yml | 10 +- .github/workflows/dylint.yml | 47 +- .github/workflows/fmt.yml | 10 +- .github/workflows/msrv.yml | 10 +- .github/workflows/nightly-platforms.yml | 92 ++- .github/workflows/qemu-linux-runtime.yml | 21 +- .github/workflows/release-auto.yml | 120 ++-- .github/workflows/template_build.yml | 6 + .github/workflows/validate-boards.yml | 10 +- CLAUDE.md | 8 +- ci/board_families.json | 3 +- ci/render_workflows.py | 265 +++++++- ci/test_fractional_workflows.py | 209 ++++++ docs/RELEASING.md | 39 +- 103 files changed, 1604 insertions(+), 1277 deletions(-) create mode 100644 .github/workflows/ci-full.yml create mode 100644 .github/workflows/ci-minimal.yml create mode 100644 .github/workflows/ci-test.yml create mode 100644 ci/test_fractional_workflows.py diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 6c5af3b69..c75550601 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -2,9 +2,34 @@ CI/CD workflows for the fbuild project, covering lint, test, documentation, and binary builds. -## CI Checks (push/PR) - -- **`check-{ubuntu,windows}.yml`** -- Clippy + tests per platform (no macOS runner: macOS +## CI tiers + +- `ci-minimal.yml` runs on ordinary PRs and `main` pushes: Linux workspace + check, Clippy, tests, and the existing small independent policy gates below. +- Add the `ci-test` PR label (or dispatch manually) for the Linux check plus + the Arduino Uno quick/release firmware build. +- Add the `ci-full` PR label (or dispatch manually) for all boards in + `ci/board_families.json`, Linux and Windows workspace checks, Dylint, + acceptance, benchmarks, QEMU runtime, fmt, docs, MSRV, board validation, + and crate gates. The release workflow calls this tier at its selected + commit ref before publication. +- `ci-minimal` owns the PR status `CI selected coverage`. It runs the selected + reusable tiers when labels are present, and fails if a selected tier is + skipped, fails, or reports incomplete full coverage. Require this stable + status for ordinary PRs alongside the independent fmt, docs, MSRV, board + validation, and crate-gate statuses. The unselected tiers allocate no PR + runners. The selected tier owns the Linux workspace check; when both labels + are present, `ci-full` subsumes `ci-test`. +- The full and nightly board matrices execute identical build inputs once. + Each matrix row carries `workflow_aliases` so every listed board workflow + remains represented, including legacy spelling aliases. +- Per-board `build-*.yml` files are manual/reusable only. Their triggers are + generated by `ci/render_workflows.py`; the nightly sweep still runs every + board independently of PR labels. + +## Independent lightweight checks (push/PR) + +- **`check-{ubuntu,windows}.yml`** -- Reusable Clippy + tests per platform (no macOS runner: macOS binaries are cross-built from Linux in the release matrix, see below) - **`fmt.yml`** -- Rustfmt check | **`docs.yml`** -- Doc build with `-D warnings` - **`msrv.yml`** -- MSRV 1.95.0 verification | **`validate-boards.yml`** -- Board JSON validation @@ -43,7 +68,7 @@ Exempt, with the reason recorded in `ci/check_workflow_concurrency.py`: would share one group and cancel each other), `hw-ci.yml` (cancelling mid-flash can wedge real hardware), and `add-to-project.yml` (fires once on PR open). -Per-board files get the block from `ci/render_workflows.py`; everything else +Per-board files and the CI tier workflows get the block from `ci/render_workflows.py`; everything else carries it by hand. `ci-workflow-drift.yml` enforces both halves via `ci/check_workflow_concurrency.py` -- a new PR-triggered workflow without the block fails CI with a copy-paste fix. @@ -53,7 +78,7 @@ block fails CI with a copy-paste fix. - **`benchmark-build-comparison.yml`** -- Arduino CLI vs PlatformIO vs fbuild Blink cold/warm benchmark; runs nightly, manually, and for relevant pushes to `main`, then force-publishes the one-commit `benchmark-stats` branch and deploys its site to GitHub Pages - **`esp32s3-size-parity.yml`** -- builds an ESP32-S3 Blink with PlatformIO and fbuild on the same pinned pioarduino platform and fails if fbuild's `firmware.bin` is larger (#1432); runs once a day and manually, never on push/PR -## Per-Board Builds (push/PR) +## Per-Board Builds (manual/reusable, called by CI tiers) - **`build-esp32{c2,c3,c5,c6,dev,h2,p4,s2,s3}.yml`** -- ESP32 variants - **`build-esp8266.yml`** -- ESP8266 @@ -115,7 +140,13 @@ For Windows artifacts, verify `fbuild.exe`, `fbuild-daemon.exe`, and ### Autonomous Releases -`release-auto.yml` follows the attested release pattern used by `soldr`: +`release-auto.yml` is dispatched explicitly with a required exact candidate SHA; +ordinary version-file pushes never start it. It follows the attested release +pattern used by `soldr`: + +Publication is presently fail-closed at `runtime-coverage`: no trusted same-SHA +physical-board result covers every supported platform. Dry-run dispatches remain +usable; `publish=true` cannot create a tag or upload until that CI proof is wired. - reads the workspace/package version from `Cargo.toml` and `pyproject.toml` - skips the run if the tag already exists or PyPI already has that version diff --git a/.github/workflows/acceptance-205.yml b/.github/workflows/acceptance-205.yml index 10d584ea4..0fe0d5ba4 100644 --- a/.github/workflows/acceptance-205.yml +++ b/.github/workflows/acceptance-205.yml @@ -4,23 +4,16 @@ on: # Don't run on every PR -- these tests take 10+ minutes each because they # download Teensyduino + arm-gcc + STM32duino. Trigger explicitly. workflow_dispatch: + workflow_call: + inputs: + ref: + type: string + required: false + default: "" schedule: # Nightly at 04:00 UTC. Catches drift in Teensyduino / STM32duino # framework downloads and fbuild's resolver behaviour. - cron: '0 4 * * *' - pull_request: - paths: - # Run when something that could affect resolver behaviour changes. - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'crates/fbuild-build/src/framework_libs.rs' - - 'crates/fbuild-build/src/teensy/**' - - 'crates/fbuild-build/src/stm32/**' - - 'crates/fbuild-build/tests/teensylc_acceptance.rs' - - 'crates/fbuild-build/tests/teensy30_acceptance.rs' - - 'crates/fbuild-build/tests/teensy41_acceptance.rs' - - 'crates/fbuild-build/tests/stm32_acceptance.rs' - - '.github/workflows/acceptance-205.yml' # Auto-cancel superseded PR runs: pushing again to a feature branch # supersedes the in-flight run instead of queueing behind it. Non-PR @@ -58,6 +51,8 @@ jobs: test_fn: teensy41_cold_library_selection_meets_205_ac6 steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - uses: astral-sh/setup-uv@v3 - name: Setup soldr uses: zackees/setup-soldr@v0 diff --git a/.github/workflows/bench-205.yml b/.github/workflows/bench-205.yml index 662cbf5b0..7d2ec2230 100644 --- a/.github/workflows/bench-205.yml +++ b/.github/workflows/bench-205.yml @@ -2,13 +2,12 @@ name: Perf benches (#205) on: workflow_dispatch: - pull_request: - paths: - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'crates/fbuild-test-support/src/mini_framework.rs' - - 'bench/fastled-examples/**' - - '.github/workflows/bench-205.yml' + workflow_call: + inputs: + ref: + type: string + required: false + default: "" # Auto-cancel superseded PR runs: pushing again to a feature branch # supersedes the in-flight run instead of queueing behind it. Non-PR @@ -42,6 +41,8 @@ jobs: target: P-01-mini (warm cache against MiniFramework, baseline TBD) steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - uses: astral-sh/setup-uv@v3 - name: Setup soldr uses: zackees/setup-soldr@v0 @@ -82,6 +83,8 @@ jobs: timeout-minutes: 30 steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - uses: astral-sh/setup-uv@v3 - name: Setup soldr uses: zackees/setup-soldr@v0 diff --git a/.github/workflows/build-apollo3_red.yml b/.github/workflows/build-apollo3_red.yml index 14345a8d1..26146948f 100644 --- a/.github/workflows/build-apollo3_red.yml +++ b/.github/workflows/build-apollo3_red.yml @@ -4,18 +4,6 @@ name: Build Apollo3 RedBoard on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/apollo3_red/**' - - 'crates/fbuild-build-arm/src/apollo3/**' - - '.github/workflows/build-apollo3_red.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/apollo3_red/**' - - 'crates/fbuild-build-arm/src/apollo3/**' - - '.github/workflows/build-apollo3_red.yml' concurrency: group: build-apollo3_red.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-apollo3_thing_explorable.yml b/.github/workflows/build-apollo3_thing_explorable.yml index cd332a8ae..9c99f9502 100644 --- a/.github/workflows/build-apollo3_thing_explorable.yml +++ b/.github/workflows/build-apollo3_thing_explorable.yml @@ -4,18 +4,6 @@ name: Build Apollo3 expLoRaBLE on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/apollo3_thing_explorable/**' - - 'crates/fbuild-build-arm/src/apollo3/**' - - '.github/workflows/build-apollo3_thing_explorable.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/apollo3_thing_explorable/**' - - 'crates/fbuild-build-arm/src/apollo3/**' - - '.github/workflows/build-apollo3_thing_explorable.yml' concurrency: group: build-apollo3_thing_explorable.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-atmega8.yml b/.github/workflows/build-atmega8.yml index 7354dcd99..09794c498 100644 --- a/.github/workflows/build-atmega8.yml +++ b/.github/workflows/build-atmega8.yml @@ -4,18 +4,6 @@ name: Build ATmega8 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/atmega8/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-atmega8.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/atmega8/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-atmega8.yml' concurrency: group: build-atmega8.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-atmega8a.yml b/.github/workflows/build-atmega8a.yml index a0abed180..cce5ff674 100644 --- a/.github/workflows/build-atmega8a.yml +++ b/.github/workflows/build-atmega8a.yml @@ -4,18 +4,6 @@ name: Build ATmega8A on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/atmega8a/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-atmega8a.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/atmega8a/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-atmega8a.yml' concurrency: group: build-atmega8a.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-attiny1604.yml b/.github/workflows/build-attiny1604.yml index 60b427073..70c28fabc 100644 --- a/.github/workflows/build-attiny1604.yml +++ b/.github/workflows/build-attiny1604.yml @@ -4,18 +4,6 @@ name: Build ATtiny1604 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/attiny1604/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny1604.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/attiny1604/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny1604.yml' concurrency: group: build-attiny1604.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-attiny1616.yml b/.github/workflows/build-attiny1616.yml index 917e636bc..72c05001a 100644 --- a/.github/workflows/build-attiny1616.yml +++ b/.github/workflows/build-attiny1616.yml @@ -4,18 +4,6 @@ name: Build ATtiny1616 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/attiny1616/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny1616.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/attiny1616/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny1616.yml' concurrency: group: build-attiny1616.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-attiny4313.yml b/.github/workflows/build-attiny4313.yml index 20ff5cd3e..e1523d908 100644 --- a/.github/workflows/build-attiny4313.yml +++ b/.github/workflows/build-attiny4313.yml @@ -4,18 +4,6 @@ name: Build ATtiny4313 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/attiny4313/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny4313.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/attiny4313/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny4313.yml' concurrency: group: build-attiny4313.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-attiny85.yml b/.github/workflows/build-attiny85.yml index e960c3c18..8f11bebf8 100644 --- a/.github/workflows/build-attiny85.yml +++ b/.github/workflows/build-attiny85.yml @@ -4,18 +4,6 @@ name: Build ATtiny85 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/attiny85/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny85.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/attiny85/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny85.yml' concurrency: group: build-attiny85.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-attiny88.yml b/.github/workflows/build-attiny88.yml index 4c1b16708..41c7d69fd 100644 --- a/.github/workflows/build-attiny88.yml +++ b/.github/workflows/build-attiny88.yml @@ -4,18 +4,6 @@ name: Build ATtiny88 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/attiny88/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny88.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/attiny88/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-attiny88.yml' concurrency: group: build-attiny88.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-blackpill.yml b/.github/workflows/build-blackpill.yml index 9a8692a9f..3483befeb 100644 --- a/.github/workflows/build-blackpill.yml +++ b/.github/workflows/build-blackpill.yml @@ -4,18 +4,6 @@ name: Build BlackPill F411CE on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/blackpill_f411ce/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-blackpill.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/blackpill_f411ce/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-blackpill.yml' concurrency: group: build-blackpill.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-bluepill.yml b/.github/workflows/build-bluepill.yml index e73b35f30..45794dbc3 100644 --- a/.github/workflows/build-bluepill.yml +++ b/.github/workflows/build-bluepill.yml @@ -4,18 +4,6 @@ name: Build BluePill F103C8 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/bluepill_f103c8/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-bluepill.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/bluepill_f103c8/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-bluepill.yml' concurrency: group: build-bluepill.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32l103.yml b/.github/workflows/build-ch32l103.yml index 5cee5350a..65a606ddb 100644 --- a/.github/workflows/build-ch32l103.yml +++ b/.github/workflows/build-ch32l103.yml @@ -4,18 +4,6 @@ name: Build CH32L103 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32l103/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32l103.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32l103/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32l103.yml' concurrency: group: build-ch32l103.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32v003.yml b/.github/workflows/build-ch32v003.yml index fe1960fa0..1a68c574d 100644 --- a/.github/workflows/build-ch32v003.yml +++ b/.github/workflows/build-ch32v003.yml @@ -4,18 +4,6 @@ name: Build CH32V003 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32v003/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v003.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32v003/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v003.yml' concurrency: group: build-ch32v003.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32v006.yml b/.github/workflows/build-ch32v006.yml index ee86567d8..5efb0d60d 100644 --- a/.github/workflows/build-ch32v006.yml +++ b/.github/workflows/build-ch32v006.yml @@ -4,18 +4,6 @@ name: Build CH32V006 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32v006/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v006.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32v006/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v006.yml' concurrency: group: build-ch32v006.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32v103.yml b/.github/workflows/build-ch32v103.yml index 768722e20..eb9ef51c8 100644 --- a/.github/workflows/build-ch32v103.yml +++ b/.github/workflows/build-ch32v103.yml @@ -4,18 +4,6 @@ name: Build CH32V103 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32v103/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v103.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32v103/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v103.yml' concurrency: group: build-ch32v103.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32v203.yml b/.github/workflows/build-ch32v203.yml index 97c231ae9..29b3c6f71 100644 --- a/.github/workflows/build-ch32v203.yml +++ b/.github/workflows/build-ch32v203.yml @@ -4,18 +4,6 @@ name: Build CH32V203 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32v203/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v203.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32v203/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v203.yml' concurrency: group: build-ch32v203.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32v208.yml b/.github/workflows/build-ch32v208.yml index 54d82a975..eae5d1c63 100644 --- a/.github/workflows/build-ch32v208.yml +++ b/.github/workflows/build-ch32v208.yml @@ -4,18 +4,6 @@ name: Build CH32V208 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32v208/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v208.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32v208/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v208.yml' concurrency: group: build-ch32v208.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32v303.yml b/.github/workflows/build-ch32v303.yml index 7fda3c987..fb0e13d18 100644 --- a/.github/workflows/build-ch32v303.yml +++ b/.github/workflows/build-ch32v303.yml @@ -4,18 +4,6 @@ name: Build CH32V303 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32v303/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v303.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32v303/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v303.yml' concurrency: group: build-ch32v303.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32v307.yml b/.github/workflows/build-ch32v307.yml index 7636a8692..dc17f35a2 100644 --- a/.github/workflows/build-ch32v307.yml +++ b/.github/workflows/build-ch32v307.yml @@ -4,18 +4,6 @@ name: Build CH32V307 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32v307/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v307.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32v307/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32v307.yml' concurrency: group: build-ch32v307.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-ch32x035.yml b/.github/workflows/build-ch32x035.yml index a9648de02..0ab6852b0 100644 --- a/.github/workflows/build-ch32x035.yml +++ b/.github/workflows/build-ch32x035.yml @@ -4,18 +4,6 @@ name: Build CH32X035 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/ch32x035/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32x035.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/ch32x035/**' - - 'crates/fbuild-build-mcu/src/ch32v/**' - - '.github/workflows/build-ch32x035.yml' concurrency: group: build-ch32x035.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-clearcore.yml b/.github/workflows/build-clearcore.yml index 8242b436b..ebd71d32e 100644 --- a/.github/workflows/build-clearcore.yml +++ b/.github/workflows/build-clearcore.yml @@ -4,18 +4,6 @@ name: Build Teknic ClearCore (SAME53) on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/clearcore/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-clearcore.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/clearcore/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-clearcore.yml' concurrency: group: build-clearcore.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-due.yml b/.github/workflows/build-due.yml index 2801846eb..537609da4 100644 --- a/.github/workflows/build-due.yml +++ b/.github/workflows/build-due.yml @@ -4,18 +4,6 @@ name: Build Arduino Due on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/due/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-due.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/due/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-due.yml' concurrency: group: build-due.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32c2.yml b/.github/workflows/build-esp32c2.yml index c5b32e6f5..3d320f69b 100644 --- a/.github/workflows/build-esp32c2.yml +++ b/.github/workflows/build-esp32c2.yml @@ -4,18 +4,6 @@ name: Build ESP32-C2 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32c2/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c2.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32c2/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c2.yml' concurrency: group: build-esp32c2.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32c3.yml b/.github/workflows/build-esp32c3.yml index 62c95e0a4..da8f6fd32 100644 --- a/.github/workflows/build-esp32c3.yml +++ b/.github/workflows/build-esp32c3.yml @@ -4,18 +4,6 @@ name: Build ESP32-C3 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32c3/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c3.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32c3/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c3.yml' concurrency: group: build-esp32c3.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32c5.yml b/.github/workflows/build-esp32c5.yml index bb4238a07..7119efbc7 100644 --- a/.github/workflows/build-esp32c5.yml +++ b/.github/workflows/build-esp32c5.yml @@ -4,18 +4,6 @@ name: Build ESP32-C5 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32c5/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c5.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32c5/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c5.yml' concurrency: group: build-esp32c5.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32c6.yml b/.github/workflows/build-esp32c6.yml index de0b09a4c..0141db299 100644 --- a/.github/workflows/build-esp32c6.yml +++ b/.github/workflows/build-esp32c6.yml @@ -4,18 +4,6 @@ name: Build ESP32-C6 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32c6/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c6.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32c6/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32c6.yml' concurrency: group: build-esp32c6.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32dev.yml b/.github/workflows/build-esp32dev.yml index a00dabe7e..8428b4374 100644 --- a/.github/workflows/build-esp32dev.yml +++ b/.github/workflows/build-esp32dev.yml @@ -4,68 +4,6 @@ name: Build ESP32 Dev on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32dev/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - 'crates/fbuild-build/**' - - 'crates/fbuild-build/Cargo.toml' - - 'crates/fbuild-cli/**' - - 'crates/fbuild-daemon/**' - - 'crates/fbuild-core/**' - - 'crates/fbuild-paths/**' - - 'crates/fbuild-config/**' - - 'crates/fbuild-deploy/**' - - 'crates/fbuild-packages/**' - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'rust-toolchain.toml' - - '.github/workflows/template_build.yml' - - '.github/workflows/ci-workflow-drift.yml' - - 'ci/render_workflows.py' - - 'ci/board_families.json' - - 'ci/ci_common_paths.txt' - - 'crates/fbuild-build-engine/**' - - 'crates/fbuild-build-mcu/src/lib.rs' - - 'crates/fbuild-build-mcu/*argo.toml' - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-library/**' - - 'crates/fbuild-packages-fetch/**' - - '.github/workflows/build-esp32dev.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32dev/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - 'crates/fbuild-build/**' - - 'crates/fbuild-build/Cargo.toml' - - 'crates/fbuild-cli/**' - - 'crates/fbuild-daemon/**' - - 'crates/fbuild-core/**' - - 'crates/fbuild-paths/**' - - 'crates/fbuild-config/**' - - 'crates/fbuild-deploy/**' - - 'crates/fbuild-packages/**' - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'rust-toolchain.toml' - - '.github/workflows/template_build.yml' - - '.github/workflows/ci-workflow-drift.yml' - - 'ci/render_workflows.py' - - 'ci/board_families.json' - - 'ci/ci_common_paths.txt' - - 'crates/fbuild-build-engine/**' - - 'crates/fbuild-build-mcu/src/lib.rs' - - 'crates/fbuild-build-mcu/*argo.toml' - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-library/**' - - 'crates/fbuild-packages-fetch/**' - - '.github/workflows/build-esp32dev.yml' concurrency: group: build-esp32dev.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32h2.yml b/.github/workflows/build-esp32h2.yml index 8d6dde27c..8cc34a152 100644 --- a/.github/workflows/build-esp32h2.yml +++ b/.github/workflows/build-esp32h2.yml @@ -4,18 +4,6 @@ name: Build ESP32-H2 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32h2/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32h2.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32h2/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32h2.yml' concurrency: group: build-esp32h2.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32p4.yml b/.github/workflows/build-esp32p4.yml index ce94ecfe0..48bca262e 100644 --- a/.github/workflows/build-esp32p4.yml +++ b/.github/workflows/build-esp32p4.yml @@ -4,18 +4,6 @@ name: Build ESP32-P4 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32p4/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32p4.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32p4/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32p4.yml' concurrency: group: build-esp32p4.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32s2.yml b/.github/workflows/build-esp32s2.yml index cd91ddc26..45f32d47e 100644 --- a/.github/workflows/build-esp32s2.yml +++ b/.github/workflows/build-esp32s2.yml @@ -4,18 +4,6 @@ name: Build ESP32-S2 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32s2/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32s2.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32s2/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32s2.yml' concurrency: group: build-esp32s2.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp32s3.yml b/.github/workflows/build-esp32s3.yml index d41d86b10..a91fbe894 100644 --- a/.github/workflows/build-esp32s3.yml +++ b/.github/workflows/build-esp32s3.yml @@ -4,18 +4,6 @@ name: Build ESP32-S3 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp32s3/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32s3.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp32s3/**' - - 'crates/fbuild-build-esp/src/esp32/**' - - '.github/workflows/build-esp32s3.yml' concurrency: group: build-esp32s3.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-esp8266.yml b/.github/workflows/build-esp8266.yml index b80cbad34..1c3d36615 100644 --- a/.github/workflows/build-esp8266.yml +++ b/.github/workflows/build-esp8266.yml @@ -4,18 +4,6 @@ name: Build ESP8266 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/esp8266/**' - - 'crates/fbuild-build-esp/src/esp8266/**' - - '.github/workflows/build-esp8266.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/esp8266/**' - - 'crates/fbuild-build-esp/src/esp8266/**' - - '.github/workflows/build-esp8266.yml' concurrency: group: build-esp8266.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-giga-r1.yml b/.github/workflows/build-giga-r1.yml index 793eacde9..0add7bd96 100644 --- a/.github/workflows/build-giga-r1.yml +++ b/.github/workflows/build-giga-r1.yml @@ -4,18 +4,6 @@ name: Build Arduino Giga R1 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/giga_r1_m7/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-giga-r1.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/giga_r1_m7/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-giga-r1.yml' concurrency: group: build-giga-r1.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-leonardo.yml b/.github/workflows/build-leonardo.yml index 9cc79b199..b818564fa 100644 --- a/.github/workflows/build-leonardo.yml +++ b/.github/workflows/build-leonardo.yml @@ -4,18 +4,6 @@ name: Build Leonardo on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/leonardo/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-leonardo.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/leonardo/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-leonardo.yml' concurrency: group: build-leonardo.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-lpc804.yml b/.github/workflows/build-lpc804.yml index c64375109..2f234ef6b 100644 --- a/.github/workflows/build-lpc804.yml +++ b/.github/workflows/build-lpc804.yml @@ -4,20 +4,6 @@ name: Build LPC804 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/lpc804/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpc804.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/lpc804/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpc804.yml' concurrency: group: build-lpc804.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-lpc845.yml b/.github/workflows/build-lpc845.yml index eb5367990..7e1b350c1 100644 --- a/.github/workflows/build-lpc845.yml +++ b/.github/workflows/build-lpc845.yml @@ -4,20 +4,6 @@ name: Build LPC845 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/lpc845/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpc845.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/lpc845/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpc845.yml' concurrency: group: build-lpc845.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-lpc845brk.yml b/.github/workflows/build-lpc845brk.yml index e15f19e3f..f63b6900a 100644 --- a/.github/workflows/build-lpc845brk.yml +++ b/.github/workflows/build-lpc845brk.yml @@ -4,20 +4,6 @@ name: Build LPC845-BRK on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/lpc845brk/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpc845brk.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/lpc845brk/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpc845brk.yml' concurrency: group: build-lpc845brk.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-lpcxpresso804.yml b/.github/workflows/build-lpcxpresso804.yml index fa719c8bb..59b37032e 100644 --- a/.github/workflows/build-lpcxpresso804.yml +++ b/.github/workflows/build-lpcxpresso804.yml @@ -4,20 +4,6 @@ name: Build LPCXpresso804 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/lpcxpresso804/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpcxpresso804.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/lpcxpresso804/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpcxpresso804.yml' concurrency: group: build-lpcxpresso804.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-lpcxpresso845max.yml b/.github/workflows/build-lpcxpresso845max.yml index a13f1c6d2..8986905d5 100644 --- a/.github/workflows/build-lpcxpresso845max.yml +++ b/.github/workflows/build-lpcxpresso845max.yml @@ -4,20 +4,6 @@ name: Build LPCXpresso845-MAX on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/lpcxpresso845max/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpcxpresso845max.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/lpcxpresso845max/**' - - 'crates/fbuild-build-arm/src/nxplpc/**' - - 'crates/fbuild-build-arm/src/generic_arm/**' - - '.github/workflows/build-lpcxpresso845max.yml' concurrency: group: build-lpcxpresso845max.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-matrix_portal_m4.yml b/.github/workflows/build-matrix_portal_m4.yml index 77c2d6e6b..1567a3b21 100644 --- a/.github/workflows/build-matrix_portal_m4.yml +++ b/.github/workflows/build-matrix_portal_m4.yml @@ -4,18 +4,6 @@ name: Build Matrix Portal M4 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/matrix_portal_m4/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-matrix_portal_m4.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/matrix_portal_m4/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-matrix_portal_m4.yml' concurrency: group: build-matrix_portal_m4.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-mgm240.yml b/.github/workflows/build-mgm240.yml index 0cf3fd85e..57d4eb74e 100644 --- a/.github/workflows/build-mgm240.yml +++ b/.github/workflows/build-mgm240.yml @@ -4,18 +4,6 @@ name: Build MGM240 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/mgm240/**' - - 'crates/fbuild-build-arm/src/silabs/**' - - '.github/workflows/build-mgm240.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/mgm240/**' - - 'crates/fbuild-build-arm/src/silabs/**' - - '.github/workflows/build-mgm240.yml' concurrency: group: build-mgm240.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nano-every.yml b/.github/workflows/build-nano-every.yml index 2043a4253..967de7ebd 100644 --- a/.github/workflows/build-nano-every.yml +++ b/.github/workflows/build-nano-every.yml @@ -4,18 +4,6 @@ name: Build Nano Every on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nano_every/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-nano-every.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nano_every/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-nano-every.yml' concurrency: group: build-nano-every.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nano_every.yml b/.github/workflows/build-nano_every.yml index e11687790..bc19075f2 100644 --- a/.github/workflows/build-nano_every.yml +++ b/.github/workflows/build-nano_every.yml @@ -4,18 +4,6 @@ name: Build Nano Every on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nano_every/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-nano_every.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nano_every/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - '.github/workflows/build-nano_every.yml' concurrency: group: build-nano_every.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nice_nano_nrf52840.yml b/.github/workflows/build-nice_nano_nrf52840.yml index 09a71ba21..9eaba5940 100644 --- a/.github/workflows/build-nice_nano_nrf52840.yml +++ b/.github/workflows/build-nice_nano_nrf52840.yml @@ -4,18 +4,6 @@ name: Build nice!nano nRF52840 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nice_nano_nrf52840/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nice_nano_nrf52840.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nice_nano_nrf52840/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nice_nano_nrf52840.yml' concurrency: group: build-nice_nano_nrf52840.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nrf52840-sense.yml b/.github/workflows/build-nrf52840-sense.yml index 09ed8182e..b3a9815d7 100644 --- a/.github/workflows/build-nrf52840-sense.yml +++ b/.github/workflows/build-nrf52840-sense.yml @@ -4,18 +4,6 @@ name: Build Adafruit Feather NRF52840 Sense on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/adafruit_feather_nrf52840_sense/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nrf52840-sense.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/adafruit_feather_nrf52840_sense/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nrf52840-sense.yml' concurrency: group: build-nrf52840-sense.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nrf52840_dk.yml b/.github/workflows/build-nrf52840_dk.yml index 6a0ca2279..14ce12978 100644 --- a/.github/workflows/build-nrf52840_dk.yml +++ b/.github/workflows/build-nrf52840_dk.yml @@ -4,18 +4,6 @@ name: Build nRF52840 DK on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nrf52840_dk/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nrf52840_dk.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nrf52840_dk/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nrf52840_dk.yml' concurrency: group: build-nrf52840_dk.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nrfmicro_nrf52840.yml b/.github/workflows/build-nrfmicro_nrf52840.yml index 5f5d475cb..59e2e0b8c 100644 --- a/.github/workflows/build-nrfmicro_nrf52840.yml +++ b/.github/workflows/build-nrfmicro_nrf52840.yml @@ -4,18 +4,6 @@ name: Build nRFMicro nRF52840 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nrfmicro_nrf52840/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nrfmicro_nrf52840.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nrfmicro_nrf52840/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-nrfmicro_nrf52840.yml' concurrency: group: build-nrfmicro_nrf52840.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nucleo-f429zi.yml b/.github/workflows/build-nucleo-f429zi.yml index 4536acae4..549d5cae4 100644 --- a/.github/workflows/build-nucleo-f429zi.yml +++ b/.github/workflows/build-nucleo-f429zi.yml @@ -4,18 +4,6 @@ name: Build Nucleo F429ZI on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nucleo_f429zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo-f429zi.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nucleo_f429zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo-f429zi.yml' concurrency: group: build-nucleo-f429zi.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nucleo-f439zi.yml b/.github/workflows/build-nucleo-f439zi.yml index c0770b52d..e58e2e3ed 100644 --- a/.github/workflows/build-nucleo-f439zi.yml +++ b/.github/workflows/build-nucleo-f439zi.yml @@ -4,18 +4,6 @@ name: Build Nucleo F439ZI on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nucleo_f439zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo-f439zi.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nucleo_f439zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo-f439zi.yml' concurrency: group: build-nucleo-f439zi.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nucleo_f429zi.yml b/.github/workflows/build-nucleo_f429zi.yml index 4d956743a..8cfeecd6f 100644 --- a/.github/workflows/build-nucleo_f429zi.yml +++ b/.github/workflows/build-nucleo_f429zi.yml @@ -4,18 +4,6 @@ name: Build Nucleo F429ZI on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nucleo_f429zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo_f429zi.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nucleo_f429zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo_f429zi.yml' concurrency: group: build-nucleo_f429zi.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-nucleo_f439zi.yml b/.github/workflows/build-nucleo_f439zi.yml index 87711dbf3..f53e3659a 100644 --- a/.github/workflows/build-nucleo_f439zi.yml +++ b/.github/workflows/build-nucleo_f439zi.yml @@ -4,18 +4,6 @@ name: Build Nucleo F439ZI on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/nucleo_f439zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo_f439zi.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/nucleo_f439zi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-nucleo_f439zi.yml' concurrency: group: build-nucleo_f439zi.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-qtpy_m0.yml b/.github/workflows/build-qtpy_m0.yml index a48fde69a..15b11e49f 100644 --- a/.github/workflows/build-qtpy_m0.yml +++ b/.github/workflows/build-qtpy_m0.yml @@ -4,18 +4,6 @@ name: Build QT Py M0 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/qtpy_m0/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-qtpy_m0.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/qtpy_m0/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-qtpy_m0.yml' concurrency: group: build-qtpy_m0.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-rp2040.yml b/.github/workflows/build-rp2040.yml index eb136a55e..3e114153f 100644 --- a/.github/workflows/build-rp2040.yml +++ b/.github/workflows/build-rp2040.yml @@ -4,18 +4,6 @@ name: Build RP2040 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/rp2040/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rp2040.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/rp2040/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rp2040.yml' concurrency: group: build-rp2040.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-rp2350.yml b/.github/workflows/build-rp2350.yml index cd7495c9e..8c4a9fc60 100644 --- a/.github/workflows/build-rp2350.yml +++ b/.github/workflows/build-rp2350.yml @@ -4,18 +4,6 @@ name: Build RP2350 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/rp2350/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rp2350.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/rp2350/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rp2350.yml' concurrency: group: build-rp2350.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-rpipico.yml b/.github/workflows/build-rpipico.yml index 1582bcab4..303fffbd3 100644 --- a/.github/workflows/build-rpipico.yml +++ b/.github/workflows/build-rpipico.yml @@ -4,18 +4,6 @@ name: Build Raspberry Pi Pico on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/rpipico/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rpipico.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/rpipico/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rpipico.yml' concurrency: group: build-rpipico.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-rpipico2.yml b/.github/workflows/build-rpipico2.yml index aeaa6758c..6c5eb1e95 100644 --- a/.github/workflows/build-rpipico2.yml +++ b/.github/workflows/build-rpipico2.yml @@ -4,18 +4,6 @@ name: Build Raspberry Pi Pico 2 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/rpipico2/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rpipico2.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/rpipico2/**' - - 'crates/fbuild-build-arm/src/rp2040/**' - - '.github/workflows/build-rpipico2.yml' concurrency: group: build-rpipico2.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-sam3x8e_due.yml b/.github/workflows/build-sam3x8e_due.yml index efb8a1f30..32a164444 100644 --- a/.github/workflows/build-sam3x8e_due.yml +++ b/.github/workflows/build-sam3x8e_due.yml @@ -4,18 +4,6 @@ name: Build Arduino Due on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/sam3x8e_due/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-sam3x8e_due.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/sam3x8e_due/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-sam3x8e_due.yml' concurrency: group: build-sam3x8e_due.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-samd21.yml b/.github/workflows/build-samd21.yml index 65e283cef..b6815eac2 100644 --- a/.github/workflows/build-samd21.yml +++ b/.github/workflows/build-samd21.yml @@ -4,18 +4,6 @@ name: Build SAMD21 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/samd21/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd21.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/samd21/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd21.yml' concurrency: group: build-samd21.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-samd21_zero.yml b/.github/workflows/build-samd21_zero.yml index a3f9457bd..b053cba10 100644 --- a/.github/workflows/build-samd21_zero.yml +++ b/.github/workflows/build-samd21_zero.yml @@ -4,18 +4,6 @@ name: Build Arduino Zero on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/samd21_zero/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd21_zero.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/samd21_zero/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd21_zero.yml' concurrency: group: build-samd21_zero.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-samd51j.yml b/.github/workflows/build-samd51j.yml index 5fd9818f5..f30921292 100644 --- a/.github/workflows/build-samd51j.yml +++ b/.github/workflows/build-samd51j.yml @@ -4,18 +4,6 @@ name: Build SAMD51J on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/samd51j/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd51j.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/samd51j/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd51j.yml' concurrency: group: build-samd51j.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-samd51p.yml b/.github/workflows/build-samd51p.yml index ad20e1cc8..e2626954f 100644 --- a/.github/workflows/build-samd51p.yml +++ b/.github/workflows/build-samd51p.yml @@ -4,18 +4,6 @@ name: Build SAMD51P on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/samd51p/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd51p.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/samd51p/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-samd51p.yml' concurrency: group: build-samd51p.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-stm32f103c8.yml b/.github/workflows/build-stm32f103c8.yml index e777ccfab..42a9ed2ca 100644 --- a/.github/workflows/build-stm32f103c8.yml +++ b/.github/workflows/build-stm32f103c8.yml @@ -4,18 +4,6 @@ name: Build STM32F103C8 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/stm32f103c8/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f103c8.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/stm32f103c8/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f103c8.yml' concurrency: group: build-stm32f103c8.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-stm32f103cb.yml b/.github/workflows/build-stm32f103cb.yml index db3eaaafe..d05fc6f54 100644 --- a/.github/workflows/build-stm32f103cb.yml +++ b/.github/workflows/build-stm32f103cb.yml @@ -4,18 +4,6 @@ name: Build STM32F103CB on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/stm32f103cb/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f103cb.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/stm32f103cb/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f103cb.yml' concurrency: group: build-stm32f103cb.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-stm32f103tb.yml b/.github/workflows/build-stm32f103tb.yml index d829281f6..2ccd4e57d 100644 --- a/.github/workflows/build-stm32f103tb.yml +++ b/.github/workflows/build-stm32f103tb.yml @@ -4,18 +4,6 @@ name: Build STM32F103TB on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/stm32f103tb/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f103tb.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/stm32f103tb/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f103tb.yml' concurrency: group: build-stm32f103tb.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-stm32f411ce.yml b/.github/workflows/build-stm32f411ce.yml index 7441c7604..c2bf07a7b 100644 --- a/.github/workflows/build-stm32f411ce.yml +++ b/.github/workflows/build-stm32f411ce.yml @@ -4,18 +4,6 @@ name: Build STM32F411CE on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/stm32f411ce/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f411ce.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/stm32f411ce/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32f411ce.yml' concurrency: group: build-stm32f411ce.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-stm32h747xi.yml b/.github/workflows/build-stm32h747xi.yml index 2f0c257cb..ac7176cd0 100644 --- a/.github/workflows/build-stm32h747xi.yml +++ b/.github/workflows/build-stm32h747xi.yml @@ -4,18 +4,6 @@ name: Build STM32H747XI on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/stm32h747xi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32h747xi.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/stm32h747xi/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-stm32h747xi.yml' concurrency: group: build-stm32h747xi.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-supermini_nrf52840.yml b/.github/workflows/build-supermini_nrf52840.yml index 73e9bc593..c6b53ad96 100644 --- a/.github/workflows/build-supermini_nrf52840.yml +++ b/.github/workflows/build-supermini_nrf52840.yml @@ -4,18 +4,6 @@ name: Build SuperMini nRF52840 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/supermini_nrf52840/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-supermini_nrf52840.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/supermini_nrf52840/**' - - 'crates/fbuild-build-arm/src/nrf52/**' - - '.github/workflows/build-supermini_nrf52840.yml' concurrency: group: build-supermini_nrf52840.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensy30.yml b/.github/workflows/build-teensy30.yml index 7bec2368a..eb7e70021 100644 --- a/.github/workflows/build-teensy30.yml +++ b/.github/workflows/build-teensy30.yml @@ -4,18 +4,6 @@ name: Build Teensy 3.0 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensy30/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy30.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensy30/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy30.yml' concurrency: group: build-teensy30.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensy31.yml b/.github/workflows/build-teensy31.yml index 29b890be2..2d15bab17 100644 --- a/.github/workflows/build-teensy31.yml +++ b/.github/workflows/build-teensy31.yml @@ -4,18 +4,6 @@ name: Build Teensy 3.1 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensy31/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy31.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensy31/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy31.yml' concurrency: group: build-teensy31.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensy32.yml b/.github/workflows/build-teensy32.yml index bbc17b688..c8f152a4f 100644 --- a/.github/workflows/build-teensy32.yml +++ b/.github/workflows/build-teensy32.yml @@ -4,18 +4,6 @@ name: Build Teensy 3.2 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensy32/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy32.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensy32/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy32.yml' concurrency: group: build-teensy32.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensy35.yml b/.github/workflows/build-teensy35.yml index 7d9d9e592..851d13e5a 100644 --- a/.github/workflows/build-teensy35.yml +++ b/.github/workflows/build-teensy35.yml @@ -4,18 +4,6 @@ name: Build Teensy 3.5 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensy35/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy35.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensy35/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy35.yml' concurrency: group: build-teensy35.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensy36.yml b/.github/workflows/build-teensy36.yml index ea1eb9fb4..0fc1956fa 100644 --- a/.github/workflows/build-teensy36.yml +++ b/.github/workflows/build-teensy36.yml @@ -4,18 +4,6 @@ name: Build Teensy 3.6 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensy36/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy36.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensy36/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy36.yml' concurrency: group: build-teensy36.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensy40.yml b/.github/workflows/build-teensy40.yml index 62a05f63f..87af1f68c 100644 --- a/.github/workflows/build-teensy40.yml +++ b/.github/workflows/build-teensy40.yml @@ -4,18 +4,6 @@ name: Build Teensy 4.0 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensy40/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy40.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensy40/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensy40.yml' concurrency: group: build-teensy40.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensy41.yml b/.github/workflows/build-teensy41.yml index d46122cc7..0364ff9c8 100644 --- a/.github/workflows/build-teensy41.yml +++ b/.github/workflows/build-teensy41.yml @@ -4,68 +4,6 @@ name: Build Teensy 4.1 on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensy41/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - 'crates/fbuild-build/**' - - 'crates/fbuild-build/Cargo.toml' - - 'crates/fbuild-cli/**' - - 'crates/fbuild-daemon/**' - - 'crates/fbuild-core/**' - - 'crates/fbuild-paths/**' - - 'crates/fbuild-config/**' - - 'crates/fbuild-deploy/**' - - 'crates/fbuild-packages/**' - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'rust-toolchain.toml' - - '.github/workflows/template_build.yml' - - '.github/workflows/ci-workflow-drift.yml' - - 'ci/render_workflows.py' - - 'ci/board_families.json' - - 'ci/ci_common_paths.txt' - - 'crates/fbuild-build-engine/**' - - 'crates/fbuild-build-mcu/src/lib.rs' - - 'crates/fbuild-build-mcu/*argo.toml' - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-library/**' - - 'crates/fbuild-packages-fetch/**' - - '.github/workflows/build-teensy41.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensy41/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - 'crates/fbuild-build/**' - - 'crates/fbuild-build/Cargo.toml' - - 'crates/fbuild-cli/**' - - 'crates/fbuild-daemon/**' - - 'crates/fbuild-core/**' - - 'crates/fbuild-paths/**' - - 'crates/fbuild-config/**' - - 'crates/fbuild-deploy/**' - - 'crates/fbuild-packages/**' - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'rust-toolchain.toml' - - '.github/workflows/template_build.yml' - - '.github/workflows/ci-workflow-drift.yml' - - 'ci/render_workflows.py' - - 'ci/board_families.json' - - 'ci/ci_common_paths.txt' - - 'crates/fbuild-build-engine/**' - - 'crates/fbuild-build-mcu/src/lib.rs' - - 'crates/fbuild-build-mcu/*argo.toml' - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-library/**' - - 'crates/fbuild-packages-fetch/**' - - '.github/workflows/build-teensy41.yml' concurrency: group: build-teensy41.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-teensylc.yml b/.github/workflows/build-teensylc.yml index 1f3596ebd..907cb30c1 100644 --- a/.github/workflows/build-teensylc.yml +++ b/.github/workflows/build-teensylc.yml @@ -4,18 +4,6 @@ name: Build Teensy LC on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/teensylc/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensylc.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/teensylc/**' - - 'crates/fbuild-build-arm/src/teensy/**' - - '.github/workflows/build-teensylc.yml' concurrency: group: build-teensylc.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-thingplusmatter.yml b/.github/workflows/build-thingplusmatter.yml index e72473eba..df3eaec68 100644 --- a/.github/workflows/build-thingplusmatter.yml +++ b/.github/workflows/build-thingplusmatter.yml @@ -4,18 +4,6 @@ name: Build SparkFun Thing Plus Matter on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/sparkfun_thingplusmatter/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-thingplusmatter.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/sparkfun_thingplusmatter/**' - - 'crates/fbuild-build-arm/src/sam/**' - - '.github/workflows/build-thingplusmatter.yml' concurrency: group: build-thingplusmatter.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-tinystm.yml b/.github/workflows/build-tinystm.yml index f5439b463..123952dbb 100644 --- a/.github/workflows/build-tinystm.yml +++ b/.github/workflows/build-tinystm.yml @@ -4,18 +4,6 @@ name: Build TinySTM103TB on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/hy_tinystm103tb/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-tinystm.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/hy_tinystm103tb/**' - - 'crates/fbuild-build-arm/src/stm32/**' - - '.github/workflows/build-tinystm.yml' concurrency: group: build-tinystm.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-uno-r4-wifi.yml b/.github/workflows/build-uno-r4-wifi.yml index 51e56bd3f..ff5decc6a 100644 --- a/.github/workflows/build-uno-r4-wifi.yml +++ b/.github/workflows/build-uno-r4-wifi.yml @@ -4,18 +4,6 @@ name: Build Arduino Uno R4 WiFi on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/uno_r4_wifi/**' - - 'crates/fbuild-build-arm/src/renesas/**' - - '.github/workflows/build-uno-r4-wifi.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/uno_r4_wifi/**' - - 'crates/fbuild-build-arm/src/renesas/**' - - '.github/workflows/build-uno-r4-wifi.yml' concurrency: group: build-uno-r4-wifi.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-uno.yml b/.github/workflows/build-uno.yml index f1830cbfb..e742f092f 100644 --- a/.github/workflows/build-uno.yml +++ b/.github/workflows/build-uno.yml @@ -4,68 +4,6 @@ name: Build Arduino Uno on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/uno/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - 'crates/fbuild-build/**' - - 'crates/fbuild-build/Cargo.toml' - - 'crates/fbuild-cli/**' - - 'crates/fbuild-daemon/**' - - 'crates/fbuild-core/**' - - 'crates/fbuild-paths/**' - - 'crates/fbuild-config/**' - - 'crates/fbuild-deploy/**' - - 'crates/fbuild-packages/**' - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'rust-toolchain.toml' - - '.github/workflows/template_build.yml' - - '.github/workflows/ci-workflow-drift.yml' - - 'ci/render_workflows.py' - - 'ci/board_families.json' - - 'ci/ci_common_paths.txt' - - 'crates/fbuild-build-engine/**' - - 'crates/fbuild-build-mcu/src/lib.rs' - - 'crates/fbuild-build-mcu/*argo.toml' - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-library/**' - - 'crates/fbuild-packages-fetch/**' - - '.github/workflows/build-uno.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/uno/**' - - 'crates/fbuild-build-mcu/src/avr/**' - - 'crates/fbuild-build/**' - - 'crates/fbuild-build/Cargo.toml' - - 'crates/fbuild-cli/**' - - 'crates/fbuild-daemon/**' - - 'crates/fbuild-core/**' - - 'crates/fbuild-paths/**' - - 'crates/fbuild-config/**' - - 'crates/fbuild-deploy/**' - - 'crates/fbuild-packages/**' - - 'crates/fbuild-header-scan/**' - - 'crates/fbuild-library-select/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'rust-toolchain.toml' - - '.github/workflows/template_build.yml' - - '.github/workflows/ci-workflow-drift.yml' - - 'ci/render_workflows.py' - - 'ci/board_families.json' - - 'ci/ci_common_paths.txt' - - 'crates/fbuild-build-engine/**' - - 'crates/fbuild-build-mcu/src/lib.rs' - - 'crates/fbuild-build-mcu/*argo.toml' - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-library/**' - - 'crates/fbuild-packages-fetch/**' - - '.github/workflows/build-uno.yml' concurrency: group: build-uno.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/build-uno_r4_wifi.yml b/.github/workflows/build-uno_r4_wifi.yml index b3b48b35d..262791c05 100644 --- a/.github/workflows/build-uno_r4_wifi.yml +++ b/.github/workflows/build-uno_r4_wifi.yml @@ -4,18 +4,6 @@ name: Build UNO R4 WiFi on: workflow_dispatch: {} workflow_call: {} - push: - branches: [main] - paths: - - 'tests/platform/uno_r4_wifi/**' - - 'crates/fbuild-build-arm/src/renesas/**' - - '.github/workflows/build-uno_r4_wifi.yml' - pull_request: - branches: [main] - paths: - - 'tests/platform/uno_r4_wifi/**' - - 'crates/fbuild-build-arm/src/renesas/**' - - '.github/workflows/build-uno_r4_wifi.yml' concurrency: group: build-uno_r4_wifi.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} diff --git a/.github/workflows/check-ubuntu.yml b/.github/workflows/check-ubuntu.yml index 46c825562..faeec4f46 100644 --- a/.github/workflows/check-ubuntu.yml +++ b/.github/workflows/check-ubuntu.yml @@ -2,10 +2,12 @@ name: Check Ubuntu on: workflow_dispatch: {} - push: - branches: [main] - pull_request: - branches: [main] + workflow_call: + inputs: + ref: + type: string + required: false + default: "" # Auto-cancel superseded PR runs: pushing again to a feature branch # supersedes the in-flight run instead of queueing behind it. Non-PR @@ -29,6 +31,14 @@ jobs: - uses: actions/checkout@v6 with: fetch-depth: 0 + ref: ${{ inputs.ref }} + - name: Verify candidate SHA + if: inputs.ref != '' + env: + CANDIDATE_SHA: ${{ inputs.ref }} + run: | + [[ "$CANDIDATE_SHA" =~ ^[0-9a-f]{40}$ ]] + test "$(git rev-parse HEAD)" = "$CANDIDATE_SHA" - uses: astral-sh/setup-uv@v3 - name: Setup soldr id: setup-soldr diff --git a/.github/workflows/check-windows.yml b/.github/workflows/check-windows.yml index 09df3d55e..c9ca4ccb0 100644 --- a/.github/workflows/check-windows.yml +++ b/.github/workflows/check-windows.yml @@ -2,10 +2,12 @@ name: Check Windows on: workflow_dispatch: {} - push: - branches: [main] - pull_request: - branches: [main] + workflow_call: + inputs: + ref: + type: string + required: false + default: "" # Auto-cancel superseded PR runs: pushing again to a feature branch # supersedes the in-flight run instead of queueing behind it. Non-PR @@ -27,6 +29,8 @@ jobs: timeout-minutes: 30 steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - name: Setup soldr id: setup-soldr uses: zackees/setup-soldr@v0 diff --git a/.github/workflows/ci-full.yml b/.github/workflows/ci-full.yml new file mode 100644 index 000000000..b3bb52801 --- /dev/null +++ b/.github/workflows/ci-full.yml @@ -0,0 +1,631 @@ +# Generated by ci/render_workflows.py from ci/board_families.json. +name: ci-full + +on: + workflow_call: + inputs: + candidate_sha: + type: string + required: true + outputs: + coverage: + description: 'All full validation jobs passed on the candidate SHA' + value: ${{ jobs.coverage.outputs.complete }} + workflow_dispatch: + inputs: + candidate_sha: + description: 'Exact 40-character commit SHA to validate' + type: string + required: true + +concurrency: + group: ci-full-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + verify: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + runs-on: ubuntu-latest + outputs: + candidate_sha: ${{ steps.sha.outputs.candidate_sha }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.candidate_sha }} + persist-credentials: false + - id: sha + env: + CANDIDATE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.candidate_sha }} + WORKFLOW_SHA: ${{ github.sha }} + EVENT_NAME: ${{ github.event_name }} + run: | + if [[ ! "$CANDIDATE_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo 'candidate_sha must be an exact 40-character commit SHA' >&2 + exit 1 + fi + if [ "$EVENT_NAME" != pull_request ] && [ "$CANDIDATE_SHA" != "$WORKFLOW_SHA" ]; then + echo "candidate $CANDIDATE_SHA differs from workflow revision $WORKFLOW_SHA; dispatch from a ref at the candidate commit" >&2 + exit 1 + fi + actual=$(git rev-parse HEAD) + if [ "$actual" != "$CANDIDATE_SHA" ]; then + echo "checkout SHA $actual does not match $CANDIDATE_SHA" >&2 + exit 1 + fi + echo "candidate_sha=$actual" >> "$GITHUB_OUTPUT" + linux: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/check-ubuntu.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + windows: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/check-windows.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + dylint: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/dylint.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + run_full: true + acceptance: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/acceptance-205.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + bench: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/bench-205.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + qemu: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/qemu-linux-runtime.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + fmt: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/fmt.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + docs: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/docs.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + msrv: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/msrv.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + validate_boards: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/validate-boards.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + crate_gate: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/crate-gate.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + boards: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + name: ${{ matrix.workflow_name }} + strategy: + fail-fast: false + matrix: + include: + - workflow: "build-apollo3_red.yml" + workflow_name: "Ambiq Apollo3 Red" + test_dir: "tests/platform/apollo3_red" + env_name: "apollo3_red" + firmware_ext: "bin" + workflow_aliases: ["build-apollo3_red.yml"] + - workflow: "build-apollo3_thing_explorable.yml" + workflow_name: "Apollo3 Thing Explorable" + test_dir: "tests/platform/apollo3_thing_explorable" + env_name: "apollo3_thing_explorable" + firmware_ext: "bin" + workflow_aliases: ["build-apollo3_thing_explorable.yml"] + - workflow: "build-atmega8.yml" + workflow_name: "ATmega8" + test_dir: "tests/platform/atmega8" + env_name: "atmega8" + firmware_ext: "hex" + workflow_aliases: ["build-atmega8.yml"] + - workflow: "build-atmega8a.yml" + workflow_name: "ATmega8A" + test_dir: "tests/platform/atmega8a" + env_name: "atmega8a" + firmware_ext: "hex" + workflow_aliases: ["build-atmega8a.yml"] + - workflow: "build-attiny1604.yml" + workflow_name: "ATtiny1604" + test_dir: "tests/platform/attiny1604" + env_name: "attiny1604" + firmware_ext: "hex" + workflow_aliases: ["build-attiny1604.yml"] + - workflow: "build-attiny1616.yml" + workflow_name: "ATtiny1616" + test_dir: "tests/platform/attiny1616" + env_name: "attiny1616" + firmware_ext: "hex" + workflow_aliases: ["build-attiny1616.yml"] + - workflow: "build-attiny4313.yml" + workflow_name: "ATtiny4313" + test_dir: "tests/platform/attiny4313" + env_name: "attiny4313" + firmware_ext: "hex" + workflow_aliases: ["build-attiny4313.yml"] + - workflow: "build-attiny85.yml" + workflow_name: "ATtiny85" + test_dir: "tests/platform/attiny85" + env_name: "attiny85" + firmware_ext: "hex" + workflow_aliases: ["build-attiny85.yml"] + - workflow: "build-attiny88.yml" + workflow_name: "ATtiny88" + test_dir: "tests/platform/attiny88" + env_name: "attiny88" + firmware_ext: "hex" + workflow_aliases: ["build-attiny88.yml"] + - workflow: "build-blackpill.yml" + workflow_name: "STM32 BlackPill F411CE" + test_dir: "tests/platform/blackpill_f411ce" + env_name: "blackpill" + firmware_ext: "hex" + workflow_aliases: ["build-blackpill.yml"] + - workflow: "build-bluepill.yml" + workflow_name: "STM32 BluePill F103C8" + test_dir: "tests/platform/bluepill_f103c8" + env_name: "bluepill" + firmware_ext: "hex" + workflow_aliases: ["build-bluepill.yml"] + - workflow: "build-ch32l103.yml" + workflow_name: "CH32L103" + test_dir: "tests/platform/ch32l103" + env_name: "ch32l103" + firmware_ext: "bin" + workflow_aliases: ["build-ch32l103.yml"] + - workflow: "build-ch32v003.yml" + workflow_name: "CH32V003" + test_dir: "tests/platform/ch32v003" + env_name: "ch32v003" + firmware_ext: "bin" + workflow_aliases: ["build-ch32v003.yml"] + - workflow: "build-ch32v006.yml" + workflow_name: "CH32V006" + test_dir: "tests/platform/ch32v006" + env_name: "ch32v006" + firmware_ext: "bin" + workflow_aliases: ["build-ch32v006.yml"] + - workflow: "build-ch32v103.yml" + workflow_name: "CH32V103" + test_dir: "tests/platform/ch32v103" + env_name: "ch32v103" + firmware_ext: "bin" + workflow_aliases: ["build-ch32v103.yml"] + - workflow: "build-ch32v203.yml" + workflow_name: "CH32V203" + test_dir: "tests/platform/ch32v203" + env_name: "ch32v203" + firmware_ext: "bin" + workflow_aliases: ["build-ch32v203.yml"] + - workflow: "build-ch32v208.yml" + workflow_name: "CH32V208" + test_dir: "tests/platform/ch32v208" + env_name: "ch32v208" + firmware_ext: "bin" + workflow_aliases: ["build-ch32v208.yml"] + - workflow: "build-ch32v303.yml" + workflow_name: "CH32V303" + test_dir: "tests/platform/ch32v303" + env_name: "ch32v303" + firmware_ext: "bin" + workflow_aliases: ["build-ch32v303.yml"] + - workflow: "build-ch32v307.yml" + workflow_name: "CH32V307" + test_dir: "tests/platform/ch32v307" + env_name: "ch32v307" + firmware_ext: "bin" + workflow_aliases: ["build-ch32v307.yml"] + - workflow: "build-ch32x035.yml" + workflow_name: "CH32X035" + test_dir: "tests/platform/ch32x035" + env_name: "ch32x035" + firmware_ext: "bin" + workflow_aliases: ["build-ch32x035.yml"] + - workflow: "build-clearcore.yml" + workflow_name: "Teknic ClearCore (SAME53)" + test_dir: "tests/platform/clearcore" + env_name: "clearcore" + firmware_ext: "bin" + workflow_aliases: ["build-clearcore.yml"] + - workflow: "build-due.yml" + workflow_name: "Arduino Due" + test_dir: "tests/platform/due" + env_name: "due" + firmware_ext: "bin" + workflow_aliases: ["build-due.yml"] + - workflow: "build-esp32c2.yml" + workflow_name: "ESP32-C2" + test_dir: "tests/platform/esp32c2" + env_name: "esp32c2" + firmware_ext: "bin" + workflow_aliases: ["build-esp32c2.yml"] + - workflow: "build-esp32c3.yml" + workflow_name: "ESP32-C3" + test_dir: "tests/platform/esp32c3" + env_name: "esp32c3" + firmware_ext: "bin" + workflow_aliases: ["build-esp32c3.yml"] + - workflow: "build-esp32c5.yml" + workflow_name: "ESP32-C5" + test_dir: "tests/platform/esp32c5" + env_name: "esp32c5" + firmware_ext: "bin" + workflow_aliases: ["build-esp32c5.yml"] + - workflow: "build-esp32c6.yml" + workflow_name: "ESP32-C6" + test_dir: "tests/platform/esp32c6" + env_name: "esp32c6" + firmware_ext: "bin" + workflow_aliases: ["build-esp32c6.yml"] + - workflow: "build-esp32dev.yml" + workflow_name: "ESP32 Dev" + test_dir: "tests/platform/esp32dev" + env_name: "esp32dev" + firmware_ext: "bin" + workflow_aliases: ["build-esp32dev.yml"] + - workflow: "build-esp32h2.yml" + workflow_name: "ESP32-H2" + test_dir: "tests/platform/esp32h2" + env_name: "esp32h2" + firmware_ext: "bin" + workflow_aliases: ["build-esp32h2.yml"] + - workflow: "build-esp32p4.yml" + workflow_name: "ESP32-P4" + test_dir: "tests/platform/esp32p4" + env_name: "esp32p4" + firmware_ext: "bin" + workflow_aliases: ["build-esp32p4.yml"] + - workflow: "build-esp32s2.yml" + workflow_name: "ESP32-S2" + test_dir: "tests/platform/esp32s2" + env_name: "esp32s2" + firmware_ext: "bin" + workflow_aliases: ["build-esp32s2.yml"] + - workflow: "build-esp32s3.yml" + workflow_name: "ESP32-S3" + test_dir: "tests/platform/esp32s3" + env_name: "esp32s3" + firmware_ext: "bin" + workflow_aliases: ["build-esp32s3.yml"] + - workflow: "build-esp8266.yml" + workflow_name: "ESP8266" + test_dir: "tests/platform/esp8266" + env_name: "esp8266" + firmware_ext: "bin" + workflow_aliases: ["build-esp8266.yml"] + - workflow: "build-giga-r1.yml" + workflow_name: "Arduino GIGA R1 M7" + test_dir: "tests/platform/giga_r1_m7" + env_name: "giga_r1" + firmware_ext: "bin" + workflow_aliases: ["build-giga-r1.yml"] + - workflow: "build-leonardo.yml" + workflow_name: "Arduino Leonardo" + test_dir: "tests/platform/leonardo" + env_name: "leonardo" + firmware_ext: "hex" + workflow_aliases: ["build-leonardo.yml"] + - workflow: "build-lpc804.yml" + workflow_name: "NXP LPCXpresso804" + test_dir: "tests/platform/lpc804" + env_name: "lpc804" + firmware_ext: "bin" + workflow_aliases: ["build-lpc804.yml"] + - workflow: "build-lpc845.yml" + workflow_name: "NXP LPC845" + test_dir: "tests/platform/lpc845" + env_name: "lpc845" + firmware_ext: "bin" + workflow_aliases: ["build-lpc845.yml"] + - workflow: "build-lpc845brk.yml" + workflow_name: "NXP LPC845-BRK" + test_dir: "tests/platform/lpc845brk" + env_name: "lpc845brk" + firmware_ext: "bin" + workflow_aliases: ["build-lpc845brk.yml"] + - workflow: "build-lpcxpresso804.yml" + workflow_name: "NXP LPCXpresso804" + test_dir: "tests/platform/lpcxpresso804" + env_name: "lpcxpresso804" + firmware_ext: "bin" + workflow_aliases: ["build-lpcxpresso804.yml"] + - workflow: "build-lpcxpresso845max.yml" + workflow_name: "NXP LPCXpresso845-MAX" + test_dir: "tests/platform/lpcxpresso845max" + env_name: "lpcxpresso845max" + firmware_ext: "bin" + workflow_aliases: ["build-lpcxpresso845max.yml"] + - workflow: "build-matrix_portal_m4.yml" + workflow_name: "Adafruit Matrix Portal M4" + test_dir: "tests/platform/matrix_portal_m4" + env_name: "matrix_portal_m4" + firmware_ext: "bin" + workflow_aliases: ["build-matrix_portal_m4.yml"] + - workflow: "build-mgm240.yml" + workflow_name: "Silicon Labs MGM240" + test_dir: "tests/platform/mgm240" + env_name: "mgm240" + firmware_ext: "bin" + workflow_aliases: ["build-mgm240.yml"] + - workflow: "build-nano-every.yml" + workflow_name: "Arduino Nano Every" + test_dir: "tests/platform/nano_every" + env_name: "nano_every" + firmware_ext: "hex" + workflow_aliases: ["build-nano-every.yml", "build-nano_every.yml"] + - workflow: "build-nice_nano_nrf52840.yml" + workflow_name: "Nice!Nano nRF52840" + test_dir: "tests/platform/nice_nano_nrf52840" + env_name: "nice_nano_nrf52840" + firmware_ext: "hex" + workflow_aliases: ["build-nice_nano_nrf52840.yml"] + - workflow: "build-nrf52840-sense.yml" + workflow_name: "Adafruit Feather nRF52840 Sense" + test_dir: "tests/platform/adafruit_feather_nrf52840_sense" + env_name: "nrf52840_sense" + firmware_ext: "hex" + workflow_aliases: ["build-nrf52840-sense.yml"] + - workflow: "build-nrf52840_dk.yml" + workflow_name: "Nordic nRF52840-DK" + test_dir: "tests/platform/nrf52840_dk" + env_name: "nrf52840_dk" + firmware_ext: "hex" + workflow_aliases: ["build-nrf52840_dk.yml"] + - workflow: "build-nrfmicro_nrf52840.yml" + workflow_name: "nrfmicro nRF52840" + test_dir: "tests/platform/nrfmicro_nrf52840" + env_name: "nrfmicro_nrf52840" + firmware_ext: "hex" + workflow_aliases: ["build-nrfmicro_nrf52840.yml"] + - workflow: "build-nucleo-f429zi.yml" + workflow_name: "ST Nucleo F429ZI" + test_dir: "tests/platform/nucleo_f429zi" + env_name: "nucleo_f429zi" + firmware_ext: "hex" + workflow_aliases: ["build-nucleo-f429zi.yml", "build-nucleo_f429zi.yml"] + - workflow: "build-nucleo-f439zi.yml" + workflow_name: "ST Nucleo F439ZI" + test_dir: "tests/platform/nucleo_f439zi" + env_name: "nucleo_f439zi" + firmware_ext: "hex" + workflow_aliases: ["build-nucleo-f439zi.yml", "build-nucleo_f439zi.yml"] + - workflow: "build-qtpy_m0.yml" + workflow_name: "Adafruit QT Py M0" + test_dir: "tests/platform/qtpy_m0" + env_name: "qtpy_m0" + firmware_ext: "bin" + workflow_aliases: ["build-qtpy_m0.yml"] + - workflow: "build-rp2040.yml" + workflow_name: "RP2040" + test_dir: "tests/platform/rp2040" + env_name: "rp2040" + firmware_ext: "bin" + workflow_aliases: ["build-rp2040.yml"] + - workflow: "build-rp2350.yml" + workflow_name: "RP2350" + test_dir: "tests/platform/rp2350" + env_name: "rp2350" + firmware_ext: "bin" + workflow_aliases: ["build-rp2350.yml"] + - workflow: "build-rpipico.yml" + workflow_name: "Raspberry Pi Pico" + test_dir: "tests/platform/rpipico" + env_name: "rpipico" + firmware_ext: "bin" + workflow_aliases: ["build-rpipico.yml"] + - workflow: "build-rpipico2.yml" + workflow_name: "Raspberry Pi Pico 2" + test_dir: "tests/platform/rpipico2" + env_name: "rpipico2" + firmware_ext: "bin" + workflow_aliases: ["build-rpipico2.yml"] + - workflow: "build-sam3x8e_due.yml" + workflow_name: "Atmel SAM3X8E (Due)" + test_dir: "tests/platform/sam3x8e_due" + env_name: "sam3x8e_due" + firmware_ext: "bin" + workflow_aliases: ["build-sam3x8e_due.yml"] + - workflow: "build-samd21.yml" + workflow_name: "Atmel SAMD21" + test_dir: "tests/platform/samd21" + env_name: "samd21" + firmware_ext: "bin" + workflow_aliases: ["build-samd21.yml"] + - workflow: "build-samd21_zero.yml" + workflow_name: "Arduino Zero (SAMD21)" + test_dir: "tests/platform/samd21_zero" + env_name: "samd21_zero" + firmware_ext: "bin" + workflow_aliases: ["build-samd21_zero.yml"] + - workflow: "build-samd51j.yml" + workflow_name: "Atmel SAMD51J" + test_dir: "tests/platform/samd51j" + env_name: "samd51j" + firmware_ext: "bin" + workflow_aliases: ["build-samd51j.yml"] + - workflow: "build-samd51p.yml" + workflow_name: "Atmel SAMD51P" + test_dir: "tests/platform/samd51p" + env_name: "samd51p" + firmware_ext: "bin" + workflow_aliases: ["build-samd51p.yml"] + - workflow: "build-stm32f103c8.yml" + workflow_name: "STM32F103C8" + test_dir: "tests/platform/stm32f103c8" + env_name: "stm32f103c8" + firmware_ext: "hex" + workflow_aliases: ["build-stm32f103c8.yml"] + - workflow: "build-stm32f103cb.yml" + workflow_name: "STM32F103CB" + test_dir: "tests/platform/stm32f103cb" + env_name: "stm32f103cb" + firmware_ext: "hex" + workflow_aliases: ["build-stm32f103cb.yml"] + - workflow: "build-stm32f103tb.yml" + workflow_name: "STM32F103TB" + test_dir: "tests/platform/stm32f103tb" + env_name: "stm32f103tb" + firmware_ext: "hex" + workflow_aliases: ["build-stm32f103tb.yml"] + - workflow: "build-stm32f411ce.yml" + workflow_name: "STM32F411CE" + test_dir: "tests/platform/stm32f411ce" + env_name: "stm32f411ce" + firmware_ext: "hex" + workflow_aliases: ["build-stm32f411ce.yml"] + - workflow: "build-stm32h747xi.yml" + workflow_name: "STM32H747XI" + test_dir: "tests/platform/stm32h747xi" + env_name: "stm32h747xi" + firmware_ext: "bin" + workflow_aliases: ["build-stm32h747xi.yml"] + - workflow: "build-supermini_nrf52840.yml" + workflow_name: "SuperMini nRF52840" + test_dir: "tests/platform/supermini_nrf52840" + env_name: "supermini_nrf52840" + firmware_ext: "hex" + workflow_aliases: ["build-supermini_nrf52840.yml"] + - workflow: "build-teensy30.yml" + workflow_name: "Teensy 3.0" + test_dir: "tests/platform/teensy30" + env_name: "teensy30" + firmware_ext: "hex" + workflow_aliases: ["build-teensy30.yml"] + - workflow: "build-teensy31.yml" + workflow_name: "Teensy 3.1" + test_dir: "tests/platform/teensy31" + env_name: "teensy31" + firmware_ext: "hex" + workflow_aliases: ["build-teensy31.yml"] + - workflow: "build-teensy32.yml" + workflow_name: "Teensy 3.2" + test_dir: "tests/platform/teensy32" + env_name: "teensy32" + firmware_ext: "hex" + workflow_aliases: ["build-teensy32.yml"] + - workflow: "build-teensy35.yml" + workflow_name: "Teensy 3.5" + test_dir: "tests/platform/teensy35" + env_name: "teensy35" + firmware_ext: "hex" + workflow_aliases: ["build-teensy35.yml"] + - workflow: "build-teensy36.yml" + workflow_name: "Teensy 3.6" + test_dir: "tests/platform/teensy36" + env_name: "teensy36" + firmware_ext: "hex" + workflow_aliases: ["build-teensy36.yml"] + - workflow: "build-teensy40.yml" + workflow_name: "Teensy 4.0" + test_dir: "tests/platform/teensy40" + env_name: "teensy40" + firmware_ext: "hex" + workflow_aliases: ["build-teensy40.yml"] + - workflow: "build-teensy41.yml" + workflow_name: "Teensy 4.1" + test_dir: "tests/platform/teensy41" + env_name: "teensy41" + firmware_ext: "hex" + workflow_aliases: ["build-teensy41.yml"] + - workflow: "build-teensylc.yml" + workflow_name: "Teensy LC" + test_dir: "tests/platform/teensylc" + env_name: "teensylc" + firmware_ext: "hex" + workflow_aliases: ["build-teensylc.yml"] + - workflow: "build-thingplusmatter.yml" + workflow_name: "SparkFun Thing Plus Matter" + test_dir: "tests/platform/sparkfun_thingplusmatter" + env_name: "thingplusmatter" + firmware_ext: "bin" + workflow_aliases: ["build-thingplusmatter.yml"] + - workflow: "build-tinystm.yml" + workflow_name: "HY TinySTM103TB" + test_dir: "tests/platform/hy_tinystm103tb" + env_name: "tinystm" + firmware_ext: "hex" + workflow_aliases: ["build-tinystm.yml"] + - workflow: "build-uno-r4-wifi.yml" + workflow_name: "Arduino Uno R4 WiFi" + test_dir: "tests/platform/uno_r4_wifi" + env_name: "uno_r4_wifi" + firmware_ext: "bin" + workflow_aliases: ["build-uno-r4-wifi.yml", "build-uno_r4_wifi.yml"] + - workflow: "build-uno.yml" + workflow_name: "Arduino Uno" + test_dir: "tests/platform/uno" + env_name: "uno" + firmware_ext: "hex" + workflow_aliases: ["build-uno.yml"] + uses: ./.github/workflows/template_build.yml + with: + workflow-name: ${{ matrix.workflow_name }} + test-dir: ${{ matrix.test_dir }} + env-name: ${{ matrix.env_name }} + firmware-ext: ${{ matrix.firmware_ext }} + checkout_ref: ${{ needs.verify.outputs.candidate_sha }} + coverage: + name: Full coverage + if: always() + needs: [verify, boards, linux, windows, dylint, acceptance, bench, qemu, fmt, docs, msrv, validate_boards, crate_gate] + runs-on: ubuntu-latest + outputs: + complete: ${{ steps.complete.outputs.complete }} + steps: + - id: complete + env: + EVENT_NAME: ${{ github.event_name }} + LABEL_PRESENT: ${{ contains(github.event.pull_request.labels.*.name, 'ci-full') }} + VERIFY: ${{ needs.verify.result }} + BOARDS: ${{ needs.boards.result }} + LINUX: ${{ needs.linux.result }} + WINDOWS: ${{ needs.windows.result }} + DYLINT: ${{ needs.dylint.result }} + ACCEPTANCE: ${{ needs.acceptance.result }} + BENCH: ${{ needs.bench.result }} + QEMU: ${{ needs.qemu.result }} + FMT: ${{ needs.fmt.result }} + DOCS: ${{ needs.docs.result }} + MSRV: ${{ needs.msrv.result }} + VALIDATE_BOARDS: ${{ needs.validate_boards.result }} + CRATE_GATE: ${{ needs.crate_gate.result }} + run: | + if [ "$EVENT_NAME" = pull_request ] && [ "$LABEL_PRESENT" != true ]; then + echo 'complete=false' >> "$GITHUB_OUTPUT" + echo 'Optional tier is not selected on this PR; coverage is incomplete' >&2 + exit 1 + fi + for result in "$VERIFY" "$BOARDS" "$LINUX" "$WINDOWS" "$DYLINT" "$ACCEPTANCE" "$BENCH" "$QEMU" "$FMT" "$DOCS" "$MSRV" "$VALIDATE_BOARDS" "$CRATE_GATE"; do + if [ "$result" != success ]; then + echo "coverage incomplete: $result" >&2 + exit 1 + fi + done + echo 'complete=true' >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/ci-minimal.yml b/.github/workflows/ci-minimal.yml new file mode 100644 index 000000000..76eb7a064 --- /dev/null +++ b/.github/workflows/ci-minimal.yml @@ -0,0 +1,53 @@ +# Generated by ci/render_workflows.py from ci/board_families.json. +name: ci-minimal + +on: + push: + branches: [main] + pull_request: + branches: [main] + types: [opened, labeled, unlabeled, synchronize, reopened] + workflow_dispatch: {} + +concurrency: + group: ci-minimal-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + linux: + if: github.event_name != 'pull_request' || (!contains(github.event.pull_request.labels.*.name, 'ci-test') && !contains(github.event.pull_request.labels.*.name, 'ci-full')) + uses: ./.github/workflows/check-ubuntu.yml + with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + test: + if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ci-test') && !contains(github.event.pull_request.labels.*.name, 'ci-full') + uses: ./.github/workflows/ci-test.yml + with: + candidate_sha: ${{ github.event.pull_request.head.sha }} + full: + if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ci-full') + uses: ./.github/workflows/ci-full.yml + with: + candidate_sha: ${{ github.event.pull_request.head.sha }} + selected-coverage: + name: CI selected coverage + if: always() + needs: [linux, test, full] + runs-on: ubuntu-latest + steps: + - env: + LINUX: ${{ needs.linux.result }} + TEST_SELECTED: ${{ contains(github.event.pull_request.labels.*.name, 'ci-test') }} + TEST_RESULT: ${{ needs.test.result }} + FULL_SELECTED: ${{ contains(github.event.pull_request.labels.*.name, 'ci-full') }} + FULL_RESULT: ${{ needs.full.result }} + FULL_COVERAGE: ${{ needs.full.outputs.coverage }} + run: | + if [ "$FULL_SELECTED" = true ]; then + test "$FULL_RESULT" = success + test "$FULL_COVERAGE" = true + elif [ "$TEST_SELECTED" = true ]; then + test "$TEST_RESULT" = success + else + test "$LINUX" = success + fi diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml new file mode 100644 index 000000000..d12f49284 --- /dev/null +++ b/.github/workflows/ci-test.yml @@ -0,0 +1,106 @@ +# Generated by ci/render_workflows.py from ci/board_families.json. +name: ci-test + +on: + workflow_call: + inputs: + candidate_sha: + type: string + required: true + workflow_dispatch: + inputs: + candidate_sha: + description: 'Exact 40-character commit SHA to validate' + type: string + required: true + +concurrency: + group: ci-test-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + verify: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-test') + runs-on: ubuntu-latest + outputs: + candidate_sha: ${{ steps.sha.outputs.candidate_sha }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.candidate_sha }} + persist-credentials: false + - id: sha + env: + CANDIDATE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.candidate_sha }} + WORKFLOW_SHA: ${{ github.sha }} + EVENT_NAME: ${{ github.event_name }} + run: | + if [[ ! "$CANDIDATE_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo 'candidate_sha must be an exact 40-character commit SHA' >&2 + exit 1 + fi + if [ "$EVENT_NAME" != pull_request ] && [ "$CANDIDATE_SHA" != "$WORKFLOW_SHA" ]; then + echo "candidate $CANDIDATE_SHA differs from workflow revision $WORKFLOW_SHA; dispatch from a ref at the candidate commit" >&2 + exit 1 + fi + actual=$(git rev-parse HEAD) + if [ "$actual" != "$CANDIDATE_SHA" ]; then + echo "checkout SHA $actual does not match $CANDIDATE_SHA" >&2 + exit 1 + fi + echo "candidate_sha=$actual" >> "$GITHUB_OUTPUT" + linux: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-test') + needs: verify + uses: ./.github/workflows/check-ubuntu.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} + boards: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-test') + needs: verify + name: ${{ matrix.workflow_name }} + strategy: + fail-fast: false + matrix: + include: + - workflow: "build-uno.yml" + workflow_name: "Arduino Uno" + test_dir: "tests/platform/uno" + env_name: "uno" + firmware_ext: "hex" + workflow_aliases: ["build-uno.yml"] + uses: ./.github/workflows/template_build.yml + with: + workflow-name: ${{ matrix.workflow_name }} + test-dir: ${{ matrix.test_dir }} + env-name: ${{ matrix.env_name }} + firmware-ext: ${{ matrix.firmware_ext }} + checkout_ref: ${{ needs.verify.outputs.candidate_sha }} + coverage: + name: ci-test coverage + if: always() + needs: [verify, boards, linux] + runs-on: ubuntu-latest + outputs: + complete: ${{ steps.complete.outputs.complete }} + steps: + - id: complete + env: + EVENT_NAME: ${{ github.event_name }} + LABEL_PRESENT: ${{ contains(github.event.pull_request.labels.*.name, 'ci-test') }} + VERIFY: ${{ needs.verify.result }} + BOARDS: ${{ needs.boards.result }} + LINUX: ${{ needs.linux.result }} + run: | + if [ "$EVENT_NAME" = pull_request ] && [ "$LABEL_PRESENT" != true ]; then + echo 'complete=false' >> "$GITHUB_OUTPUT" + echo 'Optional tier is not selected on this PR; coverage is incomplete' >&2 + exit 1 + fi + for result in "$VERIFY" "$BOARDS" "$LINUX"; do + if [ "$result" != success ]; then + echo "coverage incomplete: $result" >&2 + exit 1 + fi + done + echo 'complete=true' >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/crate-gate.yml b/.github/workflows/crate-gate.yml index 5f905a38f..92c6b708a 100644 --- a/.github/workflows/crate-gate.yml +++ b/.github/workflows/crate-gate.yml @@ -5,13 +5,19 @@ on: branches: [main] pull_request: workflow_dispatch: + workflow_call: + inputs: + ref: + type: string + required: false + default: "" # Auto-cancel superseded PR runs: pushing again to a feature branch # supersedes the in-flight run instead of queueing behind it. Non-PR # events key on run_id so each gets its own group -- a shared group keeps # only ONE pending run, which would silently drop queued main SHAs. concurrency: - group: crate-gate.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + group: crate-gate.yml-${{ inputs.ref != '' && github.run_id || (github.event_name == 'pull_request' && github.ref || github.run_id) }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: @@ -21,6 +27,8 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} - name: Install uv uses: astral-sh/setup-uv@v3 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3b9ae03a7..c14bd6880 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -2,6 +2,12 @@ name: Documentation on: workflow_dispatch: {} + workflow_call: + inputs: + ref: + type: string + required: false + default: "" push: branches: [main] pull_request: @@ -12,7 +18,7 @@ on: # events key on run_id so each gets its own group -- a shared group keeps # only ONE pending run, which would silently drop queued main SHAs. concurrency: - group: docs.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + group: docs.yml-${{ inputs.ref != '' && github.run_id || (github.event_name == 'pull_request' && github.ref || github.run_id) }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: @@ -27,6 +33,8 @@ jobs: RUSTDOCFLAGS: "-D warnings" steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - name: Setup soldr id: setup-soldr uses: zackees/setup-soldr@v0 diff --git a/.github/workflows/dylint.yml b/.github/workflows/dylint.yml index feb7a655a..0308a4f57 100644 --- a/.github/workflows/dylint.yml +++ b/.github/workflows/dylint.yml @@ -1,14 +1,20 @@ name: Dylint -# Runs the custom dylints over the workspace. This is enforced as its own -# required GitHub status check rather than being folded into `./lint`, because -# Dylint builds the lint crates and rustc-dev driver and would force a heavy -# recompile on every local lint call. See #264 and #994. +# The required Dylint status checks policy files on ordinary PRs. The +# compiler-heavy Linux/Windows lint matrix runs through ci-full or dispatch. on: workflow_dispatch: {} - push: - branches: [main] + workflow_call: + inputs: + ref: + type: string + required: false + default: "" + run_full: + type: boolean + required: false + default: false pull_request: branches: [main] @@ -17,7 +23,7 @@ on: # events key on run_id so each gets its own group -- a shared group keeps # only ONE pending run, which would silently drop queued main SHAs. concurrency: - group: dylint.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + group: dylint.yml-${{ inputs.ref != '' && github.run_id || (github.event_name == 'pull_request' && github.ref || github.run_id) }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} # Read-only token. Every step here reads: checkout, a shallow `git fetch @@ -40,12 +46,23 @@ env: SOLDR_JOBS: "1" jobs: + policy: + name: Dylint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} + - uses: astral-sh/setup-uv@v3 + - name: Validate Dylint policy files + run: | + uv run --no-project python ci/check_dylint_allowlists.py + uv run --no-project python ci/enforce_platform_boundary.py --print-totals + dylint: - # The matrix `name` is pinned per-leg rather than derived, so the ubuntu - # leg keeps reporting as exactly `Dylint` — that string is a required - # status check, and letting the matrix rename it to `Dylint - # (ubuntu-latest)` would leave the required check permanently pending on - # every PR. The Windows leg is additive and reports under its own name. + if: inputs.run_full || github.event_name == 'workflow_dispatch' + # The ordinary policy job retains the required `Dylint` status name; + # these full-run names distinguish compiler-heavy validation. name: ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: @@ -55,7 +72,7 @@ jobs: matrix: include: - os: ubuntu-latest - name: Dylint + name: Dylint Full (linux) # FastLED/fbuild#1359 — `cargo check` only compiles the modules the # target platform selects, so every `#[cfg(windows)]` / # `platform/windows/**` module was invisible to a ubuntu-only gate. @@ -68,7 +85,7 @@ jobs: # actually bite — #875, #885, #890 and #912 were all Windows path # handling. - os: windows-latest - name: Dylint (windows) + name: Dylint Full (windows) timeout-minutes: 90 defaults: run: @@ -78,6 +95,8 @@ jobs: shell: bash steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - uses: astral-sh/setup-uv@v3 - name: Validate Dylint allowlist paths if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml index 0f47e8674..4fa02f77c 100644 --- a/.github/workflows/fmt.yml +++ b/.github/workflows/fmt.yml @@ -2,6 +2,12 @@ name: Formatting on: workflow_dispatch: {} + workflow_call: + inputs: + ref: + type: string + required: false + default: "" push: branches: [main] pull_request: @@ -12,7 +18,7 @@ on: # events key on run_id so each gets its own group -- a shared group keeps # only ONE pending run, which would silently drop queued main SHAs. concurrency: - group: fmt.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + group: fmt.yml-${{ inputs.ref != '' && github.run_id || (github.event_name == 'pull_request' && github.ref || github.run_id) }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: @@ -25,6 +31,8 @@ jobs: timeout-minutes: 15 steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - name: Setup soldr id: setup-soldr uses: zackees/setup-soldr@v0 diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index ba619b8f5..12b1dccb7 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -2,6 +2,12 @@ name: Min Rust Version (1.95.0) on: workflow_dispatch: {} + workflow_call: + inputs: + ref: + type: string + required: false + default: "" push: branches: [main] pull_request: @@ -12,7 +18,7 @@ on: # events key on run_id so each gets its own group -- a shared group keeps # only ONE pending run, which would silently drop queued main SHAs. concurrency: - group: msrv.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + group: msrv.yml-${{ inputs.ref != '' && github.run_id || (github.event_name == 'pull_request' && github.ref || github.run_id) }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: @@ -25,6 +31,8 @@ jobs: timeout-minutes: 20 steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - name: Setup soldr id: setup-soldr uses: zackees/setup-soldr@v0 diff --git a/.github/workflows/nightly-platforms.yml b/.github/workflows/nightly-platforms.yml index 29aa000a2..4364651cb 100644 --- a/.github/workflows/nightly-platforms.yml +++ b/.github/workflows/nightly-platforms.yml @@ -61,322 +61,382 @@ jobs: test_dir: "tests/platform/apollo3_red" env_name: "apollo3_red" firmware_ext: "bin" + workflow_aliases: ["build-apollo3_red.yml"] - workflow_name: "Apollo3 Thing Explorable" test_dir: "tests/platform/apollo3_thing_explorable" env_name: "apollo3_thing_explorable" firmware_ext: "bin" + workflow_aliases: ["build-apollo3_thing_explorable.yml"] - workflow_name: "ATmega8" test_dir: "tests/platform/atmega8" env_name: "atmega8" firmware_ext: "hex" + workflow_aliases: ["build-atmega8.yml"] - workflow_name: "ATmega8A" test_dir: "tests/platform/atmega8a" env_name: "atmega8a" firmware_ext: "hex" + workflow_aliases: ["build-atmega8a.yml"] - workflow_name: "ATtiny1604" test_dir: "tests/platform/attiny1604" env_name: "attiny1604" firmware_ext: "hex" + workflow_aliases: ["build-attiny1604.yml"] - workflow_name: "ATtiny1616" test_dir: "tests/platform/attiny1616" env_name: "attiny1616" firmware_ext: "hex" + workflow_aliases: ["build-attiny1616.yml"] - workflow_name: "ATtiny4313" test_dir: "tests/platform/attiny4313" env_name: "attiny4313" firmware_ext: "hex" + workflow_aliases: ["build-attiny4313.yml"] - workflow_name: "ATtiny85" test_dir: "tests/platform/attiny85" env_name: "attiny85" firmware_ext: "hex" + workflow_aliases: ["build-attiny85.yml"] - workflow_name: "ATtiny88" test_dir: "tests/platform/attiny88" env_name: "attiny88" firmware_ext: "hex" + workflow_aliases: ["build-attiny88.yml"] - workflow_name: "STM32 BlackPill F411CE" test_dir: "tests/platform/blackpill_f411ce" env_name: "blackpill" firmware_ext: "hex" + workflow_aliases: ["build-blackpill.yml"] - workflow_name: "STM32 BluePill F103C8" test_dir: "tests/platform/bluepill_f103c8" env_name: "bluepill" firmware_ext: "hex" + workflow_aliases: ["build-bluepill.yml"] - workflow_name: "CH32L103" test_dir: "tests/platform/ch32l103" env_name: "ch32l103" firmware_ext: "bin" + workflow_aliases: ["build-ch32l103.yml"] - workflow_name: "CH32V003" test_dir: "tests/platform/ch32v003" env_name: "ch32v003" firmware_ext: "bin" + workflow_aliases: ["build-ch32v003.yml"] - workflow_name: "CH32V006" test_dir: "tests/platform/ch32v006" env_name: "ch32v006" firmware_ext: "bin" + workflow_aliases: ["build-ch32v006.yml"] - workflow_name: "CH32V103" test_dir: "tests/platform/ch32v103" env_name: "ch32v103" firmware_ext: "bin" + workflow_aliases: ["build-ch32v103.yml"] - workflow_name: "CH32V203" test_dir: "tests/platform/ch32v203" env_name: "ch32v203" firmware_ext: "bin" + workflow_aliases: ["build-ch32v203.yml"] - workflow_name: "CH32V208" test_dir: "tests/platform/ch32v208" env_name: "ch32v208" firmware_ext: "bin" + workflow_aliases: ["build-ch32v208.yml"] - workflow_name: "CH32V303" test_dir: "tests/platform/ch32v303" env_name: "ch32v303" firmware_ext: "bin" + workflow_aliases: ["build-ch32v303.yml"] - workflow_name: "CH32V307" test_dir: "tests/platform/ch32v307" env_name: "ch32v307" firmware_ext: "bin" + workflow_aliases: ["build-ch32v307.yml"] - workflow_name: "CH32X035" test_dir: "tests/platform/ch32x035" env_name: "ch32x035" firmware_ext: "bin" + workflow_aliases: ["build-ch32x035.yml"] - workflow_name: "Teknic ClearCore (SAME53)" test_dir: "tests/platform/clearcore" env_name: "clearcore" firmware_ext: "bin" + workflow_aliases: ["build-clearcore.yml"] - workflow_name: "Arduino Due" test_dir: "tests/platform/due" env_name: "due" firmware_ext: "bin" + workflow_aliases: ["build-due.yml"] - workflow_name: "ESP32-C2" test_dir: "tests/platform/esp32c2" env_name: "esp32c2" firmware_ext: "bin" + workflow_aliases: ["build-esp32c2.yml"] - workflow_name: "ESP32-C3" test_dir: "tests/platform/esp32c3" env_name: "esp32c3" firmware_ext: "bin" + workflow_aliases: ["build-esp32c3.yml"] - workflow_name: "ESP32-C5" test_dir: "tests/platform/esp32c5" env_name: "esp32c5" firmware_ext: "bin" + workflow_aliases: ["build-esp32c5.yml"] - workflow_name: "ESP32-C6" test_dir: "tests/platform/esp32c6" env_name: "esp32c6" firmware_ext: "bin" + workflow_aliases: ["build-esp32c6.yml"] - workflow_name: "ESP32 Dev" test_dir: "tests/platform/esp32dev" env_name: "esp32dev" firmware_ext: "bin" + workflow_aliases: ["build-esp32dev.yml"] - workflow_name: "ESP32-H2" test_dir: "tests/platform/esp32h2" env_name: "esp32h2" firmware_ext: "bin" + workflow_aliases: ["build-esp32h2.yml"] - workflow_name: "ESP32-P4" test_dir: "tests/platform/esp32p4" env_name: "esp32p4" firmware_ext: "bin" + workflow_aliases: ["build-esp32p4.yml"] - workflow_name: "ESP32-S2" test_dir: "tests/platform/esp32s2" env_name: "esp32s2" firmware_ext: "bin" + workflow_aliases: ["build-esp32s2.yml"] - workflow_name: "ESP32-S3" test_dir: "tests/platform/esp32s3" env_name: "esp32s3" firmware_ext: "bin" + workflow_aliases: ["build-esp32s3.yml"] - workflow_name: "ESP8266" test_dir: "tests/platform/esp8266" env_name: "esp8266" firmware_ext: "bin" + workflow_aliases: ["build-esp8266.yml"] - workflow_name: "Arduino GIGA R1 M7" test_dir: "tests/platform/giga_r1_m7" env_name: "giga_r1" firmware_ext: "bin" + workflow_aliases: ["build-giga-r1.yml"] - workflow_name: "Arduino Leonardo" test_dir: "tests/platform/leonardo" env_name: "leonardo" firmware_ext: "hex" + workflow_aliases: ["build-leonardo.yml"] - workflow_name: "NXP LPCXpresso804" test_dir: "tests/platform/lpc804" env_name: "lpc804" firmware_ext: "bin" + workflow_aliases: ["build-lpc804.yml"] - workflow_name: "NXP LPC845" test_dir: "tests/platform/lpc845" env_name: "lpc845" firmware_ext: "bin" + workflow_aliases: ["build-lpc845.yml"] - workflow_name: "NXP LPC845-BRK" test_dir: "tests/platform/lpc845brk" env_name: "lpc845brk" firmware_ext: "bin" + workflow_aliases: ["build-lpc845brk.yml"] - workflow_name: "NXP LPCXpresso804" test_dir: "tests/platform/lpcxpresso804" env_name: "lpcxpresso804" firmware_ext: "bin" + workflow_aliases: ["build-lpcxpresso804.yml"] - workflow_name: "NXP LPCXpresso845-MAX" test_dir: "tests/platform/lpcxpresso845max" env_name: "lpcxpresso845max" firmware_ext: "bin" + workflow_aliases: ["build-lpcxpresso845max.yml"] - workflow_name: "Adafruit Matrix Portal M4" test_dir: "tests/platform/matrix_portal_m4" env_name: "matrix_portal_m4" firmware_ext: "bin" + workflow_aliases: ["build-matrix_portal_m4.yml"] - workflow_name: "Silicon Labs MGM240" test_dir: "tests/platform/mgm240" env_name: "mgm240" firmware_ext: "bin" + workflow_aliases: ["build-mgm240.yml"] - workflow_name: "Arduino Nano Every" test_dir: "tests/platform/nano_every" env_name: "nano_every" firmware_ext: "hex" - - workflow_name: "Arduino Nano Every" - test_dir: "tests/platform/nano_every" - env_name: "nano_every" - firmware_ext: "hex" + workflow_aliases: ["build-nano-every.yml", "build-nano_every.yml"] - workflow_name: "Nice!Nano nRF52840" test_dir: "tests/platform/nice_nano_nrf52840" env_name: "nice_nano_nrf52840" firmware_ext: "hex" + workflow_aliases: ["build-nice_nano_nrf52840.yml"] - workflow_name: "Adafruit Feather nRF52840 Sense" test_dir: "tests/platform/adafruit_feather_nrf52840_sense" env_name: "nrf52840_sense" firmware_ext: "hex" + workflow_aliases: ["build-nrf52840-sense.yml"] - workflow_name: "Nordic nRF52840-DK" test_dir: "tests/platform/nrf52840_dk" env_name: "nrf52840_dk" firmware_ext: "hex" + workflow_aliases: ["build-nrf52840_dk.yml"] - workflow_name: "nrfmicro nRF52840" test_dir: "tests/platform/nrfmicro_nrf52840" env_name: "nrfmicro_nrf52840" firmware_ext: "hex" + workflow_aliases: ["build-nrfmicro_nrf52840.yml"] - workflow_name: "ST Nucleo F429ZI" test_dir: "tests/platform/nucleo_f429zi" env_name: "nucleo_f429zi" firmware_ext: "hex" + workflow_aliases: ["build-nucleo-f429zi.yml", "build-nucleo_f429zi.yml"] - workflow_name: "ST Nucleo F439ZI" test_dir: "tests/platform/nucleo_f439zi" env_name: "nucleo_f439zi" firmware_ext: "hex" - - workflow_name: "ST Nucleo F429ZI" - test_dir: "tests/platform/nucleo_f429zi" - env_name: "nucleo_f429zi" - firmware_ext: "hex" - - workflow_name: "ST Nucleo F439ZI" - test_dir: "tests/platform/nucleo_f439zi" - env_name: "nucleo_f439zi" - firmware_ext: "hex" + workflow_aliases: ["build-nucleo-f439zi.yml", "build-nucleo_f439zi.yml"] - workflow_name: "Adafruit QT Py M0" test_dir: "tests/platform/qtpy_m0" env_name: "qtpy_m0" firmware_ext: "bin" + workflow_aliases: ["build-qtpy_m0.yml"] - workflow_name: "RP2040" test_dir: "tests/platform/rp2040" env_name: "rp2040" firmware_ext: "bin" + workflow_aliases: ["build-rp2040.yml"] - workflow_name: "RP2350" test_dir: "tests/platform/rp2350" env_name: "rp2350" firmware_ext: "bin" + workflow_aliases: ["build-rp2350.yml"] - workflow_name: "Raspberry Pi Pico" test_dir: "tests/platform/rpipico" env_name: "rpipico" firmware_ext: "bin" + workflow_aliases: ["build-rpipico.yml"] - workflow_name: "Raspberry Pi Pico 2" test_dir: "tests/platform/rpipico2" env_name: "rpipico2" firmware_ext: "bin" + workflow_aliases: ["build-rpipico2.yml"] - workflow_name: "Atmel SAM3X8E (Due)" test_dir: "tests/platform/sam3x8e_due" env_name: "sam3x8e_due" firmware_ext: "bin" + workflow_aliases: ["build-sam3x8e_due.yml"] - workflow_name: "Atmel SAMD21" test_dir: "tests/platform/samd21" env_name: "samd21" firmware_ext: "bin" + workflow_aliases: ["build-samd21.yml"] - workflow_name: "Arduino Zero (SAMD21)" test_dir: "tests/platform/samd21_zero" env_name: "samd21_zero" firmware_ext: "bin" + workflow_aliases: ["build-samd21_zero.yml"] - workflow_name: "Atmel SAMD51J" test_dir: "tests/platform/samd51j" env_name: "samd51j" firmware_ext: "bin" + workflow_aliases: ["build-samd51j.yml"] - workflow_name: "Atmel SAMD51P" test_dir: "tests/platform/samd51p" env_name: "samd51p" firmware_ext: "bin" + workflow_aliases: ["build-samd51p.yml"] - workflow_name: "STM32F103C8" test_dir: "tests/platform/stm32f103c8" env_name: "stm32f103c8" firmware_ext: "hex" + workflow_aliases: ["build-stm32f103c8.yml"] - workflow_name: "STM32F103CB" test_dir: "tests/platform/stm32f103cb" env_name: "stm32f103cb" firmware_ext: "hex" + workflow_aliases: ["build-stm32f103cb.yml"] - workflow_name: "STM32F103TB" test_dir: "tests/platform/stm32f103tb" env_name: "stm32f103tb" firmware_ext: "hex" + workflow_aliases: ["build-stm32f103tb.yml"] - workflow_name: "STM32F411CE" test_dir: "tests/platform/stm32f411ce" env_name: "stm32f411ce" firmware_ext: "hex" + workflow_aliases: ["build-stm32f411ce.yml"] - workflow_name: "STM32H747XI" test_dir: "tests/platform/stm32h747xi" env_name: "stm32h747xi" firmware_ext: "bin" + workflow_aliases: ["build-stm32h747xi.yml"] - workflow_name: "SuperMini nRF52840" test_dir: "tests/platform/supermini_nrf52840" env_name: "supermini_nrf52840" firmware_ext: "hex" + workflow_aliases: ["build-supermini_nrf52840.yml"] - workflow_name: "Teensy 3.0" test_dir: "tests/platform/teensy30" env_name: "teensy30" firmware_ext: "hex" + workflow_aliases: ["build-teensy30.yml"] - workflow_name: "Teensy 3.1" test_dir: "tests/platform/teensy31" env_name: "teensy31" firmware_ext: "hex" + workflow_aliases: ["build-teensy31.yml"] - workflow_name: "Teensy 3.2" test_dir: "tests/platform/teensy32" env_name: "teensy32" firmware_ext: "hex" + workflow_aliases: ["build-teensy32.yml"] - workflow_name: "Teensy 3.5" test_dir: "tests/platform/teensy35" env_name: "teensy35" firmware_ext: "hex" + workflow_aliases: ["build-teensy35.yml"] - workflow_name: "Teensy 3.6" test_dir: "tests/platform/teensy36" env_name: "teensy36" firmware_ext: "hex" + workflow_aliases: ["build-teensy36.yml"] - workflow_name: "Teensy 4.0" test_dir: "tests/platform/teensy40" env_name: "teensy40" firmware_ext: "hex" + workflow_aliases: ["build-teensy40.yml"] - workflow_name: "Teensy 4.1" test_dir: "tests/platform/teensy41" env_name: "teensy41" firmware_ext: "hex" + workflow_aliases: ["build-teensy41.yml"] - workflow_name: "Teensy LC" test_dir: "tests/platform/teensylc" env_name: "teensylc" firmware_ext: "hex" + workflow_aliases: ["build-teensylc.yml"] - workflow_name: "SparkFun Thing Plus Matter" test_dir: "tests/platform/sparkfun_thingplusmatter" env_name: "thingplusmatter" firmware_ext: "bin" + workflow_aliases: ["build-thingplusmatter.yml"] - workflow_name: "HY TinySTM103TB" test_dir: "tests/platform/hy_tinystm103tb" env_name: "tinystm" firmware_ext: "hex" + workflow_aliases: ["build-tinystm.yml"] - workflow_name: "Arduino Uno R4 WiFi" test_dir: "tests/platform/uno_r4_wifi" env_name: "uno_r4_wifi" firmware_ext: "bin" + workflow_aliases: ["build-uno-r4-wifi.yml", "build-uno_r4_wifi.yml"] - workflow_name: "Arduino Uno" test_dir: "tests/platform/uno" env_name: "uno" firmware_ext: "hex" - - workflow_name: "Arduino Uno R4 WiFi" - test_dir: "tests/platform/uno_r4_wifi" - env_name: "uno_r4_wifi" - firmware_ext: "bin" + workflow_aliases: ["build-uno.yml"] uses: ./.github/workflows/template_build.yml with: workflow-name: ${{ matrix.workflow_name }} diff --git a/.github/workflows/qemu-linux-runtime.yml b/.github/workflows/qemu-linux-runtime.yml index a8eeaac62..e88db1f74 100644 --- a/.github/workflows/qemu-linux-runtime.yml +++ b/.github/workflows/qemu-linux-runtime.yml @@ -10,20 +10,12 @@ name: QEMU Linux Runtime on: workflow_dispatch: {} - push: - branches: [main] - paths: - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-daemon/src/handlers/emulator/**' - - 'ci/build_qemu_linux_runtime.py' - - '.github/workflows/qemu-linux-runtime.yml' - pull_request: - branches: [main] - paths: - - 'crates/fbuild-toolchain/**' - - 'crates/fbuild-daemon/src/handlers/emulator/**' - - 'ci/build_qemu_linux_runtime.py' - - '.github/workflows/qemu-linux-runtime.yml' + workflow_call: + inputs: + ref: + type: string + required: false + default: "" # Auto-cancel superseded PR runs: pushing again to a feature branch # supersedes the in-flight run instead of queueing behind it. Non-PR @@ -52,6 +44,7 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false + ref: ${{ inputs.ref }} - name: Confirm the runner really is missing the libraries # Documents the premise of the test. Informational: a future runner diff --git a/.github/workflows/release-auto.yml b/.github/workflows/release-auto.yml index 157877463..875f4c0e0 100644 --- a/.github/workflows/release-auto.yml +++ b/.github/workflows/release-auto.yml @@ -1,17 +1,12 @@ name: Autonomous Release on: - push: - branches: [main] - 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: + candidate_sha: + description: "Exact 40-character commit SHA to build and validate" + required: true + type: string publish: description: "Publish GitHub Release and PyPI after building" required: false @@ -24,7 +19,7 @@ permissions: id-token: write concurrency: - group: release-auto-${{ github.ref_name }} + group: release-auto-${{ inputs.candidate_sha }} cancel-in-progress: false env: @@ -47,15 +42,33 @@ jobs: steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.candidate_sha }} - name: Check release eligibility id: prepare shell: bash env: PUBLISH_REQUESTED: ${{ inputs.publish }} + CANDIDATE_SHA: ${{ inputs.candidate_sha }} + WORKFLOW_SHA: ${{ github.sha }} run: | set -euo pipefail + if [[ ! "$CANDIDATE_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "candidate_sha must be an exact 40-character commit SHA" >&2 + exit 1 + fi + if [ "$CANDIDATE_SHA" != "$WORKFLOW_SHA" ]; then + echo "candidate $CANDIDATE_SHA differs from workflow revision $WORKFLOW_SHA; dispatch from a ref at the candidate commit" >&2 + exit 1 + fi + commit_sha="$(git rev-parse HEAD)" + if [ "$commit_sha" != "$CANDIDATE_SHA" ]; then + echo "checkout SHA $commit_sha does not match candidate $CANDIDATE_SHA" >&2 + exit 1 + fi + cargo_version="$( python - <<'PY' import tomllib @@ -77,14 +90,18 @@ jobs: fi version="v${cargo_version}" - commit_sha="$(git rev-parse HEAD)" release_ref="${commit_sha}" tag_exists="false" - tag_sha="" - if tag_sha="$(git ls-remote --exit-code --tags origin "refs/tags/${version}" | awk '{print $1}' | tail -n1)"; then + tag_sha="$(git ls-remote --tags origin "refs/tags/${version}" | awk '{print $1}' | tail -n1)" + if [ -n "$tag_sha" ]; then tag_exists="true" - release_ref="${version}" + git fetch --no-tags --depth=1 origin "refs/tags/${version}" + tag_commit_sha="$(git rev-parse 'FETCH_HEAD^{commit}')" + if [ "$tag_commit_sha" != "$CANDIDATE_SHA" ]; then + echo "existing tag $version points to $tag_commit_sha, not candidate $CANDIDATE_SHA" >&2 + exit 1 + fi fi pypi_json="$(mktemp)" @@ -92,14 +109,17 @@ jobs: latest="$(python -c 'import json,sys; print(json.load(open(sys.argv[1]))["info"]["version"])' "$pypi_json")" pypi_file_count="$(python -c 'import json,sys; data=json.load(open(sys.argv[1])); print(len(data.get("releases", {}).get(sys.argv[2], [])))' "$pypi_json" "$cargo_version")" else + if [ "${PUBLISH_REQUESTED:-false}" = "true" ]; then + echo "cannot verify existing PyPI files; refusing publication" >&2 + exit 1 + fi latest="0.0.0" pypi_file_count="0" fi - should_build="false" + should_build="true" should_publish_github="false" should_publish_pypi="false" - newest="$(printf '%s\n%s\n' "$latest" "$cargo_version" | sort -V | tail -n1)" # One wheel per PLATFORMS entry in ci/publish.py — derive the # expected count from the SOT instead of hardcoding it, so @@ -107,32 +127,30 @@ jobs: # (a stale hardcoded 4 bit the 2.3.22-2.3.24 window). expected_wheels="$(python -c 'import ci.publish; print(len(ci.publish.PLATFORMS))')" - if [ "${GITHUB_EVENT_NAME}" != "workflow_dispatch" ] \ + newest="$(printf '%s\n%s\n' "$latest" "$cargo_version" | sort -V | tail -n1)" + if [ "${PUBLISH_REQUESTED:-false}" = "true" ] \ && [ "$tag_exists" != "true" ] \ - && [ "$newest" = "$cargo_version" ] \ - && [ "$pypi_file_count" -lt "$expected_wheels" ]; then - should_build="true" - should_publish_github="true" - should_publish_pypi="true" + && [ "$newest" != "$cargo_version" ]; then + echo "candidate version $cargo_version is older than latest PyPI $latest" >&2 + exit 1 fi - # Manual dispatch is always at least a build. Publishing is an - # explicit opt-in checkbox so release dry-runs are the default. - if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then - should_build="true" - release_ref="${commit_sha}" - should_publish_github="false" - should_publish_pypi="false" - - if [ "${PUBLISH_REQUESTED:-false}" = "true" ]; then + # The version bump is ordinary CI. Only an explicit dispatch with + # publish=true can reach either publication job. + if [ "${PUBLISH_REQUESTED:-false}" = "true" ]; then + if [ "$tag_exists" != "true" ]; then should_publish_github="true" + fi + if [ "$pypi_file_count" -lt "$expected_wheels" ]; then should_publish_pypi="true" - if [ "$tag_exists" = "true" ]; then - release_ref="${version}" - fi fi fi + if [[ ! "$release_ref" =~ ^[0-9a-f]{40}$ ]]; then + echo "release_ref is not an exact commit SHA: $release_ref" >&2 + exit 1 + fi + { echo "version=${version}" echo "project_version=${cargo_version}" @@ -239,10 +257,32 @@ jobs: mac_cross_linux: ${{ matrix.mac_cross_linux == true }} ref: ${{ needs.prepare.outputs.release_ref }} + full-ci: + name: Full validation on release commit + needs: prepare + if: needs.prepare.outputs.should_build == 'true' + uses: ./.github/workflows/ci-full.yml + with: + candidate_sha: ${{ needs.prepare.outputs.release_ref }} + + runtime-coverage: + name: All-platform physical runtime coverage + needs: [prepare, full-ci] + if: needs.prepare.outputs.should_publish_github == 'true' || needs.prepare.outputs.should_publish_pypi == 'true' + runs-on: ubuntu-24.04 + steps: + - name: Require trusted runtime evidence + run: | + # ci-full proves the software and policy gates on this candidate SHA. + # Physical proof must come from trusted CI executing every supported + # board family; a dispatch input or self-attested file is not evidence. + echo '::error::physical-board runtime coverage for every supported platform is not available on this candidate SHA; publication is blocked' + exit 1 + publish: name: Publish GitHub release - needs: [prepare, build] - if: needs.prepare.outputs.should_publish_github == 'true' + needs: [prepare, build, full-ci, runtime-coverage] + if: needs.prepare.outputs.should_publish_github == 'true' && needs.full-ci.outputs.coverage == 'true' && needs.runtime-coverage.result == 'success' runs-on: ubuntu-24.04 timeout-minutes: 30 @@ -311,8 +351,8 @@ jobs: build-pypi: name: Build PyPI wheels - needs: [prepare, build] - if: needs.prepare.outputs.should_publish_pypi == 'true' + needs: [prepare, build, full-ci, runtime-coverage] + if: needs.prepare.outputs.should_publish_pypi == 'true' && needs.full-ci.outputs.coverage == 'true' && needs.runtime-coverage.result == 'success' runs-on: ubuntu-24.04 timeout-minutes: 30 @@ -386,8 +426,8 @@ jobs: publish-pypi: name: Publish PyPI - needs: [prepare, build-pypi] - if: needs.prepare.outputs.should_publish_pypi == 'true' + needs: [prepare, build-pypi, full-ci, runtime-coverage] + if: needs.prepare.outputs.should_publish_pypi == 'true' && needs.full-ci.outputs.coverage == 'true' && needs.runtime-coverage.result == 'success' runs-on: ubuntu-24.04 timeout-minutes: 20 environment: pypi diff --git a/.github/workflows/template_build.yml b/.github/workflows/template_build.yml index 058b89585..8e5275d91 100644 --- a/.github/workflows/template_build.yml +++ b/.github/workflows/template_build.yml @@ -19,6 +19,10 @@ on: required: true type: string description: "Firmware file extension (bin or hex)" + checkout_ref: + required: false + type: string + default: "" env: CARGO_TERM_COLOR: always @@ -38,6 +42,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v6 + with: + ref: ${{ inputs.checkout_ref }} - name: Setup soldr id: setup-soldr diff --git a/.github/workflows/validate-boards.yml b/.github/workflows/validate-boards.yml index 2efc6705f..3df8d8984 100644 --- a/.github/workflows/validate-boards.yml +++ b/.github/workflows/validate-boards.yml @@ -2,6 +2,12 @@ name: Validate Boards on: workflow_dispatch: {} + workflow_call: + inputs: + ref: + type: string + required: false + default: "" push: branches: [main] pull_request: @@ -12,7 +18,7 @@ on: # events key on run_id so each gets its own group -- a shared group keeps # only ONE pending run, which would silently drop queued main SHAs. concurrency: - group: validate-boards.yml-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + group: validate-boards.yml-${{ inputs.ref != '' && github.run_id || (github.event_name == 'pull_request' && github.ref || github.run_id) }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: @@ -31,6 +37,8 @@ jobs: PLATFORMIO_TEENSY_VERSION: "5.1.0" steps: - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} - uses: actions/setup-python@v6 with: python-version: "3.12" diff --git a/CLAUDE.md b/CLAUDE.md index 2026c0f8c..74e8a3998 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,7 +108,7 @@ soldr cargo run -p fbuild-config --bin enrich_boards # enrich from local Platfo ## Distribution -Releases ship via the **Autonomous Release** GitHub Action (`.github/workflows/release-auto.yml`). PyPI is the distribution channel; per-platform native binaries are built, assembled into wheels, and uploaded via PyPI trusted publishing — there is no local publish script. +Releases ship via the explicit-dispatch **Autonomous Release** GitHub Action (`.github/workflows/release-auto.yml`). PyPI is the distribution channel; per-platform native binaries are built, assembled into wheels, and uploaded via PyPI trusted publishing — there is no local publish script. To cut a release: @@ -116,8 +116,10 @@ To cut a release: # 1. Bump version in both files (must match) # Cargo.toml -> [workspace.package] version # pyproject.toml -> [project] version -# 2. Push the bump commit to main (do NOT push a tag manually — -# the action creates one only after the build + upload succeed) +# 2. Push the bump commit to main; this runs ordinary CI only. +# 3. Dispatch release-auto.yml with the exact commit SHA and publish=false +# for a dry run. publish=true is currently blocked by the missing trusted +# all-platform physical runtime result; no tag can be minted yet. ``` See [docs/RELEASING.md](docs/RELEASING.md) for the full flow, gating logic, and re-run instructions when a release stalls. diff --git a/ci/board_families.json b/ci/board_families.json index f3d8ec78f..7f3554b02 100644 --- a/ci/board_families.json +++ b/ci/board_families.json @@ -702,7 +702,8 @@ "env_name": "uno", "firmware_ext": "hex", "family": "avr", - "core": true + "core": true, + "fractional": true }, { "workflow": "build-uno_r4_wifi.yml", diff --git a/ci/render_workflows.py b/ci/render_workflows.py index 3ee7a57a5..aa6c10da2 100644 --- a/ci/render_workflows.py +++ b/ci/render_workflows.py @@ -36,6 +36,9 @@ COMMON_PATH = REPO / "ci" / "ci_common_paths.txt" WORKFLOWS_DIR = REPO / ".github" / "workflows" NIGHTLY_PATH = WORKFLOWS_DIR / "nightly-platforms.yml" +FULL_PATH = WORKFLOWS_DIR / "ci-full.yml" +TEST_PATH = WORKFLOWS_DIR / "ci-test.yml" +MINIMAL_PATH = WORKFLOWS_DIR / "ci-minimal.yml" BEGIN_MARKER = "# >>> RENDERED-ON-BEGIN (ci/render_workflows.py) -- do not edit by hand <<<\n" END_MARKER = "# >>> RENDERED-ON-END <<<\n" @@ -132,21 +135,263 @@ def render_concurrency_block(board: dict) -> str: def render_on_block(board: dict, families: dict, common_paths: list[str]) -> str: - paths = render_paths_for_board(board, families, common_paths) - paths_yaml = "\n".join(f" - '{p}'" for p in paths) return ( "on:\n" " workflow_dispatch: {}\n" " workflow_call: {}\n" + ) + render_concurrency_block(board) + + +def execution_boards(boards: list[dict]) -> list[dict]: + """Run identical build inputs once while retaining every workflow alias.""" + unique: dict[tuple[str, str, str], dict] = {} + for board in boards: + key = (board["test_dir"], board["env_name"], board["firmware_ext"]) + if key not in unique: + unique[key] = {**board, "workflow_aliases": [board["workflow"]]} + else: + unique[key]["workflow_aliases"].append(board["workflow"]) + return list(unique.values()) + + +def render_ci(boards: list[dict], tier: str) -> str: + full = tier == "full" + minimal = tier == "minimal" + selected = execution_boards(boards if full else [b for b in boards if b.get("fractional", False)]) + entries = "".join( + f" - workflow: {json.dumps(b['workflow'])}\n" + f" workflow_name: {json.dumps(b['workflow_name'])}\n" + f" test_dir: {json.dumps(b['test_dir'])}\n" + f" env_name: {json.dumps(b['env_name'])}\n" + f" firmware_ext: {json.dumps(b['firmware_ext'])}\n" + f" workflow_aliases: {json.dumps(b['workflow_aliases'])}\n" + for b in selected + ) + if not selected: + raise ValueError("fractional CI requires at least one selected board") + name = f"ci-{tier}" + trigger = ( + " workflow_call:\n" + " inputs:\n" + " candidate_sha:\n" + " type: string\n" + " required: true\n" + + ( + " outputs:\n" + " coverage:\n" + " description: 'All full validation jobs passed on the candidate SHA'\n" + " value: ${{ jobs.coverage.outputs.complete }}\n" + if full else "" + ) + + " workflow_dispatch:\n" + " inputs:\n" + " candidate_sha:\n" + " description: 'Exact 40-character commit SHA to validate'\n" + " type: string\n" + " required: true\n" + if not minimal else " push:\n" " branches: [main]\n" - " paths:\n" - f"{paths_yaml}\n" " pull_request:\n" " branches: [main]\n" - " paths:\n" - f"{paths_yaml}\n" - ) + render_concurrency_block(board) + " types: [opened, labeled, unlabeled, synchronize, reopened]\n" + " workflow_dispatch: {}\n" + ) + gate = ( + f" if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, '{name}')\n" + if not minimal else "" + ) + verified_ref = "${{ needs.verify.outputs.candidate_sha }}" + verify = ( + " verify:\n" + + gate + + " runs-on: ubuntu-latest\n" + + " outputs:\n" + + " candidate_sha: ${{ steps.sha.outputs.candidate_sha }}\n" + + " steps:\n" + + " - uses: actions/checkout@v6\n" + + " with:\n" + + " ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.candidate_sha }}\n" + + " persist-credentials: false\n" + + " - id: sha\n" + + " env:\n" + + " CANDIDATE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || inputs.candidate_sha }}\n" + + " WORKFLOW_SHA: ${{ github.sha }}\n" + + " EVENT_NAME: ${{ github.event_name }}\n" + + " run: |\n" + + " if [[ ! \"$CANDIDATE_SHA\" =~ ^[0-9a-f]{40}$ ]]; then\n" + + " echo 'candidate_sha must be an exact 40-character commit SHA' >&2\n" + + " exit 1\n" + + " fi\n" + + " if [ \"$EVENT_NAME\" != pull_request ] && [ \"$CANDIDATE_SHA\" != \"$WORKFLOW_SHA\" ]; then\n" + + " echo \"candidate $CANDIDATE_SHA differs from workflow revision $WORKFLOW_SHA; dispatch from a ref at the candidate commit\" >&2\n" + + " exit 1\n" + + " fi\n" + + " actual=$(git rev-parse HEAD)\n" + + " if [ \"$actual\" != \"$CANDIDATE_SHA\" ]; then\n" + + " echo \"checkout SHA $actual does not match $CANDIDATE_SHA\" >&2\n" + + " exit 1\n" + + " fi\n" + + " echo \"candidate_sha=$actual\" >> \"$GITHUB_OUTPUT\"\n" + if not minimal else "" + ) + host = ( + " windows:\n" + + gate + + " needs: verify\n" + + " uses: ./.github/workflows/check-windows.yml\n" + + " with:\n" + + f" ref: {verified_ref}\n" + + " dylint:\n" + + gate + + " needs: verify\n" + + " uses: ./.github/workflows/dylint.yml\n" + + " with:\n" + + f" ref: {verified_ref}\n" + + " run_full: true\n" + + " acceptance:\n" + + gate + + " needs: verify\n" + + " uses: ./.github/workflows/acceptance-205.yml\n" + + " with:\n" + + f" ref: {verified_ref}\n" + + " bench:\n" + + gate + + " needs: verify\n" + + " uses: ./.github/workflows/bench-205.yml\n" + + " with:\n" + + f" ref: {verified_ref}\n" + + " qemu:\n" + + gate + + " needs: verify\n" + + " uses: ./.github/workflows/qemu-linux-runtime.yml\n" + + " with:\n" + + f" ref: {verified_ref}\n" + if full else "" + ) + policy = "".join( + f" {job}:\n" + + gate + + " needs: verify\n" + + f" uses: ./.github/workflows/{workflow}\n" + + " with:\n" + + f" ref: {verified_ref}\n" + for job, workflow in ( + ("fmt", "fmt.yml"), + ("docs", "docs.yml"), + ("msrv", "msrv.yml"), + ("validate_boards", "validate-boards.yml"), + ("crate_gate", "crate-gate.yml"), + ) + ) if full else "" + return ( + f"# Generated by ci/render_workflows.py from ci/board_families.json.\n" + f"name: {name}\n\n" + "on:\n" + trigger + "\n" + "concurrency:\n" + f" group: {name}-${{{{ github.event_name == 'pull_request' && github.ref || github.run_id }}}}\n" + " cancel-in-progress: ${{ github.event_name == 'pull_request' }}\n\n" + "jobs:\n" + + verify + + " linux:\n" + + (" if: github.event_name != 'pull_request' || (!contains(github.event.pull_request.labels.*.name, 'ci-test') && !contains(github.event.pull_request.labels.*.name, 'ci-full'))\n" if minimal else gate) + + ("" if minimal else " needs: verify\n") + + " uses: ./.github/workflows/check-ubuntu.yml\n" + + " with:\n" + + (" ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}\n" if minimal else f" ref: {verified_ref}\n") + + host + + policy + + ("" if minimal else + " boards:\n" + + gate + + " needs: verify\n" + + " name: ${{ matrix.workflow_name }}\n" + + " strategy:\n" + + " fail-fast: false\n" + + " matrix:\n" + + " include:\n" + + entries + + " uses: ./.github/workflows/template_build.yml\n" + + " with:\n" + + " workflow-name: ${{ matrix.workflow_name }}\n" + + " test-dir: ${{ matrix.test_dir }}\n" + + " env-name: ${{ matrix.env_name }}\n" + + " firmware-ext: ${{ matrix.firmware_ext }}\n" + + f" checkout_ref: {verified_ref}\n") + + ("" if minimal else + " coverage:\n" + + (" name: Full coverage\n" if full else " name: ci-test coverage\n") + + " if: always()\n" + + (" needs: [verify, boards, linux, windows, dylint, acceptance, bench, qemu, fmt, docs, msrv, validate_boards, crate_gate]\n" if full else " needs: [verify, boards, linux]\n") + + " runs-on: ubuntu-latest\n" + + " outputs:\n" + + " complete: ${{ steps.complete.outputs.complete }}\n" + + " steps:\n" + + " - id: complete\n" + + " env:\n" + + " EVENT_NAME: ${{ github.event_name }}\n" + + f" LABEL_PRESENT: ${{{{ contains(github.event.pull_request.labels.*.name, '{name}') }}}}\n" + + " VERIFY: ${{ needs.verify.result }}\n" + + " BOARDS: ${{ needs.boards.result }}\n" + + " LINUX: ${{ needs.linux.result }}\n" + + (" WINDOWS: ${{ needs.windows.result }}\n" + " DYLINT: ${{ needs.dylint.result }}\n" + " ACCEPTANCE: ${{ needs.acceptance.result }}\n" + " BENCH: ${{ needs.bench.result }}\n" + " QEMU: ${{ needs.qemu.result }}\n" + " FMT: ${{ needs.fmt.result }}\n" + " DOCS: ${{ needs.docs.result }}\n" + " MSRV: ${{ needs.msrv.result }}\n" + " VALIDATE_BOARDS: ${{ needs.validate_boards.result }}\n" + " CRATE_GATE: ${{ needs.crate_gate.result }}\n" if full else "") + + " run: |\n" + + " if [ \"$EVENT_NAME\" = pull_request ] && [ \"$LABEL_PRESENT\" != true ]; then\n" + + " echo 'complete=false' >> \"$GITHUB_OUTPUT\"\n" + + " echo 'Optional tier is not selected on this PR; coverage is incomplete' >&2\n" + + " exit 1\n" + + " fi\n" + + (" for result in \"$VERIFY\" \"$BOARDS\" \"$LINUX\" \"$WINDOWS\" \"$DYLINT\" \"$ACCEPTANCE\" \"$BENCH\" \"$QEMU\" \"$FMT\" \"$DOCS\" \"$MSRV\" \"$VALIDATE_BOARDS\" \"$CRATE_GATE\"; do\n" if full else " for result in \"$VERIFY\" \"$BOARDS\" \"$LINUX\"; do\n") + + " if [ \"$result\" != success ]; then\n" + + " echo \"coverage incomplete: $result\" >&2\n" + + " exit 1\n" + + " fi\n" + + " done\n" + + " echo 'complete=true' >> \"$GITHUB_OUTPUT\"\n" + ) + + (" test:\n" + " if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ci-test') && !contains(github.event.pull_request.labels.*.name, 'ci-full')\n" + " uses: ./.github/workflows/ci-test.yml\n" + " with:\n" + " candidate_sha: ${{ github.event.pull_request.head.sha }}\n" + " full:\n" + " if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ci-full')\n" + " uses: ./.github/workflows/ci-full.yml\n" + " with:\n" + " candidate_sha: ${{ github.event.pull_request.head.sha }}\n" + " selected-coverage:\n" + " name: CI selected coverage\n" + " if: always()\n" + " needs: [linux, test, full]\n" + " runs-on: ubuntu-latest\n" + " steps:\n" + " - env:\n" + " LINUX: ${{ needs.linux.result }}\n" + " TEST_SELECTED: ${{ contains(github.event.pull_request.labels.*.name, 'ci-test') }}\n" + " TEST_RESULT: ${{ needs.test.result }}\n" + " FULL_SELECTED: ${{ contains(github.event.pull_request.labels.*.name, 'ci-full') }}\n" + " FULL_RESULT: ${{ needs.full.result }}\n" + " FULL_COVERAGE: ${{ needs.full.outputs.coverage }}\n" + " run: |\n" + " if [ \"$FULL_SELECTED\" = true ]; then\n" + " test \"$FULL_RESULT\" = success\n" + " test \"$FULL_COVERAGE\" = true\n" + " elif [ \"$TEST_SELECTED\" = true ]; then\n" + " test \"$TEST_RESULT\" = success\n" + " else\n" + " test \"$LINUX\" = success\n" + " fi\n" + if minimal else "") + ) def _find_on_and_jobs(lines: list[str]) -> tuple[int, int]: @@ -218,12 +463,13 @@ def render_nightly(boards: list[dict]) -> str: expressions. """ matrix_entries: list[str] = [] - for b in boards: + for b in execution_boards(boards): matrix_entries.append( f" - workflow_name: {json.dumps(b['workflow_name'])}\n" f" test_dir: {json.dumps(b['test_dir'])}\n" f" env_name: {json.dumps(b['env_name'])}\n" f" firmware_ext: {json.dumps(b['firmware_ext'])}\n" + f" workflow_aliases: {json.dumps(b['workflow_aliases'])}\n" ) jobs_yaml = ( " build:\n" @@ -352,6 +598,9 @@ def main() -> int: write_if_changed(path, new, args.check, drift, updated) write_if_changed(NIGHTLY_PATH, render_nightly(boards), args.check, drift, updated) + write_if_changed(MINIMAL_PATH, render_ci(boards, "minimal"), args.check, drift, updated) + write_if_changed(TEST_PATH, render_ci(boards, "test"), args.check, drift, updated) + write_if_changed(FULL_PATH, render_ci(boards, "full"), args.check, drift, updated) if args.check and drift: print("Drift detected -- the following workflows are out of sync with the SOT:", file=sys.stderr) diff --git a/ci/test_fractional_workflows.py b/ci/test_fractional_workflows.py new file mode 100644 index 000000000..bfd22019c --- /dev/null +++ b/ci/test_fractional_workflows.py @@ -0,0 +1,209 @@ +"""Contract checks for the generated fractional CI entry points.""" + +import unittest +import os +import subprocess +import tempfile + +import yaml + +from ci import render_workflows + + +class FractionalWorkflowTests(unittest.TestCase): + def load(self, name): + # PyYAML treats `on` as a YAML 1.1 boolean; inspect its True key. + return yaml.safe_load((render_workflows.WORKFLOWS_DIR / name).read_text()) + + def test_board_workflows_have_no_independent_pr_or_main_trigger(self): + for board in render_workflows.load_sot()["boards"]: + with self.subTest(board=board["workflow"]): + events = self.load(board["workflow"])[True] + self.assertNotIn("push", events) + self.assertNotIn("pull_request", events) + self.assertIn("workflow_call", events) + + def test_full_matrix_matches_every_supported_board(self): + boards = render_workflows.load_sot()["boards"] + full = self.load("ci-full.yml") + self.assertNotIn("paths", full[True].get("pull_request", {})) + matrix = full["jobs"]["boards"]["strategy"]["matrix"]["include"] + self.assertEqual({b["workflow"] for b in boards}, {alias for b in matrix for alias in b["workflow_aliases"]}) + self.assertEqual(len(render_workflows.execution_boards(boards)), len(matrix)) + self.assertEqual(len(matrix), len({(b["test_dir"], b["env_name"], b["firmware_ext"]) for b in matrix})) + self.assertFalse(full["jobs"]["boards"]["strategy"]["fail-fast"]) + self.assertEqual("./.github/workflows/check-ubuntu.yml", full["jobs"]["linux"]["uses"]) + self.assertEqual("./.github/workflows/check-windows.yml", full["jobs"]["windows"]["uses"]) + self.assertEqual("./.github/workflows/dylint.yml", full["jobs"]["dylint"]["uses"]) + self.assertTrue(full["jobs"]["dylint"]["with"]["run_full"]) + dylint = self.load("dylint.yml") + self.assertEqual("Dylint", dylint["jobs"]["policy"]["name"]) + self.assertIn("inputs.run_full", dylint["jobs"]["dylint"]["if"]) + for job, workflow in ( + ("acceptance", "acceptance-205.yml"), + ("bench", "bench-205.yml"), + ("qemu", "qemu-linux-runtime.yml"), + ): + self.assertEqual(f"./.github/workflows/{workflow}", full["jobs"][job]["uses"]) + self.assertEqual("${{ needs.verify.outputs.candidate_sha }}", full["jobs"][job]["with"]["ref"]) + self.assertNotIn("pull_request", self.load(workflow)[True]) + self.assertEqual("${{ needs.verify.outputs.candidate_sha }}", full["jobs"]["boards"]["with"]["checkout_ref"]) + for job, workflow_name in ( + ("fmt", "fmt.yml"), ("docs", "docs.yml"), ("msrv", "msrv.yml"), + ("validate_boards", "validate-boards.yml"), ("crate_gate", "crate-gate.yml"), + ): + self.assertEqual(f"./.github/workflows/{workflow_name}", full["jobs"][job]["uses"]) + self.assertEqual("${{ needs.verify.outputs.candidate_sha }}", full["jobs"][job]["with"]["ref"]) + self.assertIn("workflow_call", self.load(workflow_name)[True]) + self.assertEqual("${{ inputs.ref }}", self.load(workflow_name)["jobs"][next(iter(self.load(workflow_name)["jobs"]))]["steps"][0]["with"]["ref"]) + self.assertIn("inputs.ref != ''", self.load(workflow_name)["concurrency"]["group"]) + + def test_fast_matrix_matches_selected_boards(self): + selected = [b["workflow"] for b in render_workflows.load_sot()["boards"] if b.get("fractional")] + fast = self.load("ci-test.yml") + matrix = fast["jobs"]["boards"]["strategy"]["matrix"]["include"] + self.assertEqual(selected, [b["workflow"] for b in matrix]) + self.assertEqual(1, len(matrix)) + full_matrix = self.load("ci-full.yml")["jobs"]["boards"]["strategy"]["matrix"]["include"] + for cell in matrix: + self.assertIn(cell, full_matrix) + + def test_nightly_deduplicates_identical_builds_but_keeps_aliases(self): + boards = render_workflows.load_sot()["boards"] + nightly = self.load("nightly-platforms.yml") + matrix = nightly["jobs"]["build"]["strategy"]["matrix"]["include"] + self.assertEqual(len(render_workflows.execution_boards(boards)), len(matrix)) + self.assertEqual({b["workflow"] for b in boards}, {alias for b in matrix for alias in b["workflow_aliases"]}) + + def test_ordinary_minimal_and_opt_in_test_are_distinct(self): + minimal = self.load("ci-minimal.yml") + fast = self.load("ci-test.yml") + self.assertIn("push", minimal[True]) + self.assertIn("pull_request", minimal[True]) + self.assertEqual({"linux", "test", "full", "selected-coverage"}, set(minimal["jobs"])) + self.assertIn("github.event.pull_request.head.sha", minimal["jobs"]["linux"]["with"]["ref"]) + self.assertIn("ci-test", minimal["jobs"]["linux"]["if"]) + self.assertIn("ci-full", minimal["jobs"]["linux"]["if"]) + self.assertNotIn("push", fast[True]) + self.assertIn("workflow_call", fast[True]) + self.assertIn("ci-test", fast["jobs"]["boards"]["if"]) + + def test_labeled_tiers_follow_head_sha_and_invalidate_removed_labels(self): + minimal = self.load("ci-minimal.yml") + self.assertEqual( + ["opened", "labeled", "unlabeled", "synchronize", "reopened"], + minimal[True]["pull_request"]["types"], + ) + for tier in ("test", "full"): + with self.subTest(tier=tier): + workflow = self.load(f"ci-{tier}.yml") + self.assertNotIn("pull_request", workflow[True]) + selected = minimal["jobs"][tier] + self.assertIn(f"'ci-{tier}'", selected["if"]) + self.assertEqual(f"./.github/workflows/ci-{tier}.yml", selected["uses"]) + self.assertEqual("${{ github.event.pull_request.head.sha }}", selected["with"]["candidate_sha"]) + if tier == "test": + self.assertIn("!contains(github.event.pull_request.labels.*.name, 'ci-full')", selected["if"]) + self.assertTrue(workflow[True]["workflow_dispatch"]["inputs"]["candidate_sha"]["required"]) + self.assertTrue(workflow[True]["workflow_call"]["inputs"]["candidate_sha"]["required"]) + self.assertIn("github.event.pull_request.head.sha", workflow["jobs"]["verify"]["steps"][0]["with"]["ref"]) + verify_step = workflow["jobs"]["verify"]["steps"][1] + self.assertEqual("${{ github.sha }}", verify_step["env"]["WORKFLOW_SHA"]) + self.assertIn('"$CANDIDATE_SHA" != "$WORKFLOW_SHA"', verify_step["run"]) + self.assertIn("always()", workflow["jobs"]["coverage"]["if"]) + self.assertIn("complete=false", workflow["jobs"]["coverage"]["steps"][0]["run"]) + self.assertNotIn("invalidate", workflow["jobs"]) + self.assertEqual("verify", workflow["jobs"]["boards"]["needs"]) + self.assertEqual("${{ needs.verify.outputs.candidate_sha }}", workflow["jobs"]["boards"]["with"]["checkout_ref"]) + + def test_selected_coverage_requires_every_requested_tier(self): + minimal = self.load("ci-minimal.yml") + job = minimal["jobs"]["selected-coverage"] + self.assertEqual({"linux", "test", "full"}, set(job["needs"])) + self.assertIn("always()", job["if"]) + script = job["steps"][0]["run"] + base = {**os.environ, "LINUX": "success", "TEST_SELECTED": "false", "TEST_RESULT": "skipped", "FULL_SELECTED": "false", "FULL_RESULT": "skipped", "FULL_COVERAGE": ""} + cases = [ + ({}, 0), + ({"TEST_SELECTED": "true", "TEST_RESULT": "skipped"}, 1), + ({"TEST_SELECTED": "true", "TEST_RESULT": "success", "LINUX": "skipped"}, 0), + ({"FULL_SELECTED": "true", "FULL_RESULT": "skipped"}, 1), + ({"FULL_SELECTED": "true", "FULL_RESULT": "success", "FULL_COVERAGE": "false"}, 1), + ({"FULL_SELECTED": "true", "FULL_RESULT": "success", "FULL_COVERAGE": "true", "LINUX": "skipped"}, 0), + ({"TEST_SELECTED": "true", "TEST_RESULT": "skipped", "FULL_SELECTED": "true", "FULL_RESULT": "success", "FULL_COVERAGE": "true", "LINUX": "skipped"}, 0), + ({"LINUX": "failure"}, 1), + ] + for overrides, expected in cases: + with self.subTest(overrides=overrides): + result = subprocess.run(["bash", "-e", "-c", script], env={**base, **overrides}, capture_output=True, text=True) + self.assertEqual(expected, result.returncode) + + def test_full_coverage_sentinel_and_release_gate(self): + full = self.load("ci-full.yml") + self.assertEqual( + {"verify", "boards", "linux", "windows", "dylint", "acceptance", "bench", "qemu", "fmt", "docs", "msrv", "validate_boards", "crate_gate"}, + set(full["jobs"]["coverage"]["needs"]), + ) + self.assertEqual("${{ jobs.coverage.outputs.complete }}", full[True]["workflow_call"]["outputs"]["coverage"]["value"]) + script = full["jobs"]["coverage"]["steps"][0]["run"] + env = {**os.environ, "EVENT_NAME": "workflow_call", "LABEL_PRESENT": "false"} + env.update({key: "success" for key in ("VERIFY", "BOARDS", "LINUX", "WINDOWS", "DYLINT", "ACCEPTANCE", "BENCH", "QEMU", "FMT", "DOCS", "MSRV", "VALIDATE_BOARDS", "CRATE_GATE")}) + with tempfile.NamedTemporaryFile() as output: + env["GITHUB_OUTPUT"] = output.name + for missing in ("FMT", "DOCS", "MSRV", "VALIDATE_BOARDS", "CRATE_GATE"): + with self.subTest(missing=missing): + result = subprocess.run(["bash", "-e", "-c", script], env={**env, missing: "skipped"}, capture_output=True, text=True) + self.assertNotEqual(0, result.returncode) + self.assertNotIn("complete=true", output.read().decode()) + release = self.load("release-auto.yml") + self.assertEqual("${{ needs.prepare.outputs.release_ref }}", release["jobs"]["full-ci"]["with"]["candidate_sha"]) + for job in ("publish", "publish-pypi"): + self.assertIn("needs.full-ci.outputs.coverage == 'true'", release["jobs"][job]["if"]) + + def test_release_publication_waits_for_full_validation(self): + release = self.load("release-auto.yml") + full = release["jobs"]["full-ci"] + self.assertEqual("./.github/workflows/ci-full.yml", full["uses"]) + self.assertEqual("${{ needs.prepare.outputs.release_ref }}", full["with"]["candidate_sha"]) + self.assertEqual("${{ needs.prepare.outputs.release_ref }}", release["jobs"]["build"]["with"]["ref"]) + prepare_script = next(step["run"] for step in release["jobs"]["prepare"]["steps"] if step.get("id") == "prepare") + self.assertIn("git rev-parse 'FETCH_HEAD^{commit}'", prepare_script) + self.assertIn("[[ ! \"$release_ref\" =~ ^[0-9a-f]{40}$ ]]", prepare_script) + self.assertIn("full-ci", release["jobs"]["publish"]["needs"]) + self.assertIn("full-ci", release["jobs"]["publish-pypi"]["needs"]) + + def test_release_requires_explicit_exact_candidate(self): + release = self.load("release-auto.yml") + events = release[True] + self.assertNotIn("push", events) + self.assertNotIn("pull_request", events) + self.assertTrue(events["workflow_dispatch"]["inputs"]["candidate_sha"]["required"]) + self.assertEqual( + "${{ inputs.candidate_sha }}", + release["jobs"]["prepare"]["steps"][0]["with"]["ref"], + ) + prepare_script = next(step["run"] for step in release["jobs"]["prepare"]["steps"] if step.get("id") == "prepare") + self.assertIn("[[ ! \"$CANDIDATE_SHA\" =~ ^[0-9a-f]{40}$ ]]", prepare_script) + self.assertIn('"$CANDIDATE_SHA" != "$WORKFLOW_SHA"', prepare_script) + prepare_step = next(step for step in release["jobs"]["prepare"]["steps"] if step.get("id") == "prepare") + self.assertEqual("${{ github.sha }}", prepare_step["env"]["WORKFLOW_SHA"]) + self.assertIn('if [ "$commit_sha" != "$CANDIDATE_SHA" ]', prepare_script) + self.assertNotIn('if [ "${GITHUB_EVENT_NAME}" != "workflow_dispatch" ]', prepare_script) + + def test_publication_waits_for_trusted_runtime_coverage(self): + release = self.load("release-auto.yml") + runtime = release["jobs"]["runtime-coverage"] + self.assertIn("full-ci", runtime["needs"]) + self.assertIn("publish", runtime["if"]) + self.assertEqual("prepare", release["jobs"]["build"]["needs"]) + self.assertEqual("prepare", release["jobs"]["full-ci"]["needs"]) + guard_script = runtime["steps"][0]["run"] + self.assertIn("physical-board runtime coverage", guard_script) + self.assertIn("exit 1", guard_script) + self.assertNotIn("runtime_coverage", release[True]["workflow_dispatch"]["inputs"]) + for job in ("publish", "build-pypi", "publish-pypi"): + self.assertIn("runtime-coverage", release["jobs"][job]["needs"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 0445ccb83..df06a6728 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -9,52 +9,46 @@ fbuild is published to PyPI by the **Autonomous Release** GitHub Action (`.githu # 2. Bump it in BOTH files; the workflow refuses to release if they differ. # - Cargo.toml [workspace.package].version # - pyproject.toml [project].version -# 3. Commit and push to main. -# DO NOT push a v tag — the action only triggers when the -# tag is absent, and it creates the tag itself after the upload -# succeeds. +# 3. Commit and push to main. This runs ordinary CI only. git commit -am "chore: bump version to X.Y.Z" git push origin main +# 4. After reviewing the exact candidate commit, dispatch a full dry run. +candidate_sha=$(git rev-parse HEAD) +gh workflow run release-auto.yml --repo FastLED/fbuild --ref main \ + -f candidate_sha="$candidate_sha" -f publish=false ``` -That's the entire happy path. Wait for the action to complete (~10-15 min for the full matrix + upload); the new wheels appear at `https://pypi.org/project/fbuild/X.Y.Z/`. +The dispatch requires a 40-character commit SHA equal to the head commit of the dispatch ref (`--ref main` in the example). This keeps the loaded workflow definitions and generated board matrix on the same revision as the code under test. If `main` has advanced, dispatch from a ref at the candidate commit. `publish=false` runs the native builds and the software jobs wired into `ci-full`, including fmt, docs, MSRV, board validation, and crate gates. **Publication is currently blocked:** there is no trusted same-SHA physical-board runtime result for every supported platform. `publish=true` fails at `runtime-coverage` before any tag or upload. Keep that guard until trusted hardware proof is available. ## What the action actually does ``` release-auto.yml ├── prepare ── compute candidate version, check tag + PyPI state +├── ci-full ── validate exact candidate SHA and every board/platform job ├── build (matrix) ── build native binaries for 6 targets ├── build-pypi ── call `ci/publish.py::build_all_wheels` → 4 wheels ├── smoke test ── pip-install one wheel, run `fbuild --version` +├── runtime-coverage ── blocks publication until physical-board proof exists ├── publish ── create GitHub release + push v tag └── publish-pypi ── upload wheels via trusted publishing (OIDC) ``` -The `prepare` job is the trigger gate. It runs on every push that touches `Cargo.toml` or `pyproject.toml`, and decides whether the rest of the pipeline runs: +The `prepare` job runs only on an explicit `workflow_dispatch`. It verifies that checkout HEAD matches `candidate_sha`, refuses an existing version tag pointing to another commit, and decides which publication jobs are needed. A version-file push cannot start this workflow. ``` -should_build = true IF (tag does not exist) - AND (version on disk >= newest known PyPI version) - AND (PyPI has < len(PLATFORMS) files for this version) +should_build = true for an explicit candidate dispatch +should_publish_github = true IF publish=true AND tag does not exist +should_publish_pypi = true IF publish=true AND PyPI has fewer than expected wheels ``` -The file-count guard exists because a complete `fbuild` release means one wheel per `PLATFORMS` entry in `ci/publish.py` (currently 4: Linux x86_64, Linux aarch64, macOS aarch64, Windows x86_64). Anything less is a partial / stranded release and the prep job will refuse to "fix it" by uploading more files to the same version. Both this gate and the post-upload "Verify all wheels visible on PyPI" check derive their expected counts at run time (from `ci/publish.py::PLATFORMS` and the built wheels respectively) — a stale hardcoded 4 broke the verify gate during the 2.3.22-2.3.24 window. Note the build matrix has more lanes than wheels: the x86_64-apple-darwin and aarch64-pc-windows-msvc binaries ship via the GitHub release archives only (Intel Macs install the arm64 wheel via its dual macosx tag; ARM Windows uses the win_amd64 wheel via emulation). +The file-count guard exists because a complete `fbuild` release means one wheel per `PLATFORMS` entry in `ci/publish.py` (currently 4: Linux x86_64, Linux aarch64, macOS aarch64, Windows x86_64). Anything less is a partial / stranded release; an explicit retry on the same candidate SHA can rebuild and upload the missing wheels after full CI and physical-board runtime coverage pass. Both this gate and the post-upload "Verify all wheels visible on PyPI" check derive their expected counts at run time (from `ci/publish.py::PLATFORMS` and the built wheels respectively) — a stale hardcoded 4 broke the verify gate during the 2.3.22-2.3.24 window. Note the build matrix has more lanes than wheels: the x86_64-apple-darwin and aarch64-pc-windows-msvc binaries ship via the GitHub release archives only (Intel Macs install the arm64 wheel via its dual macosx tag; ARM Windows uses the win_amd64 wheel via emulation). ## Common failure modes ### "I pushed the version bump but nothing happened" -The most common cause is a manually-pushed tag. If `v` already exists on the remote when the `prepare` job runs, `should_build` stays false and every downstream job is skipped — the action conclusion shows `success` because nothing failed, but no wheels were built. Symptom: action completed quickly (<1 min), no `build` jobs ran. - -**Fix:** delete the tag and re-run the workflow. - -```bash -git push --delete origin v -gh workflow run release-auto.yml --repo FastLED/fbuild --ref main -``` - -Then `gh run watch` to confirm `should_build=true` this time. +That is expected: the version bump runs ordinary CI. Dispatch `release-auto.yml` with the exact candidate SHA when ready. Do not create the version tag manually. ### Cargo.toml and pyproject.toml versions disagree @@ -62,10 +56,11 @@ The `prepare` job aborts with a non-zero exit if `[workspace.package].version` ( ### A wheel built but never uploaded (partial release) -Re-run via `workflow_dispatch`. The fallback branch in `prepare` allows builds when the tag exists but PyPI has fewer than the expected number of files — but only on a manual dispatch: +Once trusted physical-board coverage is connected, re-run the same candidate SHA with `publish=true`. `prepare` verifies that the existing tag points to that SHA and rebuilds the missing wheels: ```bash -gh workflow run release-auto.yml --repo FastLED/fbuild --ref main +gh workflow run release-auto.yml --repo FastLED/fbuild --ref main \ + -f candidate_sha= -f publish=true ``` The action will rebuild the missing wheels and upload, leaving the existing wheels in place. PyPI does not let you re-upload the same filename, so the new run produces fresh sdists/wheels only for the missing platforms. From b72472a514aeaf726d65577cb792cc297732eb05 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 23 Sep 2026 05:12:11 -0700 Subject: [PATCH 2/6] test: retire release push-path assertion after dispatch migration --- crates/fbuild-python/tests/pyo3_policy.rs | 28 ----------------------- 1 file changed, 28 deletions(-) diff --git a/crates/fbuild-python/tests/pyo3_policy.rs b/crates/fbuild-python/tests/pyo3_policy.rs index d0f5f66dc..44b44af77 100644 --- a/crates/fbuild-python/tests/pyo3_policy.rs +++ b/crates/fbuild-python/tests/pyo3_policy.rs @@ -87,20 +87,6 @@ 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, @@ -209,10 +195,7 @@ 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(); let workflow_lines = workflow.lines().collect::>(); - let release_workflow_lines = release_workflow.lines().collect::>(); assert_eq!( yaml_step_mapping_value(&workflow_lines, "Setup soldr", &["with", "version"]), @@ -252,15 +235,4 @@ fn native_release_workflow_uses_current_cross_toolchains() { "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", - ] { - assert!( - release_paths.contains(&release_input), - "release workflow fixes must retrigger an incomplete publication: {release_input}" - ); - } } From cff5358e243548ceb7bf41bf84d7fe4f4bd31a84 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 23 Sep 2026 13:16:10 -0700 Subject: [PATCH 3/6] ci: test both macOS hosts in full and release validation --- .github/workflows/build.yml | 2 +- .github/workflows/check-macos.yml | 47 ++++++++++++++++++++++++++++++ .github/workflows/ci-full.yml | 11 +++++-- .github/workflows/release-auto.yml | 28 ++++++++++++++++-- CLAUDE.md | 8 ++--- ci/render_workflows.py | 11 +++++-- ci/test_fractional_workflows.py | 18 ++++++++++-- 7 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/check-macos.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5040cd3c3..21f40e482 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: # aarch64-apple-darwin cross-compiles from the Linux runner via # soldr's blessed cross path + its managed Apple SDK, matching the - # release matrix. No macos-latest runner is used anywhere in this + # release matrix. Hosted macOS test runners are scoped to ci-full # workflow. - target: aarch64-apple-darwin runner: ubuntu-latest diff --git a/.github/workflows/check-macos.yml b/.github/workflows/check-macos.yml new file mode 100644 index 000000000..9d0a34149 --- /dev/null +++ b/.github/workflows/check-macos.yml @@ -0,0 +1,47 @@ +name: Check macOS + +on: + workflow_call: + inputs: + ref: + type: string + required: true + +permissions: + contents: read + +jobs: + test: + name: Test (${{ matrix.runner }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + runner: [macos-15-intel, macos-15] + timeout-minutes: 90 + env: + CARGO_TERM_COLOR: always + RUSTFLAGS: "-D warnings" + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.ref }} + persist-credentials: false + - name: Verify candidate SHA + env: + CANDIDATE_SHA: ${{ inputs.ref }} + run: | + [[ "$CANDIDATE_SHA" =~ ^[0-9a-f]{40}$ ]] + test "$(git rev-parse HEAD)" = "$CANDIDATE_SHA" + - uses: actions/setup-python@v6 + with: + python-version: "3.13" + - uses: zackees/setup-soldr@v0 + with: + cache: true + build-cache: true + target-cache: true + prebuild-deps: none + linker: platform-default + - name: Test Rust workspace on macOS + run: soldr cargo test --workspace diff --git a/.github/workflows/ci-full.yml b/.github/workflows/ci-full.yml index b3bb52801..91f9a5c82 100644 --- a/.github/workflows/ci-full.yml +++ b/.github/workflows/ci-full.yml @@ -65,6 +65,12 @@ jobs: uses: ./.github/workflows/check-windows.yml with: ref: ${{ needs.verify.outputs.candidate_sha }} + macos: + if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') + needs: verify + uses: ./.github/workflows/check-macos.yml + with: + ref: ${{ needs.verify.outputs.candidate_sha }} dylint: if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci-full') needs: verify @@ -594,7 +600,7 @@ jobs: coverage: name: Full coverage if: always() - needs: [verify, boards, linux, windows, dylint, acceptance, bench, qemu, fmt, docs, msrv, validate_boards, crate_gate] + needs: [verify, boards, linux, windows, macos, dylint, acceptance, bench, qemu, fmt, docs, msrv, validate_boards, crate_gate] runs-on: ubuntu-latest outputs: complete: ${{ steps.complete.outputs.complete }} @@ -607,6 +613,7 @@ jobs: BOARDS: ${{ needs.boards.result }} LINUX: ${{ needs.linux.result }} WINDOWS: ${{ needs.windows.result }} + MACOS: ${{ needs.macos.result }} DYLINT: ${{ needs.dylint.result }} ACCEPTANCE: ${{ needs.acceptance.result }} BENCH: ${{ needs.bench.result }} @@ -622,7 +629,7 @@ jobs: echo 'Optional tier is not selected on this PR; coverage is incomplete' >&2 exit 1 fi - for result in "$VERIFY" "$BOARDS" "$LINUX" "$WINDOWS" "$DYLINT" "$ACCEPTANCE" "$BENCH" "$QEMU" "$FMT" "$DOCS" "$MSRV" "$VALIDATE_BOARDS" "$CRATE_GATE"; do + for result in "$VERIFY" "$BOARDS" "$LINUX" "$WINDOWS" "$MACOS" "$DYLINT" "$ACCEPTANCE" "$BENCH" "$QEMU" "$FMT" "$DOCS" "$MSRV" "$VALIDATE_BOARDS" "$CRATE_GATE"; do if [ "$result" != success ]; then echo "coverage incomplete: $result" >&2 exit 1 diff --git a/.github/workflows/release-auto.yml b/.github/workflows/release-auto.yml index 875f4c0e0..fdef757cb 100644 --- a/.github/workflows/release-auto.yml +++ b/.github/workflows/release-auto.yml @@ -265,6 +265,30 @@ jobs: with: candidate_sha: ${{ needs.prepare.outputs.release_ref }} + macos-release-smoke: + name: Smoke release binary (${{ matrix.target }}) + needs: [prepare, build] + if: needs.prepare.outputs.should_build == 'true' + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-apple-darwin + runner: macos-15-intel + - target: aarch64-apple-darwin + runner: macos-15 + steps: + - uses: actions/download-artifact@v8 + with: + name: binaries-${{ matrix.target }} + path: dist + - name: Execute Linux-built macOS release binary + shell: bash + run: | + chmod +x dist/fbuild + dist/fbuild --version + runtime-coverage: name: All-platform physical runtime coverage needs: [prepare, full-ci] @@ -281,7 +305,7 @@ jobs: publish: name: Publish GitHub release - needs: [prepare, build, full-ci, runtime-coverage] + needs: [prepare, build, full-ci, macos-release-smoke, runtime-coverage] if: needs.prepare.outputs.should_publish_github == 'true' && needs.full-ci.outputs.coverage == 'true' && needs.runtime-coverage.result == 'success' runs-on: ubuntu-24.04 timeout-minutes: 30 @@ -351,7 +375,7 @@ jobs: build-pypi: name: Build PyPI wheels - needs: [prepare, build, full-ci, runtime-coverage] + needs: [prepare, build, full-ci, macos-release-smoke, runtime-coverage] if: needs.prepare.outputs.should_publish_pypi == 'true' && needs.full-ci.outputs.coverage == 'true' && needs.runtime-coverage.result == 'success' runs-on: ubuntu-24.04 timeout-minutes: 30 diff --git a/CLAUDE.md b/CLAUDE.md index 74e8a3998..096589ab9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,10 +60,10 @@ The four rules an agent must internalize before doing anything else (all listed - **Always use a globally-installed `soldr` to execute Rust commands.** Bare cargo/rustc and legacy `uv run cargo` shims are blocked by hook. soldr uses `rustup which` to pick the rustup-managed toolchain from `rust-toolchain.toml`. The standard Cargo path is `soldr cargo ...`, so repo Rust builds get soldr's managed zccache path by default; do not add repo-specific `RUSTC_WRAPPER` wiring for normal builds. Install soldr globally via `uv tool install soldr` (or see https://github.com/zackees/soldr). - **Always use `uv` for Python.** Bare `python`/`pip` are blocked by hook. Use `uv run ...` or `uv pip ...`. - MSRV: 1.95.0 | Edition: 2021 | Toolchain: 1.95.0 pinned in `rust-toolchain.toml` (clippy + rustfmt) -- CI hosts: Linux, Windows. All warnings denied (`RUSTFLAGS="-D warnings"`). There are - **no macOS runners** — macOS is a build *target*, not a test host: every apple-darwin - binary is cross-built from Linux by the Linux-hosted native workflows, - `release-auto.yml` and `build.yml`. Do not add a `macos-latest` lane back. +- CI hosts: Linux and Windows for routine PRs and `main`; `ci-full` and release + validation also run tests on hosted Intel and Apple Silicon macOS. All warnings + denied (`RUSTFLAGS="-D warnings"`). Every apple-darwin release binary is still + cross-built from Linux by `release-auto.yml` and `build.yml`. - **Cross-compilation goes through soldr, and only soldr** — `soldr prepare --target X` then `soldr build --target X`. The zig- and xwin-based wrappers are retired and enforced-out by `ci/check_no_legacy_cross.py` plus the `tool_guard.py` hook. Never pass zigbuild's `.2.17` glibc suffix to soldr: it has no diff --git a/ci/render_workflows.py b/ci/render_workflows.py index aa6c10da2..b8560074d 100644 --- a/ci/render_workflows.py +++ b/ci/render_workflows.py @@ -242,6 +242,12 @@ def render_ci(boards: list[dict], tier: str) -> str: + " uses: ./.github/workflows/check-windows.yml\n" + " with:\n" + f" ref: {verified_ref}\n" + + " macos:\n" + + gate + + " needs: verify\n" + + " uses: ./.github/workflows/check-macos.yml\n" + + " with:\n" + + f" ref: {verified_ref}\n" + " dylint:\n" + gate + " needs: verify\n" @@ -322,7 +328,7 @@ def render_ci(boards: list[dict], tier: str) -> str: " coverage:\n" + (" name: Full coverage\n" if full else " name: ci-test coverage\n") + " if: always()\n" - + (" needs: [verify, boards, linux, windows, dylint, acceptance, bench, qemu, fmt, docs, msrv, validate_boards, crate_gate]\n" if full else " needs: [verify, boards, linux]\n") + + (" needs: [verify, boards, linux, windows, macos, dylint, acceptance, bench, qemu, fmt, docs, msrv, validate_boards, crate_gate]\n" if full else " needs: [verify, boards, linux]\n") + " runs-on: ubuntu-latest\n" + " outputs:\n" + " complete: ${{ steps.complete.outputs.complete }}\n" @@ -335,6 +341,7 @@ def render_ci(boards: list[dict], tier: str) -> str: + " BOARDS: ${{ needs.boards.result }}\n" + " LINUX: ${{ needs.linux.result }}\n" + (" WINDOWS: ${{ needs.windows.result }}\n" + " MACOS: ${{ needs.macos.result }}\n" " DYLINT: ${{ needs.dylint.result }}\n" " ACCEPTANCE: ${{ needs.acceptance.result }}\n" " BENCH: ${{ needs.bench.result }}\n" @@ -350,7 +357,7 @@ def render_ci(boards: list[dict], tier: str) -> str: + " echo 'Optional tier is not selected on this PR; coverage is incomplete' >&2\n" + " exit 1\n" + " fi\n" - + (" for result in \"$VERIFY\" \"$BOARDS\" \"$LINUX\" \"$WINDOWS\" \"$DYLINT\" \"$ACCEPTANCE\" \"$BENCH\" \"$QEMU\" \"$FMT\" \"$DOCS\" \"$MSRV\" \"$VALIDATE_BOARDS\" \"$CRATE_GATE\"; do\n" if full else " for result in \"$VERIFY\" \"$BOARDS\" \"$LINUX\"; do\n") + + (" for result in \"$VERIFY\" \"$BOARDS\" \"$LINUX\" \"$WINDOWS\" \"$MACOS\" \"$DYLINT\" \"$ACCEPTANCE\" \"$BENCH\" \"$QEMU\" \"$FMT\" \"$DOCS\" \"$MSRV\" \"$VALIDATE_BOARDS\" \"$CRATE_GATE\"; do\n" if full else " for result in \"$VERIFY\" \"$BOARDS\" \"$LINUX\"; do\n") + " if [ \"$result\" != success ]; then\n" + " echo \"coverage incomplete: $result\" >&2\n" + " exit 1\n" diff --git a/ci/test_fractional_workflows.py b/ci/test_fractional_workflows.py index bfd22019c..ce201fb83 100644 --- a/ci/test_fractional_workflows.py +++ b/ci/test_fractional_workflows.py @@ -34,6 +34,14 @@ def test_full_matrix_matches_every_supported_board(self): self.assertFalse(full["jobs"]["boards"]["strategy"]["fail-fast"]) self.assertEqual("./.github/workflows/check-ubuntu.yml", full["jobs"]["linux"]["uses"]) self.assertEqual("./.github/workflows/check-windows.yml", full["jobs"]["windows"]["uses"]) + self.assertEqual("./.github/workflows/check-macos.yml", full["jobs"]["macos"]["uses"]) + self.assertNotIn("macos", self.load("ci-minimal.yml")["jobs"]) + self.assertNotIn("macos", self.load("ci-test.yml")["jobs"]) + macos = self.load("check-macos.yml") + self.assertEqual( + {"macos-15-intel", "macos-15"}, + set(macos["jobs"]["test"]["strategy"]["matrix"]["runner"]), + ) self.assertEqual("./.github/workflows/dylint.yml", full["jobs"]["dylint"]["uses"]) self.assertTrue(full["jobs"]["dylint"]["with"]["run_full"]) dylint = self.load("dylint.yml") @@ -141,13 +149,13 @@ def test_selected_coverage_requires_every_requested_tier(self): def test_full_coverage_sentinel_and_release_gate(self): full = self.load("ci-full.yml") self.assertEqual( - {"verify", "boards", "linux", "windows", "dylint", "acceptance", "bench", "qemu", "fmt", "docs", "msrv", "validate_boards", "crate_gate"}, + {"verify", "boards", "linux", "windows", "macos", "dylint", "acceptance", "bench", "qemu", "fmt", "docs", "msrv", "validate_boards", "crate_gate"}, set(full["jobs"]["coverage"]["needs"]), ) self.assertEqual("${{ jobs.coverage.outputs.complete }}", full[True]["workflow_call"]["outputs"]["coverage"]["value"]) script = full["jobs"]["coverage"]["steps"][0]["run"] env = {**os.environ, "EVENT_NAME": "workflow_call", "LABEL_PRESENT": "false"} - env.update({key: "success" for key in ("VERIFY", "BOARDS", "LINUX", "WINDOWS", "DYLINT", "ACCEPTANCE", "BENCH", "QEMU", "FMT", "DOCS", "MSRV", "VALIDATE_BOARDS", "CRATE_GATE")}) + env.update({key: "success" for key in ("VERIFY", "BOARDS", "LINUX", "WINDOWS", "MACOS", "DYLINT", "ACCEPTANCE", "BENCH", "QEMU", "FMT", "DOCS", "MSRV", "VALIDATE_BOARDS", "CRATE_GATE")}) with tempfile.NamedTemporaryFile() as output: env["GITHUB_OUTPUT"] = output.name for missing in ("FMT", "DOCS", "MSRV", "VALIDATE_BOARDS", "CRATE_GATE"): @@ -157,6 +165,12 @@ def test_full_coverage_sentinel_and_release_gate(self): self.assertNotIn("complete=true", output.read().decode()) release = self.load("release-auto.yml") self.assertEqual("${{ needs.prepare.outputs.release_ref }}", release["jobs"]["full-ci"]["with"]["candidate_sha"]) + self.assertEqual( + {"macos-15-intel", "macos-15"}, + {cell["runner"] for cell in release["jobs"]["macos-release-smoke"]["strategy"]["matrix"]["include"]}, + ) + self.assertIn("macos-release-smoke", release["jobs"]["publish"]["needs"]) + self.assertIn("macos-release-smoke", release["jobs"]["build-pypi"]["needs"]) for job in ("publish", "publish-pypi"): self.assertIn("needs.full-ci.outputs.coverage == 'true'", release["jobs"][job]["if"]) From eae134a79400fad305c43679523063cacc41b6b0 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 23 Sep 2026 14:52:07 -0700 Subject: [PATCH 4/6] ci: canary setup-soldr worker fix in full board builds --- .github/workflows/README.md | 3 +++ .github/workflows/template_build.yml | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index c75550601..e7b429127 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -111,6 +111,9 @@ block fails CI with a copy-paste fix. ### Bumping soldr All `zackees/setup-soldr@v0` steps pin the installed soldr binary version. +The full board template temporarily pins the setup action itself to merged +commit `f63d8987580fd50667d95b19130e82d9691ddd75` as a downstream canary +for setup-soldr#517 before the floating `v0` reference moves. When bumping it, first confirm the proposed tag is a published, non-draft release with the required platform assets. Update every setup-soldr call site in one PR, retain the previous pin until that release exists, and require diff --git a/.github/workflows/template_build.yml b/.github/workflows/template_build.yml index 8e5275d91..7d48a5fb9 100644 --- a/.github/workflows/template_build.yml +++ b/.github/workflows/template_build.yml @@ -47,7 +47,8 @@ jobs: - name: Setup soldr id: setup-soldr - uses: zackees/setup-soldr@v0 + # Canary setup-soldr#517's merged worker-lifecycle fix on full board CI. + uses: zackees/setup-soldr@f63d8987580fd50667d95b19130e82d9691ddd75 with: # cache-preset: foundation expands to: # build-cache: true, target-cache: false, From ba7a9a44d0833e01089bf5365e0201db8b13af15 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 23 Sep 2026 17:08:26 -0700 Subject: [PATCH 5/6] ci: canary exact setup-soldr main before v0 promotion --- .github/workflows/template_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/template_build.yml b/.github/workflows/template_build.yml index 7d48a5fb9..5164f4007 100644 --- a/.github/workflows/template_build.yml +++ b/.github/workflows/template_build.yml @@ -47,8 +47,8 @@ jobs: - name: Setup soldr id: setup-soldr - # Canary setup-soldr#517's merged worker-lifecycle fix on full board CI. - uses: zackees/setup-soldr@f63d8987580fd50667d95b19130e82d9691ddd75 + # Canary exact setup-soldr main SHA before v0 promotion. + uses: zackees/setup-soldr@bdd9e802eae68065c06a68b9378ab83f416ee3e0 with: # cache-preset: foundation expands to: # build-cache: true, target-cache: false, From bfeb497dc9b40209b6df42551a790451d42a4fae Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 23 Sep 2026 17:51:56 -0700 Subject: [PATCH 6/6] test: canary setup-soldr main retry fix --- .github/workflows/template_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/template_build.yml b/.github/workflows/template_build.yml index 5164f4007..5df5f0d35 100644 --- a/.github/workflows/template_build.yml +++ b/.github/workflows/template_build.yml @@ -48,7 +48,7 @@ jobs: - name: Setup soldr id: setup-soldr # Canary exact setup-soldr main SHA before v0 promotion. - uses: zackees/setup-soldr@bdd9e802eae68065c06a68b9378ab83f416ee3e0 + uses: zackees/setup-soldr@a71cd1c92c72f8effbe65862e1440e9627678189 with: # cache-preset: foundation expands to: # build-cache: true, target-cache: false,