mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-22 07:48:26 +00:00
Clear the GitHub Node 20 runner deprecation across CI, AUR, and the Linux/macOS/Windows packaging workflows: actions/checkout v4 -> v6 actions/cache v4 -> v5 actions/upload-artifact v4 -> v7 actions/download-artifact v4 -> v8 package-openwrt.yml is handled separately on fix/openwrt-checksums.
362 lines
12 KiB
YAML
362 lines
12 KiB
YAML
name: macOS Package
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
- maint
|
|
- next
|
|
tags:
|
|
- "v*"
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
determine-versioning:
|
|
runs-on: macos-latest
|
|
outputs:
|
|
macos_package_version: ${{ steps.macos_version.outputs.macos_package_version }}
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Derive macOS package version
|
|
id: macos_version
|
|
shell: bash
|
|
run: |
|
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
|
|
|
BASE_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
|
|
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
else
|
|
BRANCH=$(echo "$GITHUB_REF_NAME" | sed 's|[^A-Za-z0-9]|.|g; s/\.\.+/./g; s/^\.//; s/\.$//')
|
|
HEIGHT=$(git rev-list --count HEAD)
|
|
HASH=$(git rev-parse --short HEAD)
|
|
if [[ -z "$BRANCH" ]]; then
|
|
BRANCH="ref"
|
|
fi
|
|
VERSION="${BASE_VERSION}+${BRANCH}.${HEIGHT}.${HASH}"
|
|
fi
|
|
|
|
echo "macos_package_version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
|
|
build:
|
|
name: Build macOS package (${{ matrix.arch }})
|
|
runs-on: ${{ matrix.os }}
|
|
needs: determine-versioning
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: macos-latest
|
|
arch: arm64
|
|
target: aarch64-apple-darwin
|
|
- os: macos-latest
|
|
arch: x86_64
|
|
target: x86_64-apple-darwin
|
|
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set SOURCE_DATE_EPOCH from git
|
|
run: echo "SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)" >> "$GITHUB_ENV"
|
|
|
|
- name: Install Rust toolchain
|
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
with:
|
|
target: ${{ matrix.target }}
|
|
cache: false
|
|
rustflags: ''
|
|
|
|
- name: Cache Cargo registry + build
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
target
|
|
key: macos-release-${{ matrix.arch }}-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
macos-release-${{ matrix.arch }}-
|
|
|
|
- name: Build release binaries
|
|
run: cargo build --release --target ${{ matrix.target }}
|
|
|
|
- name: Build macOS package
|
|
run: |
|
|
packaging/macos/build-pkg.sh \
|
|
--version "${{ needs.determine-versioning.outputs.macos_package_version }}" \
|
|
--target ${{ matrix.target }} \
|
|
--no-build
|
|
|
|
- name: Resolve macOS asset path
|
|
id: macos-assets
|
|
shell: bash
|
|
run: |
|
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
|
set -euo pipefail
|
|
|
|
# build-pkg.sh names the package from the build target, so each
|
|
# matrix leg produces a distinctly named, arch-correct asset.
|
|
# Assert that here: a regression in that naming then fails loudly
|
|
# at the build stage instead of as a silent collision when the
|
|
# release job merges both artifacts into one directory.
|
|
EXPECTED="deploy/fips-${{ needs.determine-versioning.outputs.macos_package_version }}-macos-${{ matrix.arch }}.pkg"
|
|
if [[ ! -f "$EXPECTED" ]]; then
|
|
echo "Expected package $EXPECTED was not produced" >&2
|
|
echo "deploy/ contains:" >&2
|
|
ls -la deploy >&2 || true
|
|
exit 1
|
|
fi
|
|
|
|
echo "pkg=$EXPECTED" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Verify .pkg structural correctness
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
PKG="${{ steps.macos-assets.outputs.pkg }}"
|
|
EXPAND_DIR="$(mktemp -d)/expanded"
|
|
PAYLOAD_DIR="$(mktemp -d)/payload"
|
|
fail=0
|
|
|
|
echo "==> Verifying $PKG"
|
|
|
|
# 1) Flat-package expansion
|
|
if pkgutil --expand "$PKG" "$EXPAND_DIR"; then
|
|
echo "PASS: pkgutil --expand"
|
|
else
|
|
echo "FAIL: pkgutil --expand"
|
|
fail=1
|
|
fi
|
|
|
|
# Extract the cpio.gz Payload so we can inspect installed file layout
|
|
PAYLOAD_FILE="$(find "$EXPAND_DIR" -name Payload -type f | head -n 1)"
|
|
if [[ -z "$PAYLOAD_FILE" ]]; then
|
|
echo "FAIL: no Payload file inside expanded pkg"
|
|
fail=1
|
|
else
|
|
mkdir -p "$PAYLOAD_DIR"
|
|
(cd "$PAYLOAD_DIR" && gzip -dc "$PAYLOAD_FILE" | cpio -i --quiet)
|
|
echo "PASS: extracted Payload to $PAYLOAD_DIR"
|
|
fi
|
|
|
|
# 2) Binary at canonical install path (./usr/local/bin/fips inside payload)
|
|
BIN_PATH="$PAYLOAD_DIR/usr/local/bin/fips"
|
|
if [[ -f "$BIN_PATH" ]]; then
|
|
echo "PASS: binary present at usr/local/bin/fips"
|
|
else
|
|
echo "FAIL: binary missing at usr/local/bin/fips"
|
|
echo " fallback search:"
|
|
find "$PAYLOAD_DIR" -name fips -type f -print || true
|
|
fail=1
|
|
fi
|
|
for extra in fipsctl fipstop; do
|
|
if [[ -f "$PAYLOAD_DIR/usr/local/bin/$extra" ]]; then
|
|
echo "PASS: binary present at usr/local/bin/$extra"
|
|
else
|
|
echo "FAIL: binary missing at usr/local/bin/$extra"
|
|
fail=1
|
|
fi
|
|
done
|
|
|
|
# 3) LaunchDaemon plist at canonical location
|
|
PLIST_PATH="$PAYLOAD_DIR/Library/LaunchDaemons/com.fips.daemon.plist"
|
|
if [[ -f "$PLIST_PATH" ]]; then
|
|
echo "PASS: plist present at Library/LaunchDaemons/com.fips.daemon.plist"
|
|
else
|
|
echo "FAIL: plist missing at Library/LaunchDaemons/com.fips.daemon.plist"
|
|
echo " fallback search:"
|
|
find "$PAYLOAD_DIR" -name '*.plist' -print || true
|
|
fail=1
|
|
fi
|
|
|
|
# 4) plutil -lint on the plist
|
|
if [[ -f "$PLIST_PATH" ]]; then
|
|
if plutil -lint "$PLIST_PATH"; then
|
|
echo "PASS: plutil -lint"
|
|
else
|
|
echo "FAIL: plutil -lint"
|
|
fail=1
|
|
fi
|
|
fi
|
|
|
|
if [[ "$fail" -ne 0 ]]; then
|
|
echo "==> .pkg verification FAILED"
|
|
exit 1
|
|
fi
|
|
echo "==> .pkg verification PASSED"
|
|
|
|
- name: SHA-256 hash and sidecar
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
PKG="${{ steps.macos-assets.outputs.pkg }}"
|
|
echo "==> macOS release asset:"
|
|
# Capture the SHA-256 of the verified .pkg on the macOS runner and
|
|
# write it to a sidecar file next to the .pkg, in the standard
|
|
# `<hash> <basename>` shasum format. The verify-handoff and release
|
|
# jobs re-check the downloaded bytes against this value, so any
|
|
# corruption introduced after this point is detected before
|
|
# publication.
|
|
( cd "$(dirname "$PKG")" && shasum -a 256 "$(basename "$PKG")" | tee "$(basename "$PKG").sha256" )
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: fips_${{ needs.determine-versioning.outputs.macos_package_version }}_${{ matrix.arch }}_macos
|
|
path: |
|
|
${{ steps.macos-assets.outputs.pkg }}
|
|
${{ steps.macos-assets.outputs.pkg }}.sha256
|
|
retention-days: 30
|
|
|
|
- name: Build summary
|
|
run: |
|
|
echo "Build Summary for macOS/${{ matrix.arch }}:"
|
|
echo " Package: ${{ steps.macos-assets.outputs.pkg }}"
|
|
|
|
verify-handoff:
|
|
name: Verify macOS package handoff integrity
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
|
|
steps:
|
|
- name: Download macOS artifacts
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
path: dist
|
|
merge-multiple: true
|
|
|
|
- name: Verify .pkg integrity across the handoff
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cd dist
|
|
|
|
pkgs=$(find . -maxdepth 1 -type f -name '*.pkg' | LC_ALL=C sort)
|
|
if [[ -z "$pkgs" ]]; then
|
|
echo "FAIL: no .pkg artifacts were downloaded" >&2
|
|
exit 1
|
|
fi
|
|
|
|
fail=0
|
|
while IFS= read -r pkg; do
|
|
base=$(basename "$pkg")
|
|
sidecar="${pkg}.sha256"
|
|
if [[ ! -f "$sidecar" ]]; then
|
|
echo "FAIL: missing SHA-256 sidecar for $base" >&2
|
|
fail=1
|
|
continue
|
|
fi
|
|
expected=$(awk '{print $1}' "$sidecar")
|
|
actual=$(sha256sum "$pkg" | awk '{print $1}')
|
|
if [[ "$expected" != "$actual" ]]; then
|
|
echo "FAIL: $base SHA-256 mismatch across the artifact handoff" >&2
|
|
echo " expected (macOS runner): $expected" >&2
|
|
echo " actual (downloaded): $actual" >&2
|
|
fail=1
|
|
continue
|
|
fi
|
|
echo "PASS: $base matches the macOS-runner SHA-256 ($actual)"
|
|
done <<<"$pkgs"
|
|
|
|
if [[ "$fail" -ne 0 ]]; then
|
|
echo "==> macOS package handoff verification FAILED" >&2
|
|
exit 1
|
|
fi
|
|
echo "==> macOS package handoff verification PASSED"
|
|
|
|
release:
|
|
name: Publish macOS assets to GitHub Release
|
|
runs-on: ubuntu-latest
|
|
needs: [build, verify-handoff]
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- name: Download macOS artifacts
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
path: dist
|
|
merge-multiple: true
|
|
|
|
- name: Validate .pkg bytes before publishing
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cd dist
|
|
|
|
pkgs=$(find . -maxdepth 1 -type f -name '*.pkg' | LC_ALL=C sort)
|
|
if [[ -z "$pkgs" ]]; then
|
|
echo "FAIL: no .pkg artifacts were downloaded" >&2
|
|
exit 1
|
|
fi
|
|
|
|
fail=0
|
|
while IFS= read -r pkg; do
|
|
base=$(basename "$pkg")
|
|
sidecar="${pkg}.sha256"
|
|
if [[ ! -f "$sidecar" ]]; then
|
|
echo "FAIL: missing SHA-256 sidecar for $base" >&2
|
|
fail=1
|
|
continue
|
|
fi
|
|
expected=$(awk '{print $1}' "$sidecar")
|
|
actual=$(sha256sum "$pkg" | awk '{print $1}')
|
|
if [[ "$expected" != "$actual" ]]; then
|
|
echo "FAIL: $base SHA-256 mismatch on the bytes about to be published" >&2
|
|
echo " expected (macOS runner): $expected" >&2
|
|
echo " actual (downloaded): $actual" >&2
|
|
fail=1
|
|
continue
|
|
fi
|
|
echo "PASS: $base matches the macOS-runner SHA-256 ($actual)"
|
|
done <<<"$pkgs"
|
|
|
|
if [[ "$fail" -ne 0 ]]; then
|
|
echo "==> pre-publish .pkg verification FAILED; not publishing" >&2
|
|
exit 1
|
|
fi
|
|
echo "==> pre-publish .pkg verification PASSED"
|
|
|
|
- name: Generate macOS release checksums
|
|
run: |
|
|
cd dist
|
|
find . -maxdepth 1 -type f -name '*.pkg' -printf '%P\n' \
|
|
| LC_ALL=C sort \
|
|
| xargs sha256sum \
|
|
> checksums-macos.txt
|
|
|
|
- name: Wait for tag release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
for attempt in $(seq 1 20); do
|
|
if gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
|
|
exit 0
|
|
fi
|
|
echo "Release ${GITHUB_REF_NAME} not available yet; waiting..."
|
|
sleep 15
|
|
done
|
|
|
|
echo "Timed out waiting for release ${GITHUB_REF_NAME}" >&2
|
|
exit 1
|
|
|
|
- name: Upload macOS assets
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
gh release upload "${GITHUB_REF_NAME}" \
|
|
dist/*.pkg \
|
|
dist/checksums-macos.txt \
|
|
--clobber \
|
|
--repo "${GITHUB_REPOSITORY}"
|