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@v4 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@v4 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: dtolnay/rust-toolchain@stable - name: Add cross-compile target run: rustup target add ${{ matrix.target }} - name: Cache Cargo registry + build uses: actions/cache@v4 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} PKG_FILE=$(find deploy -maxdepth 1 -type f -name "fips-*-macos-*.pkg" | sort | head -n 1) if [[ -z "$PKG_FILE" ]]; then echo "Missing macOS package" >&2 exit 1 fi echo "pkg=$PKG_FILE" >> "$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 run: | echo "==> macOS release asset:" shasum -a 256 "${{ steps.macos-assets.outputs.pkg }}" - name: Upload artifact uses: actions/upload-artifact@v4 with: name: fips_${{ needs.determine-versioning.outputs.macos_package_version }}_${{ matrix.arch }}_macos path: ${{ steps.macos-assets.outputs.pkg }} retention-days: 30 - name: Build summary run: | echo "Build Summary for macOS/${{ matrix.arch }}:" echo " Package: ${{ steps.macos-assets.outputs.pkg }}" release: name: Publish macOS assets to GitHub Release runs-on: ubuntu-latest needs: build if: startsWith(github.ref, 'refs/tags/') permissions: contents: write steps: - name: Download macOS artifacts uses: actions/download-artifact@v4 with: path: dist merge-multiple: true - 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}"