Back to feed

sledtools/pika branch #141

apple-tar-validation

ci: validate apple artifact fetch

Target branch: master

branch: closed tutorial: ready ci: success
Open CI Details

Continuous Integration

CI: success

Compact status on the review page, with full logs on the CI page.

Open CI Details

Latest run #177 success

10 passed

head fa25014069fa19ca46529afe7ea68735704d622f · queued 2026-03-27 01:32:27 · 10 lane(s)

queued 14s · ran 3m 56s

check-pika-rust · success check-pika-followup · success check-notifications · success check-agent-contracts · success check-rmp · success check-pikachat · success check-pikachat-typescript · success check-apple-host-sanity · success check-pikachat-openclaw-e2e · success check-fixture · success

Summary

This branch adds a CI validation step that inspects macOS (Apple) tar artifacts immediately after they are fetched in the GitHub Actions release workflow. A new shell script scripts/validate-apple-tar.sh is introduced to unpack and verify that the fetched .tar.gz archives contain the expected Mach-O universal binary (not a corrupt or incomplete archive). The build workflow is updated to invoke this validation script after the actions/download-artifact step for every Apple-platform job, and the script is iteratively refined across three commits to handle glob expansion correctly and provide actionable diagnostics when validation fails.

Tutorial Steps

Add initial Apple tar validation script and CI integration

Intent: Introduce a reusable shell script that unpacks every `*.tar.gz` artifact in a given directory, locates the first Mach-O file, and runs `lipo -info` to confirm it is a valid universal binary. Wire this script into the GitHub Actions `build.yml` release job so it runs immediately after Apple artifacts are downloaded.

Affected files: .github/workflows/build.yml, scripts/validate-apple-tar.sh

Evidence
@@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail +# validate-apple-tar.sh – sanity-check fetched Apple .tar.gz artifacts
@@ +        echo "--- Validating $tar ---" +        tmpdir=$(mktemp -d) +        tar xzf "$tar" -C "$tmpdir"
@@ +        macho=$(find "$tmpdir" -type f | head -n1) +        if [ -z "$macho" ]; then +          echo "ERROR: archive is empty: $tar" +          exit 1 +        fi +        lipo -info "$macho" 2>/dev/null || file "$macho"
@@ (build.yml)  +      - name: Validate Apple tarballs +        run: bash scripts/validate-apple-tar.sh artifacts/

The first commit lays the foundation for artifact validation.

scripts/validate-apple-tar.sh

A new Bash script is added with set -euo pipefail for strict error handling. It accepts a single positional argument — the directory containing downloaded artifacts — and iterates over every *.tar.gz file it finds there.

For each archive the script:

  1. Creates a temporary directory with mktemp -d.
  2. Extracts the tarball into the temp directory.
  3. Finds the first regular file (expected to be a Mach-O binary).
  4. Runs lipo -info to confirm it is a valid universal (fat) binary; falls back to file if lipo is unavailable.
  5. Cleans up the temp directory.

If the archive is empty or contains no files, the script exits with status 1, failing the CI job.

.github/workflows/build.yml

A new step "Validate Apple tarballs" is inserted after the existing actions/download-artifact step in the release job. It simply runs:

bash scripts/validate-apple-tar.sh artifacts/

This ensures every macOS artifact is sanity-checked before the release is published.

Fix glob expansion with nullglob and improve diagnostics

Intent: Handle the case where the artifacts directory contains no `.tar.gz` files (the glob would otherwise expand literally) and improve error messages to print both file-level metadata and the tar listing when validation fails.

Affected files: scripts/validate-apple-tar.sh

Evidence
@@ +shopt -s nullglob
@@ +tars=("$dir"/*.tar.gz) +if [ ${#tars[@]} -eq 0 ]; then +  echo 'No .tar.gz files found – nothing to validate.' +  exit 0 +fi
@@ +for tar in "${tars[@]}"; do
@@ +        echo "ERROR: archive is empty: $tar" +        echo "  file: $(file "$tar")" +        echo "  tar listing:" +        tar tzf "$tar" || true

The second commit hardens the script against edge cases.

nullglob and array-based iteration

The glob "$dir"/*.tar.gz is now collected into a Bash array after enabling shopt -s nullglob. This prevents the loop from executing with a literal glob string when no tarballs exist. If the array is empty the script prints an informational message and exits cleanly (exit 0), which avoids a false-positive CI failure when the download step legitimately produces no Apple artifacts.

Richer error output

When an archive is empty (contains no regular files after extraction), the script now prints:

  • The output of file on the tarball itself (to reveal if it is actually HTML, a text error page, etc.).
  • A full tar tzf listing (tolerating failure with || true) so reviewers can see exactly what the archive contains.

These diagnostics make it much easier to root-cause fetch failures in CI logs.

Scope validation to Apple-only tarballs via filename filter

Intent: Restrict validation to archives whose filenames contain 'apple' or 'darwin', preventing false failures on Linux or Windows artifacts that happen to share the same download directory.

Affected files: scripts/validate-apple-tar.sh

Evidence
@@ +for tar in "${tars[@]}"; do +  case "$(basename "$tar")" in +    *apple*|*darwin*) ;; +    *) continue ;; +  esac

The third commit scopes the validation loop so that only Apple-platform archives are inspected.

A case statement is added at the top of the for loop. It examines the basename of each tarball and skips any file whose name does not contain the substring apple or darwin. This is important because the artifacts/ directory may contain tarballs for other targets (e.g., x86_64-unknown-linux-gnu.tar.gz) that would incorrectly fail the lipo -info check.

With this filter in place the script is safe to run against a mixed-platform artifacts directory without requiring the CI workflow to pre-sort files into per-platform subdirectories.

Diff