Skip to content

Add --lint to validate a project without building - #23

Merged
jordancalhoun merged 4 commits into
codecarton:nextfrom
rodchristiansen:feat/lint
Aug 2, 2026
Merged

Add --lint to validate a project without building#23
jordancalhoun merged 4 commits into
codecarton:nextfrom
rodchristiansen:feat/lint

Conversation

@rodchristiansen

@rodchristiansen rodchristiansen commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

--lint checks a project's build-info and layout — identifier style, name safety, notarization-without-signing, missing payload/scripts, non-executable scripts without a shebang — and reports findings without running a build.

Note: this PR intentionally does not warn on a missing .pkg extension. The companion "munki-pkg drop-in compatibility" PR normalizes the resolved name to end in .pkg, so such a warning would be redundant. If that PR isn't merged, the only effect is the absence of that one advisory warning.

Tests: LinterTests; full suite green (26 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 a --lint command to validate package projects without building them.
    • Reports errors and warnings for invalid metadata, missing payloads or scripts, and script configuration issues.
    • Supports JSON and YAML project metadata formats.
    • Returns a failing status when errors are detected and a success status otherwise.
  • Tests

    • Added coverage for valid projects and common lint failures and warnings.

--lint loads the build-info and checks it without invoking pkgbuild, for a
fast PR/CI pre-check. Errors (empty version, name that isn't a single path
component, project with neither payload nor scripts, undecodable build-info)
exit non-zero; warnings (non-reverse-DNS identifier, name not ending .pkg,
notarization without signing, script missing a shebang or exec bit) are
advisory. Findings go to stderr; the exit code is the machine signal.

Tests: LinterTests cover clean, error, and warning cases. verify-loop.sh
lints a good project (passes) and a bad one (must fail).
@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: 87a5f57e-63cd-4d39-aa48-eed3f676b51a

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

Adds a non-building linter for Swift package projects, exposes it through a new --lint CLI command, reports findings and exit statuses, and adds unit and verification-loop coverage for valid and invalid projects.

Changes

Package linting

Layer / File(s) Summary
Project lint validation
swiftpkg/Linter.swift, swiftpkgTests/LinterTests.swift
Defines lint findings and validates metadata, package contents, script shebangs, and executable permissions, with tests covering errors and warnings.
CLI lint command
swiftpkgCLI/CLI.swift, swiftpkgCLI/SwiftPkg.swift
Adds --lint, dispatches lint requests, prints findings to standard error, and returns failure when errors exist.
Command-line verification
scripts/verify-loop.sh
Checks lint success for a valid temporary project and failure for a malformed project.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLIParser
  participant CLICommand
  participant SwiftPkg
  participant Linter
  CLIParser->>CLICommand: resolve --lint options
  CLICommand->>SwiftPkg: dispatch lint command
  SwiftPkg->>Linter: lint project
  Linter-->>SwiftPkg: return findings
  SwiftPkg-->>CLIParser: print results and return exit status
Loading

Suggested reviewers: jordancalhoun

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 clearly and concisely describes the primary change: adding --lint validation without building.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@rodchristiansen

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@rodchristiansen

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 57 minutes.

@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: 3

🤖 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/Linter.swift`:
- Around line 45-49: The identifier validation in Linter.swift around the
configuration identifier checks must validate every dot-separated component,
emitting the existing style warning for leading, repeated, or trailing dots as
well as identifiers without a dot. Add regression cases in
swiftpkgTests/LinterTests.swift covering leading-, repeated-, and trailing-dot
identifiers at lines 38-46.
- Around line 85-91: Update the install-script validation around
fileManager.fileExists(atPath:) to detect when script.path refers to a
directory, emit an error finding, and reject it before the shebang and
executable-permission checks. Preserve the existing checks for valid script
files.

In `@swiftpkgTests/LinterTests.swift`:
- Around line 65-77: Update the warnsOnBadScript test’s makeProject call to use
addPayload: false, creating a payload-free project with install scripts. Keep
the existing shebang and executable warning assertions, and additionally assert
that every finding has warning severity so the scripts-only compatibility path
does not report an error.
🪄 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: cbe73391-1b3f-43e1-9385-fa5d2cae4bb5

📥 Commits

Reviewing files that changed from the base of the PR and between 25f222d and 7e21571.

📒 Files selected for processing (5)
  • scripts/verify-loop.sh
  • swiftpkg/Linter.swift
  • swiftpkgCLI/CLI.swift
  • swiftpkgCLI/SwiftPkg.swift
  • swiftpkgTests/LinterTests.swift

Comment thread swiftpkg/Linter.swift
Comment thread swiftpkg/Linter.swift Outdated
Comment thread swiftpkgTests/LinterTests.swift
…ories

The reverse-DNS check only tested for a dot, so ".example", "com..example",
and "com.example." passed; it now requires at least two non-empty dot-separated
components. lintScripts used fileExists, which also matches a directory, letting
a preinstall/postinstall directory skip the shebang check and pass the
executable-bit check; it now errors on a directory. Added regression tests plus
a scripts-only compatibility case.
@rodchristiansen

Copy link
Copy Markdown
Contributor Author

All three addressed in the latest commit: reverse-DNS validation now requires ≥2 non-empty dot-separated components (rejecting .example, com..example, com.example.); lintScripts now uses fileExists(atPath:isDirectory:) and errors when a preinstall/postinstall is a directory instead of silently passing; and I added the scripts-only compatibility test plus malformed-identifier regression cases. Suite green (30 tests).

warnsOnBadScript kept the default payload, so it proved the script findings
appear but not that they appear alone. A payload-free project with scripts is
supported, and nothing asserted that such a project escapes the "neither a
payload nor a non-empty scripts directory" error while still reporting the
shebang and permission warnings.

Drop the payload from that test and require every finding to be a warning.

Also cover the boundary the hasScripts check draws: a scripts directory holding
nothing but .DS_Store is not a scripts directory, and a project with only that
is still empty.
@rodchristiansen

Copy link
Copy Markdown
Contributor Author

Addressed. warnsOnBadScript now builds the project with addPayload: false and requires every finding to be a warning, so it proves the script findings appear alone — a scripts-only project escapes the "neither a payload" error while still reporting the shebang and permission problems. The clean scripts-only case was already covered by scriptsOnlyProjectIsClean; what was missing was the case where the scripts are also faulty.

Added one more while in there: a scripts directory holding nothing but .DS_Store is not a scripts directory, so a project with only that is still empty. That is the boundary the hasScripts check draws and nothing exercised it.

Suite green (31 tests) and ./scripts/verify-loop.sh passes.

@jordancalhoun
jordancalhoun changed the base branch from main to next August 2, 2026 14:01
@jordancalhoun
jordancalhoun merged commit af1fb2b into codecarton:next Aug 2, 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