mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
macos package: derive package arch from the build target
The published v0.3.0 macOS installer is a structurally corrupt xar archive: pkgutil and xar reject it even though its SHA-256 matches the published checksum. build-pkg.sh derived the architecture suffix in the .pkg filename from `uname -m`. On the Apple-silicon macOS runner that always reports arm64, so the cross-compiled x86_64 build also named its output fips-<version>-macos-arm64.pkg. The release job downloads both build artifacts with merge-multiple into one directory, where the two identically named files collide and tear into a malformed result. The x86_64 package never reaches the release at all. Derive the package architecture from the Rust target triple, which is authoritative for cross-compiles, instead of from the build host. Each matrix leg now produces a distinctly named, arch-correct package, so the two artifacts no longer collide. Add a SHA-256 integrity chain so a corrupt or mismatched asset cannot be published again: - Capture the .pkg SHA-256 on the macOS runner, after the on-runner structural verification, into a sidecar file carried in the artifact. - Add a verify-handoff job that runs on every trigger and asserts each downloaded .pkg still matches its macOS-runner SHA-256. - Gate the release job on verify-handoff and repeat the check on the exact bytes about to be published. The build step now asserts it produced the expected arch-named package so a regression in the naming fails loudly rather than as a silent collision. Relates to #102. The published v0.3.0 macOS assets still need to be rebuilt and reuploaded separately.
This commit is contained in:
@@ -100,14 +100,22 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
: ${GITHUB_OUTPUT:=/tmp/github_output}
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
PKG_FILE=$(find deploy -maxdepth 1 -type f -name "fips-*-macos-*.pkg" | sort | head -n 1)
|
# build-pkg.sh names the package from the build target, so each
|
||||||
if [[ -z "$PKG_FILE" ]]; then
|
# matrix leg produces a distinctly named, arch-correct asset.
|
||||||
echo "Missing macOS package" >&2
|
# 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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "pkg=$PKG_FILE" >> "$GITHUB_OUTPUT"
|
echo "pkg=$EXPECTED" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Verify .pkg structural correctness
|
- name: Verify .pkg structural correctness
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -185,16 +193,27 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
echo "==> .pkg verification PASSED"
|
echo "==> .pkg verification PASSED"
|
||||||
|
|
||||||
- name: SHA-256 hash
|
- name: SHA-256 hash and sidecar
|
||||||
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
PKG="${{ steps.macos-assets.outputs.pkg }}"
|
||||||
echo "==> macOS release asset:"
|
echo "==> macOS release asset:"
|
||||||
shasum -a 256 "${{ steps.macos-assets.outputs.pkg }}"
|
# 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
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: fips_${{ needs.determine-versioning.outputs.macos_package_version }}_${{ matrix.arch }}_macos
|
name: fips_${{ needs.determine-versioning.outputs.macos_package_version }}_${{ matrix.arch }}_macos
|
||||||
path: ${{ steps.macos-assets.outputs.pkg }}
|
path: |
|
||||||
|
${{ steps.macos-assets.outputs.pkg }}
|
||||||
|
${{ steps.macos-assets.outputs.pkg }}.sha256
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- name: Build summary
|
- name: Build summary
|
||||||
@@ -202,10 +221,61 @@ jobs:
|
|||||||
echo "Build Summary for macOS/${{ matrix.arch }}:"
|
echo "Build Summary for macOS/${{ matrix.arch }}:"
|
||||||
echo " Package: ${{ steps.macos-assets.outputs.pkg }}"
|
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@v4
|
||||||
|
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:
|
release:
|
||||||
name: Publish macOS assets to GitHub Release
|
name: Publish macOS assets to GitHub Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: [build, verify-handoff]
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -217,6 +287,45 @@ jobs:
|
|||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
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
|
- name: Generate macOS release checksums
|
||||||
run: |
|
run: |
|
||||||
cd dist
|
cd dist
|
||||||
|
|||||||
@@ -55,7 +55,23 @@ while [[ $# -gt 0 ]]; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
VERSION="${VERSION_OVERRIDE:-$(grep '^version' "${PROJECT_ROOT}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)"/\1/')}"
|
VERSION="${VERSION_OVERRIDE:-$(grep '^version' "${PROJECT_ROOT}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)"/\1/')}"
|
||||||
ARCH="$(uname -m)"
|
|
||||||
|
# Derive the package architecture from the build target, not the build
|
||||||
|
# host. When cross-compiling (for example building the x86_64 package on
|
||||||
|
# an Apple-silicon machine) `uname -m` reports the host architecture and
|
||||||
|
# would mislabel the package; the Rust target triple is authoritative.
|
||||||
|
if [[ -n "${TARGET_TRIPLE}" ]]; then
|
||||||
|
case "${TARGET_TRIPLE}" in
|
||||||
|
aarch64-*) ARCH="arm64" ;;
|
||||||
|
x86_64-*) ARCH="x86_64" ;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported target triple: ${TARGET_TRIPLE}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
fi
|
||||||
PKG_NAME="fips-${VERSION}-macos-${ARCH}"
|
PKG_NAME="fips-${VERSION}-macos-${ARCH}"
|
||||||
DEPLOY_DIR="${PROJECT_ROOT}/deploy"
|
DEPLOY_DIR="${PROJECT_ROOT}/deploy"
|
||||||
STAGING_DIR="$(mktemp -d)"
|
STAGING_DIR="$(mktemp -d)"
|
||||||
|
|||||||
Reference in New Issue
Block a user