Skip to content

Docs: enable SPI-hosted DocC + landing page + CI guard#275

Merged
dfed merged 5 commits intomainfrom
claude/enable-spi-hosted-docc
Apr 20, 2026
Merged

Docs: enable SPI-hosted DocC + landing page + CI guard#275
dfed merged 5 commits intomainfrom
claude/enable-spi-hosted-docc

Conversation

@dfed
Copy link
Copy Markdown
Owner

@dfed dfed commented Apr 20, 2026

Summary

Turns on Swift Package Index's DocC hosting for SafeDI and adds a CI job that fails when our own doc rot creeps in. Downgrades the prebuilt/sourceBuild mutual-exclusion #error to a #warning so we can depend on swift-docc-plugin.

Changes

Mutual-exclusion #error#warning

The previous `#if prebuilt && sourceBuild #error("...")` in `Sources/SafeDI/Decorators/SafeDIConfiguration.swift` tripped whenever a dependency activated all traits during resolution — notably `swift-docc-plugin`. Downgrading to `#warning` keeps the diagnostic for users who explicitly enable both (e.g. `swift build --traits prebuilt,sourceBuild`) while letting swift-docc-plugin's symbol extraction succeed.

What actually happens with both traits enabled: the prebuilt binary wins the tool-name collision (both the artifact bundle's `SafeDITool` and the source target named `SafeDITool` are visible to `context.tool(named:)`, and SPM resolves to the binary). Source edits are silently ignored. The warning tells the user about this instead of leaving it as a mystery.

Normal paths:

  • `swift build` (default): only `prebuilt` active → binary, no warning.
  • `swift build --traits sourceBuild`: `--traits` replaces defaults, so only `sourceBuild` active → source-built tool, no warning.
  • `swift package generate-documentation`: activates all traits → warning fires, but the collision is moot because DocC doesn't invoke build-tool plugins; `--warnings-as-errors` on `swift-docc-plugin` scopes to DocC's own doc warnings, not swift compile warnings, so strict mode still passes.

Sources/SafeDI/SafeDI.docc/SafeDI.md

Minimal landing page, adapted from the README pitch:

  • `## Overview` — what-is-SafeDI + code sample
  • `## Getting Started` — three-step integration pointing to the Manual for depth
  • `## Topics` — organizes the auto-generated API reference into decorator macros, configuration, and delayed-instantiation types

DocC harvests `///` comments for every public symbol automatically, so this one file unlocks the whole reference tree.

.spi.yml

Opts us into SPI's DocC hosting. Only `SafeDI` is documented — `SafeDIMacros` is a compiler plugin (not user-facing); `SafeDITool` is a build tool (not library). Leaving platform unpinned lets SPI build on whatever works.

CI docc job

Uses `swift-docc-plugin` directly: `swift package generate-documentation --target SafeDI --warnings-as-errors`. The plugin's per-target scoping keeps swift-syntax's internal DocC noise out of strict mode, which `xcodebuild docbuild` can't do (it walks the whole target graph).

What this doesn't do

  • Doesn't delete Manual.md. The Manual stays on GitHub; SPI-hosted DocC complements it.
  • Doesn't add README links to the SPI URL yet. Per plan discussed: wait for SPI to successfully build once before wiring up README links. A follow-up PR adds them + adds `swiftpackageindex.com` to the markdown link-check exclusion list.
  • Doesn't migrate the Manual into DocC articles. That's a separate, larger decision.

Verification

  • `swift build` (default traits, `prebuilt` active) — plugin uses the prebuilt binary; no warning (verified with a local sentinel-in-generated-output test)
  • `swift build --traits sourceBuild` — plugin uses the source-built `SafeDITool`; no warning (verified with the same sentinel test)
  • `swift build --traits prebuilt,sourceBuild` — warning fires, build succeeds, binary wins
  • `swift test --traits sourceBuild` — 885 tests pass
  • `./CLI/lint.sh`
  • Local `swift package generate-documentation --target SafeDI --warnings-as-errors` succeeds (the #warning fires during symbol extraction but doesn't fail DocC's strict mode)

Next steps (separate PRs)

  1. After this lands and SPI picks it up, verify the hosted URL loads cleanly.
  2. Tag a release (SPI documents the latest tagged version, not `main`).
  3. Add README badge/link to the hosted docs + add `swiftpackageindex.com` to link-check exclusions.
  4. Optional: migrate sections of Manual.md into DocC articles piecewise.

🤖 Generated with Claude Code

Three pieces:

1. **`Sources/SafeDI/SafeDI.docc/SafeDI.md`** — landing page adapted
   from the README pitch. Overview + code sample + three-step getting
   started + `## Topics` organizing the auto-generated API reference
   into decorator macros, configuration, and delayed-instantiation
   types.

2. **`.spi.yml`** — opts SafeDI into Swift Package Index's DocC
   hosting. No platform pin (SafeDI builds on all Apple platforms, so
   SPI picks what works).

3. **CI `docc` job** — runs `xcodebuild docbuild` on every PR and
   fails when DocC emits warnings or errors inside our own files
   (`SafeDI.docc/` or `/Sources/SafeDI/`). Warnings from dependency
   packages are tolerated because DocC analyzes the whole target
   graph in xcodebuild mode, and swift-syntax's internal API surface
   has sparse doc comments that would otherwise blow up the build.
   `swift-docc-plugin` would scope natively but its symbol extraction
   trips SafeDI's `#error` trait-conflict guard; the grep-based
   warning filter is the pragmatic hermetic alternative.

Manual.md stays intact — the SPI-hosted DocC complements it, doesn't
replace it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 20, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (f9f10d0) to head (cf1f408).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #275   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           41        41           
  Lines         6796      6796           
=========================================
  Hits          6796      6796           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…swift-docc-plugin

**Traits refactor**
The old `prebuilt` + `sourceBuild` pair needed a `#error` guard to
reject the both-enabled case, which turned out to be unreachable in
practice and was the reason `swift-docc-plugin` (which activates all
traits during plugin resolution) tripped the build.

Drop the `prebuilt` trait. The remaining `sourceBuild` trait is
off-by-default; when off, `SafeDIToolBinary` provides the tool. When
on, the source-built `SafeDITool` target resolves the plugin's tool
lookup instead. Removing the mutual-exclusion `#error` is safe because
the trait values are no longer in conflict — there's nothing to be
mutually-exclusive with.

`SafeDIToolBinary` is now unconditional on the plugin dependency list.
SPM traits can't express "when this trait is NOT active," so we trade
always-linking the artifact for clean resolution in both trait states.

**DocC strict mode**
With no trait conflict, we can depend on swift-docc-plugin directly.
That gives us `--warnings-as-errors --target SafeDI` scoping, which the
`xcodebuild docbuild` path can't offer (it walks the whole graph, so
swift-syntax's internal DocC noise always fails strict mode).

The CI docc job drops the grep-filter post-processor and the xcodebuild
docbuild invocation — the plugin's per-target scoping replaces both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9eaf5ccc58

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Package.swift Outdated
dfed and others added 3 commits April 20, 2026 11:30
…ce tool

Collapsing to a single `sourceBuild` trait (with `SafeDIToolBinary`
unconditional) silently broke the trait's contract: SPM's
`context.tool(named: \"SafeDITool\")` resolved to the prebuilt binary
whose artifact bundle exposes a tool literally named `SafeDITool`, so
the source-built target was never invoked even when the trait was on.
Verified with a local sentinel test — generated output showed no
sentinel under `--traits sourceBuild`.

Restore the original `prebuilt` + `sourceBuild` trait pair, each gating
their respective plugin dependency. The `#error` mutual-exclusion guard
in `SafeDIConfiguration.swift` stays removed — it was the only reason
`swift-docc-plugin` (which activates both traits during resolution)
tripped compilation. DocC doesn't invoke build-tool plugins, so the
binary-vs-source tool resolution doesn't matter during docs builds,
and normal builds activate exactly one trait so resolution stays
unambiguous.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DocC doesn't compile samples, but readers who copy-paste the landing
page code hit a syntax error when the body contains a literal ellipsis
character. Use a block comment so the sample compiles when pasted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Downgrade the old `#error` mutual-exclusion guard to `#warning`. The
error was removed in this PR to unblock swift-docc-plugin (which
activates all traits during resolution). A warning lets us still
surface the misconfiguration for users who explicitly opt into both
traits (e.g. `swift build --traits prebuilt,sourceBuild`) — the
prebuilt binary wins the tool-name collision, so source edits are
silently ignored otherwise.

The warning also fires during swift-docc-plugin symbol extraction,
where it's harmless — DocC doesn't invoke the SafeDIGenerator build-
tool plugin. Verified `--warnings-as-errors` on the DocC plugin
continues to pass (it scopes to DocC's own documentation warnings,
not swift compile warnings in the source).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dfed dfed merged commit 5076684 into main Apr 20, 2026
18 checks passed
@dfed dfed deleted the claude/enable-spi-hosted-docc branch April 20, 2026 20:01
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.

1 participant