**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>
171 lines
6.9 KiB
YAML
171 lines
6.9 KiB
YAML
# Reusable workflow that installs and validates a set of macOS Fleet-maintained
|
|
# apps. Called by test-fma-darwin-pr-only.yml and test-fma-darwin.yml with a
|
|
# matrix produced by .github/scripts/partition-fma-apps.sh. All darwin apps run
|
|
# on the arm64 macos-latest runner (x86-only casks run under Rosetta 2, which
|
|
# matches how customer Macs run them), so unlike Windows there is no
|
|
# per-architecture routing.
|
|
name: Validate Fleet Maintained Apps - Darwin
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
runner:
|
|
description: 'Runner label to validate on (e.g. "macos-latest")'
|
|
required: true
|
|
type: string
|
|
slugs:
|
|
description: 'JSON array of app slugs to validate (e.g. ["box-drive/darwin"])'
|
|
required: true
|
|
type: string
|
|
log_level:
|
|
description: "Log level (debug, info, warn, error)"
|
|
required: false
|
|
type: string
|
|
default: "info"
|
|
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
LOG_LEVEL: ${{ inputs.log_level }}
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
validate:
|
|
runs-on: ${{ inputs.runner }}
|
|
|
|
steps:
|
|
- name: Harden Runner
|
|
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
|
|
with:
|
|
egress-policy: audit
|
|
|
|
# Changed-app detection and sharding happen in the calling workflow on a
|
|
# Linux runner, so no git history is needed here.
|
|
- name: Checkout Fleet
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
with:
|
|
fetch-depth: 1
|
|
path: fleet
|
|
persist-credentials: false
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
|
with:
|
|
go-version-file: "fleet/go.mod"
|
|
|
|
- name: Determine pre-installed apps to remove
|
|
id: check-darwin-apps
|
|
# Pass the slugs through env rather than expanding ${{ inputs.slugs }}
|
|
# into the script body (flagged by zizmor as template injection).
|
|
env:
|
|
SLUGS_JSON: ${{ inputs.slugs }}
|
|
run: |
|
|
echo "Apps to validate on this $(uname -m) runner:"
|
|
echo "$SLUGS_JSON" | jq -r '.[] | " - \(.)"'
|
|
|
|
# The runner images ship with some of the apps we validate already
|
|
# installed (or bundled, in Icon Composer's case); flag the ones
|
|
# present in this shard so the steps below start the validator from
|
|
# a clean state.
|
|
has_flag() {
|
|
echo "$SLUGS_JSON" | jq -e --arg slug "$1" 'index($slug) != null' > /dev/null && echo "true" || echo "false"
|
|
}
|
|
{
|
|
echo "has_google_chrome=$(has_flag 'google-chrome/darwin')"
|
|
echo "has_icon_composer=$(has_flag 'icon-composer/darwin')"
|
|
echo "has_fleet_desktop=$(has_flag 'fleet-desktop/darwin')"
|
|
} >> "$GITHUB_OUTPUT"
|
|
shell: bash
|
|
|
|
- name: Install osquery mac
|
|
run: |
|
|
echo "Runner architecture: $(uname -m)"
|
|
curl -L -o osquery.tar.gz "https://github.com/osquery/osquery/releases/download/5.18.1/osquery-5.18.1_1.macos_arm64.tar.gz"
|
|
tar -xzf osquery.tar.gz
|
|
sudo cp -r opt /
|
|
sudo cp -r private /
|
|
sudo ln -sf /opt/osquery/lib/osquery.app/Contents/MacOS/osqueryd /usr/local/bin/osqueryi
|
|
sudo ln -sf /opt/osquery/lib/osquery.app/Contents/Resources/osqueryctl /usr/local/bin/osqueryctl
|
|
|
|
- name: Remove pre-installed google chrome mac
|
|
if: steps.check-darwin-apps.outputs.has_google_chrome == 'true'
|
|
run: |
|
|
find /Applications -maxdepth 1 -iname "*chrome*"
|
|
find /Applications -name "*Chrome*.app" -type d | while read -r app;
|
|
do
|
|
echo "Removing $app..."
|
|
sudo rm -rf "$app"
|
|
done
|
|
|
|
# Icon Composer ships bundled inside Xcode, and GitHub macOS runners
|
|
# come with Xcode pre-installed. Remove it so the Icon Composer FMA
|
|
# install script is validated against a clean install rather than an
|
|
# already-present copy. Only runs when icon-composer/darwin is being
|
|
# validated in this shard.
|
|
- name: Remove pre-installed Xcode mac
|
|
if: steps.check-darwin-apps.outputs.has_icon_composer == 'true'
|
|
run: |
|
|
find /Applications -maxdepth 1 -iname "Xcode*"
|
|
find /Applications -maxdepth 1 -iname "Xcode*.app" -type d | while read -r app;
|
|
do
|
|
echo "Removing $app..."
|
|
sudo rm -rf "$app"
|
|
done
|
|
|
|
# Fleet Desktop's installer refuses to run unless the
|
|
# com.fleetdm.fleetd.config managed preferences profile is present
|
|
# (it's normally delivered via MDM). CI runners aren't MDM-enrolled,
|
|
# so we drop a stub plist in place before the validate step so the
|
|
# install script succeeds. This only runs when fleet-desktop/darwin
|
|
# is actually being validated in this shard.
|
|
- name: Create Fleet Desktop MDM config stub (CI-only)
|
|
if: steps.check-darwin-apps.outputs.has_fleet_desktop == 'true'
|
|
run: |
|
|
sudo mkdir -p "/Library/Managed Preferences"
|
|
sudo tee "/Library/Managed Preferences/com.fleetdm.fleetd.config.plist" > /dev/null <<'PLIST'
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>EnrollSecret</key>
|
|
<string>ci-test-placeholder</string>
|
|
<key>FleetURL</key>
|
|
<string>https://ci.test.example.com</string>
|
|
</dict>
|
|
</plist>
|
|
PLIST
|
|
sudo chmod 644 "/Library/Managed Preferences/com.fleetdm.fleetd.config.plist"
|
|
ls -l "/Library/Managed Preferences/com.fleetdm.fleetd.config.plist"
|
|
|
|
- name: Filter apps.json and validate apps
|
|
# Pass the slugs through env rather than expanding ${{ inputs.slugs }}
|
|
# into the script body (flagged by zizmor as template injection).
|
|
env:
|
|
SLUGS_JSON: ${{ inputs.slugs }}
|
|
run: |
|
|
cd fleet
|
|
# Set GITHUB_WORKSPACE to current directory so scripts can find files
|
|
export GITHUB_WORKSPACE="$PWD"
|
|
|
|
# The shard's slugs arrive as a compact JSON array string built by
|
|
# the partition script, ready for filter-apps-json.sh as-is.
|
|
echo "Filtering apps.json for slugs: $SLUGS_JSON"
|
|
|
|
# Backup original apps.json
|
|
cp ee/maintained-apps/outputs/apps.json ee/maintained-apps/outputs/apps.json.backup
|
|
|
|
# Create filtered apps.json
|
|
FILTERED_APPS_JSON=$(mktemp)
|
|
.github/scripts/filter-apps-json.sh "$SLUGS_JSON" "$FILTERED_APPS_JSON"
|
|
|
|
# Replace apps.json with filtered version
|
|
mv "$FILTERED_APPS_JSON" ee/maintained-apps/outputs/apps.json
|
|
|
|
# Run validation
|
|
ls /Applications
|
|
sudo -E go run ./cmd/maintained-apps/validate
|
|
|
|
# Restore original apps.json
|
|
mv ee/maintained-apps/outputs/apps.json.backup ee/maintained-apps/outputs/apps.json
|