Skip to content

ci(self-packaging #49): cross-platform pack smoke jobs#59

Merged
jrosskopf merged 5 commits into
mainfrom
feature/gh-49-ci-cross-platform
May 22, 2026
Merged

ci(self-packaging #49): cross-platform pack smoke jobs#59
jrosskopf merged 5 commits into
mainfrom
feature/gh-49-ci-cross-platform

Conversation

@jrosskopf
Copy link
Copy Markdown
Contributor

Part of epic #40. Stacked on #58 -> #57 -> #56 -> #55 -> #54 -> #53 -> #52 -> #51.

Summary

Adds four new CI jobs exercising the self-packaging surface on every
platform flapi builds for:

Job Runner Verifies
pack-smoke-linux-amd64 ubuntu-24.04 pack / info / unpack / reproducibility
pack-smoke-linux-arm64 ubuntu-24.04-arm same
pack-smoke-macos macos-latest pack / info / unpack + otool -l segment check + codesign --verify --strict + --macos-append legacy path + oversized-payload error
pack-smoke-windows windows-latest (pwsh) pack / info / unpack / reproducibility

create-release now needs the four new jobs in addition to the
existing build + HTTP smoke matrix, so a broken pack on any platform
blocks the release.

Verifications per job

Common across all four:

# Pack the fixture
flapi pack --in fixture --out out-a

# `info` lists the fixture entries
flapi info | grep -q "flapi.yaml"
flapi info | grep -q "sqls/hello.sql"

# `unpack` restores byte-for-byte
flapi unpack --to extracted
diff fixture/sqls/hello.sql extracted/sqls/hello.sql

# Reproducibility (Linux + Windows only -- see note below)
SOURCE_DATE_EPOCH=1700000000 flapi pack --in fixture --out out-b
sha256sum out-a == sha256sum out-b   # asserted

The Windows job uses pwsh with Get-FileHash instead of sha256sum;
fixture YAML is written via PowerShell here-strings.

macOS-specific assertions

# Pre-condition: the unbundled binary carries the link-time placeholder.
otool -l bin/flapi | grep -q "segname __FLAPI"
otool -l bin/flapi | grep -q "sectname __bundle"

# Default pack uses reserved-segment mode -- notarisable.
flapi pack --in fixture --out out-segment
codesign --verify --strict out-segment        # NOTARISATION PRECONDITION

# Legacy --macos-append still produces a runnable artifact (ad-hoc only).
flapi pack --in fixture --out out-append --macos-append
out-append info | grep -q "flapi.yaml"

# Oversized payload is rejected with a corrective error.
dd if=/dev/urandom of=fixture/data/huge.bin bs=1m count=32
flapi pack --in fixture --out out-too-big   # non-zero exit
grep -qi "FLAPI_RESERVED_BUNDLE_MIB" err.txt
grep -Eqi "reserved|exceeds" err.txt

Why no reproducibility on macOS

codesign may include non-deterministic data in the signature blob
(designated requirement timestamps, optionally embedded timestamps,
etc). Linux and Windows just append a deterministic archive after the
host bytes -- reproducibility is by construction. The macOS jobs
still validate every other invariant, just not byte-equal pack output.

What integration-tests already covers (unchanged)

The existing integration-tests job on ubuntu-24.04 auto-picks up the
pytest files added in this stack:

That job is left unchanged -- pytest discovery picks the new files up
for free.

Test plan

  • YAML syntax validates (yaml.safe_load).
  • Linux x86_64 / Linux ARM64 / macOS / Windows CI legs all green
    on this branch.
  • On a tag push, create-release waits for the new jobs.

This is the first PR in the stack where I can't usefully validate
end-to-end locally (no macOS / Windows / ARM64 box). The bash and
pwsh scripts are based on the existing smoke-test patterns in this
file, which already run reliably across all four legs.

Closes #49. Part of #40. Stacked on #58.

@jrosskopf jrosskopf changed the base branch from feature/gh-48-macos-notarised to main May 22, 2026 16:58
Part of #40. Closes #49.

Adds four new CI jobs that exercise the self-packaging surface on
every platform flapi builds for:

  pack-smoke-linux-amd64   ubuntu-24.04
  pack-smoke-linux-arm64   ubuntu-24.04-arm
  pack-smoke-macos         macos-latest
  pack-smoke-windows       windows-latest

Each job:
- downloads the platform's `flapi` artifact from the existing
  windows-build / linux-build / osx-universal-build jobs
- builds a tiny fixture tree (flapi.yaml + one endpoint + sample SQL)
- runs `flapi pack --in fixture --out out-a`
- runs `out-a info`, asserts the entry list contains the fixture files
- runs `out-a unpack --to extracted`, diffs files byte-for-byte
- runs a second `flapi pack ... --out out-b` with the same
  `SOURCE_DATE_EPOCH=1700000000` and asserts
  `sha256(out-a) == sha256(out-b)` -- the reproducible-build invariant
  baked into archive_io (#41)

The macOS leg additionally:
- runs `otool -l` to confirm the unbundled binary carries the
  reserved `__FLAPI/__bundle` segment from link time
- runs `codesign --verify --strict out-segment` after the default
  reserved-segment pack -- the notarisation precondition this whole
  approach was built for
- runs `flapi pack --macos-append --out out-append` and confirms
  `info` still discovers the bundle (ad-hoc legacy path still works,
  signature is intentionally invalid -- we do not run codesign verify
  here)
- runs an oversized-payload pack (32 MiB into a 16-MiB segment) and
  confirms it exits non-zero with both "FLAPI_RESERVED_BUNDLE_MIB"
  and "reserved|exceeds" in the error message

The Windows job uses pwsh; pack/info/unpack/sha256 logic mirrors
Linux via PowerShell idioms.

`create-release` now `needs` the four new jobs in addition to the
existing build + smoke matrix, so a broken pack on any platform
blocks the release.

The existing `integration-tests` job (which already runs on
ubuntu-24.04 amd64) auto-picks up `test_self_packaging.py` (#56) and
`test_env_overrides.py` (#57) via pytest discovery -- no change
needed there.

Note: on macOS we deliberately do NOT assert reproducibility because
the codesign step may include non-deterministic data; on Linux and
Windows the host-bytes + appended-archive layout is reproducible by
construction.
@jrosskopf jrosskopf force-pushed the feature/gh-49-ci-cross-platform branch from ec12140 to 78900c0 Compare May 22, 2026 18:44
jrosskopf added 4 commits May 22, 2026 21:22
Previously LocateBundle(path) only did the EOF tail scan. Only
LocateBundleInSelf() probed the __FLAPI/__bundle Mach-O section
first. That asymmetry meant flapi info / flapi unpack -- both of
which call LocateBundle directly against a given binary -- couldn't
find bundles that flapi pack wrote into the reserved segment on
macOS.

Caught by CI: pack-smoke-macos reported

  Packed 3 entries (683 bytes) into out-segment
  Bundle: none (filesystem mode)

The pack succeeded (3 entries went into the section), but info
checked only the EOF tail and saw nothing.

Fix: move the section-probe into LocateBundle(path), where every
caller benefits. LocateBundleInSelf becomes a thin wrapper. The
fall-through to EOF tail is preserved so --macos-append bundles
(legacy ad-hoc layout) remain discoverable.

Found by CI on PR #59 (#49 cross-platform smoke jobs).
… array-match

Two follow-up fixes surfaced by CI on PR #59:

1. macOS: `flapi pack --macos-append` always tried to codesign the
   output, which fails with "main executable failed strict
   validation" because the appended ZIP after __LINKEDIT puts the
   binary outside what codesign considers signable. That's the
   documented trade-off -- append-mode output is explicitly not
   notarisable. Now we warn and continue; only the
   reserved-segment path treats codesign failure as fatal.

2. Windows pack-smoke: `$info -notmatch "x"` on a PowerShell
   array returns the filtered subset, not a boolean -- so the
   negation in the if statement was always truthy. Pack and info
   both worked correctly (info actually listed flapi.yaml, sqls/...);
   the test logic was the bug. Join the captured lines into a scalar
   string before -notmatch.

Re-running the macOS leg should now show pack-smoke-macos passing
through the --macos-append step. Re-running the Windows leg should
get all the way to the reproducibility check.

Found by CI on PR #59 (#49 cross-platform smoke jobs).
CI on macos-latest failed to build src/pack.cpp:
  error: no member named 'cerr' in namespace 'std'

Caused by the previous fix that warns instead of throws on the
--macos-append codesign-failure path. <ostream> doesn't drag
std::cerr in; we need <iostream> explicitly.
@jrosskopf jrosskopf marked this pull request as ready for review May 22, 2026 22:46
@jrosskopf jrosskopf merged commit 80ffbc0 into main May 22, 2026
21 checks passed
@jrosskopf jrosskopf deleted the feature/gh-49-ci-cross-platform branch May 22, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Self-packaging #9: CI cross-platform verification (Linux x86/ARM, macOS, Windows)

1 participant