Skip to content

munki-pkg drop-in compatibility: name handling, keychain paths, --skip-import, receipt-only, notarization deferral - #21

Merged
jordancalhoun merged 8 commits into
codecarton:nextfrom
rodchristiansen:feat/munkipkg-compat
Jul 26, 2026
Merged

munki-pkg drop-in compatibility: name handling, keychain paths, --skip-import, receipt-only, notarization deferral#21
jordancalhoun merged 8 commits into
codecarton:nextfrom
rodchristiansen:feat/munkipkg-compat

Conversation

@rodchristiansen

@rodchristiansen rodchristiansen commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

A set of small behaviors that let the tool stand in for munki-pkg against real-world build-info without surprises. Each is independent; they're grouped because they share the "faithful drop-in" theme and are individually tiny.

  • Reject package names that escape the build directory (path-traversal safety).
  • Expand ${HOME} and ~ in signing keychain paths — munki-pkg expands these; a literal ${HOME} otherwise fails signing.
  • Accept --skip-import as an ignored no-op for pipelines that pass it.
  • Allow receipt-only projects (no payload and no scripts) to build.
  • Defer notarization_info validation until notarization actually runs, so --skip-notarization builds with incomplete notary config still succeed.
  • Append .pkg to the resolved name when it lacks the extension, matching munki-pkg — otherwise the artifact is extensionless and find '*.pkg' / munkiimport miss it.

Tests: PackageNameValidationTests, KeychainPathTests, CLITests, NotarizationDeferTests, PackageNameExtensionTests; full suite green (33 tests).


Part of a 9-PR series splitting a batch of features into small, themed, independently reviewable PRs. Each applies cleanly to main on its own; the ordering below only minimizes rebases as they land:

  1. Process and exit-code correctness: pipe drain, notarization failure, distinct exit codes #19 — process & exit-code correctness
  2. Version resolution: --pkg-version/--output-dir overrides and dynamic version tokens #20 — version resolution · munki-pkg drop-in compatibility: name handling, keychain paths, --skip-import, receipt-only, notarization deferral #21 — munki-pkg compatibility
  3. Add --output-format json build manifest #22 — json manifest · Add --lint to validate a project without building #23 — lint · Add --verify to check the built package against build-info #24 — verify · Add --provenance attestation sidecar #25 — provenance · Add .env build-time variable substitution for scripts #26 — .env substitution
  4. Add GitHub Action and Azure DevOps templates for building packages #27 — CI templates

Happy to squash, split, or reorder any of these to suit your review preferences.

Summary by CodeRabbit

  • New Features
    • Added support for building receipt-only packages (no payloads or scripts).
    • Added the --skip-import option (parsed/accepted without changing the normal build workflow).
  • Bug Fixes
    • Package names are normalized to always end with .pkg after version substitution.
    • Unsafe package names that could escape the build directory are rejected before invoking pkgbuild.
    • Incomplete notarization configuration no longer blocks --skip-notarization; errors surface only when notarization is attempted.
  • Tests
    • Added coverage for receipt-only builds, package name validation, keychain path expansion, and notarization deferral.

The output package path is build/<name> (and build/Dist-<name> for
distribution builds), with name coming from build-info after ${version}
substitution. A name containing a path separator or ".." wrote the
artifact outside build/ — a path traversal driven by untrusted build-info.

Validate the resolved name is a single, safe path component before the
build starts, throwing invalidConfiguration otherwise.

Add PackageNameValidationTests covering unsafe/safe names and an
end-to-end build that must throw before pkgbuild is ever invoked.
Build-info files commonly set signing_info.keychain to
${HOME}/Library/Keychains/signing.keychain. swiftpkg passed that value
to productbuild verbatim, so signing failed with "Could not find
appropriate signing identity ... in keychain at ${HOME}/...". Expand
${HOME} to the user home directory and resolve a leading tilde before
handing the path to productbuild/productsign, matching munki-pkg.
munki-pkg prompts to import the built package into a Munki repo and
offers --skip-import to suppress that prompt; CI pipelines pass it
routinely. swiftpkg never prompts, so it previously rejected the flag
as unknown and any pipeline passing --skip-import failed. Accept it as
a documented no-op so those invocations work unchanged.
A project that has build-info but neither a payload folder nor a scripts
folder is valid: pkgbuild --nopayload produces a receipt-only package
that installs no files but records a receipt Munki conditions can key
off. swiftpkg rejected these outright; munki-pkg builds them. The
component builder already emits --nopayload with no --scripts, so only
the up-front guard needed to go.
Loading a project with a present-but-incomplete notarization_info (e.g.
a bare password with no apple_id/team_id/keychain_profile) threw at load
time, so even --skip-notarization builds failed. munki-pkg tolerates the
incomplete block at load and only errors when notarization is actually
attempted. Parse it into a new .invalid(reason:) authentication case;
notarize() rejects .invalid, while skipped builds proceed unaffected.
build-info may set name without a .pkg suffix (e.g. MunkiBootstrap).
munki-pkg writes the artifact as <name>.pkg; swiftpkg used the name
verbatim, producing an extensionless file that find '*.pkg' and
munkiimport miss. Normalize the resolved name to end in .pkg after
${version} substitution, matching munki-pkg.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b1260cb7-9d7e-43b0-bb9d-010e2d85e7a0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The changes add receipt-only package verification, normalize and validate package names, defer invalid notarization errors until notarization, expand signing keychain paths, and support the ignored --skip-import compatibility flag.

