**Related issue:** N/A — CI improvement for the FMA validation workflows. ## Summary Restructures the Windows and macOS Fleet-maintained app validation workflows around a cheap Linux detect/shard job, with Windows apps additionally routed to a CI runner whose native architecture matches the app's installer. **Both platforms:** - Change detection and sharding run on `ubuntu-latest`. Expensive Windows/macOS runners only spin up when their platform actually has changed apps — e.g. a Windows-only letter-batch PR no longer boots a macOS runner just to discover there's nothing to do (and vice versa) — and they check out at depth 1 instead of full history. - A new `.github/scripts/partition-fma-apps.sh <windows|darwin>` emits the job matrix; validation steps move unchanged into reusable workflows (`test-fma-windows-validate.yml`, `test-fma-darwin-validate.yml`). - Large PRs shard into parallel jobs (Windows: 25 apps/shard, macOS: 30), and the manual full-run workflows gain a `shard_size` input (Windows default 20 → ~20 shards over 384 apps; macOS default 25 → ~39 shards over 961 apps). Neither full run could previously finish: hundreds of sequential installs blow the 6-hour job limit. - Pre-installed app handling is computed per shard from that shard's slug list — Windows removals (Chrome, 7-Zip, Firefox, Node.js, PowerShell, R, Git) and macOS steps (Chrome, Xcode for Icon Composer, the Fleet Desktop MDM config stub) only run on the runner validating that app. This also brings the full-run workflows to parity with the PR gates (they previously only removed Chrome). - Stable summary jobs (`test-fma-pr-only`, `test-fma`) aggregate the dynamic matrix results so branch protection / PR gating keeps a fixed check name. **Windows arch routing:** - Each changed `<name>/windows` slug's `installer_arch` is read from `ee/maintained-apps/inputs/winget/<name>.json`: `arm64` apps → `windows-11-arm`, x64/x86/neutral apps → `windows-latest` (x64). Missing input files default to x64 with a warning. This fixes installers that check the native OS architecture and abort under Prism emulation on the ARM runner (Inno Setup `ArchitecturesAllowed=x64` — GOG Galaxy, Reqable — and Docker Desktop). Future arm64 FMAs need no workflow change — `installer_arch: arm64` in the winget input is enough. - macOS needs no arch matrix: `macos-latest` is arm64 and x86-only casks run under Rosetta 2, which matches how customer Macs run them. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. ## Testing - [x] QA'd all new/changed functionality manually Manual QA: - Partition script is shellcheck-clean and tested against the real repo for both platforms: empty input, mixed-platform slug lists, x86/neutral routing to the x64 runner, single-slug arrays, missing input file fallback, arm64/x64 split with sharding (via a synthetic arm64 input), invalid platform/shard-size rejection, and full-catalog partitions (384 Windows apps → 20 shards, 961 darwin apps → 39 shards, all slugs accounted for, matrix outputs well under the 1 MB job-output limit). - All six workflows pass `actionlint` and zizmor 1.25.2 (with the repo's `.github/zizmor-gate.yml` config) with no findings. - The rewritten Windows PR gate ran on this PR itself: the Linux detect job correctly found no changed Windows apps, skipped the Windows runners, and the `test-fma-pr-only` summary check passed. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added sharded validation for maintained macOS and Windows apps to run tests in parallel. * Added configurable `shard_size` for manual validation runs. * Introduced reusable validation workflows for Darwin and Windows. * Improved Windows testing to be architecture-aware (ARM64 vs x64). * **Bug Fixes** * Improved pull request gating to validate only changed apps and report results more reliably. * Workflows now gracefully handle scenarios where no matching apps are found (avoid unnecessary failures). <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Github Actions
Fleet uses Github Actions for continuous integration (CI). This document describes best practices and at patterns for writing and maintaining Fleet's Github Actions workflows.
Bash
By default, Github Actions sets the shell to bash -e for linux and MacOS runners. To help write
safer bash scripts in run jobs and avoid common issues, override the default by adding the following
to the workflow file
defaults:
run:
# fail-fast using bash -eo pipefail. See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
shell: bash
By specifying the default shell to bash, some extra flags are set. The option pipefail changes
the behaviour when using the pipe | operator such that if any command in a pipeline fails, that
commands return code will be used a the return code for the whole pipeline. Consider the following
example in test-go.yaml
- name: Run Go Tests
run: |
# omitted ...
make test-go 2>&1 | tee /tmp/gotest.log
If the pipefail option was not set, this job would always succeed because tee would always
return success. This is not the intended behavior. Instead, we want the job to fail if make test-go fails.
Concurrency
Github Action runners are limited. If a lot of workflows are queued, they will wait in pending until a runner becomes available. This has caused issue in the past where workflows take an excessively long time to start. To help with this issue, use the following in workflows
# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id}}
cancel-in-progress: true
When a workflow is triggered via a pull request, it will cancel previous running workflows for that
pull request. This is especially useful when changes are pushed to a pull request frequently.
Manually triggered workflows, workflows that run on a schedule, and workflows triggered by pushes to
main are unaffected.