Changes

Package Build Updates

Layer / File(s) Summary
Package naming and receipt-only builds
swiftpkg/BuildInfo.swift, swiftpkg/PackageBuilder.swift, swiftpkgTests/PackageName*Tests.swift, swiftpkgTests/ReceiptOnlyBuildTests.swift, scripts/verify-loop.sh
Package names receive a .pkg suffix and are validated as safe path components before building. Projects without payloads or scripts now build receipt-only packages, with shell and unit coverage.
Deferred notarization and signing paths
swiftpkg/BuildInfo.swift, swiftpkg/PackageBuilder.swift, swiftpkg/PackageSettingsDraft.swift, swiftpkgTests/NotarizationDeferTests.swift, swiftpkgTests/KeychainPathTests.swift
Incomplete notarization metadata produces .invalid authentication, which is rejected only when notarization runs; signing keychain paths expand ${HOME} and ~.
Skip-import compatibility flag
swiftpkgCLI/CLI.swift, swiftpkgTests/CLITests.swift
The ignored --skip-import flag is parsed, documented, and verified to preserve .build command resolution.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main compatibility changes and matches the changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@swiftpkg/PackageBuilder.swift`:
- Around line 89-92: Add targeted Swift compatibility coverage for the
receipt-only behavior in PackageBuilder: create a project without payload or
scripts using TemporaryDirectory and RecordingRunner, build it, and assert the
generated command uses the pkgbuild --nopayload path. Keep the test hermetic and
focused on payload-free package behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f6a05848-c43c-492a-b125-7c564980db8c

📥 Commits

Reviewing files that changed from the base of the PR and between 25f222d and 2ac58fc.

📒 Files selected for processing (10)
  • scripts/verify-loop.sh
  • swiftpkg/BuildInfo.swift
  • swiftpkg/PackageBuilder.swift
  • swiftpkg/PackageSettingsDraft.swift
  • swiftpkgCLI/CLI.swift
  • swiftpkgTests/CLITests.swift
  • swiftpkgTests/KeychainPathTests.swift
  • swiftpkgTests/NotarizationDeferTests.swift
  • swiftpkgTests/PackageNameExtensionTests.swift
  • swiftpkgTests/PackageNameValidationTests.swift

Comment on lines +89 to +92
// A project with neither payload nor scripts is valid: it builds a
// receipt-only package (pkgbuild --nopayload) that installs no files but
// records a receipt, which Munki conditions can key off. munki-pkg
// allows this, so swiftpkg does too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add hermetic receipt-only compatibility coverage.

Add a Swift test that builds a project with neither payload nor scripts using TemporaryDirectory and RecordingRunner, and asserts the generated command uses the receipt-only path.

As per coding guidelines, behavior changes affecting “payload-free packages” require targeted compatibility tests, and unit tests must use TemporaryDirectory and RecordingRunner.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@swiftpkg/PackageBuilder.swift` around lines 89 - 92, Add targeted Swift
compatibility coverage for the receipt-only behavior in PackageBuilder: create a
project without payload or scripts using TemporaryDirectory and RecordingRunner,
build it, and assert the generated command uses the pkgbuild --nopayload path.
Keep the test hermetic and focused on payload-free package behavior.

Source: Coding guidelines

Assert that a project with neither payload nor scripts invokes pkgbuild with
--nopayload and no --root, using TemporaryDirectory and RecordingRunner per the
repo's unit-test conventions.
@rodchristiansen

Copy link
Copy Markdown
Contributor Author

Added a hermetic receipt-only unit test in 620b918 (ReceiptOnlyBuildTests): builds a project with neither payload nor scripts via TemporaryDirectory+RecordingRunner and asserts pkgbuild is invoked with --nopayload and no --root. Full suite green (34 tests).

@jordancalhoun
jordancalhoun changed the base branch from main to next July 26, 2026 01:00
@jordancalhoun
jordancalhoun force-pushed the feat/munkipkg-compat branch from 39a691d to 568e647 Compare July 26, 2026 01:07
@jordancalhoun
jordancalhoun merged commit 1dc103d into codecarton:next Jul 26, 2026
2 checks passed
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.

2 participants