Add `aspire publish --verify` to detect drift between checked-in generated artifacts and the AppHost
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
## Summary
Add a verification mode to `aspire publish` that regenerates a manifest/artifact set into a
throwaway location and reconciles it against an existing (checked-in) output directory, failing with
an actionable diff when they differ. This makes "checked-in generated artifacts are stale" a fast,
local/CI-catchable error instead of something discovered late in a downstream pipeline.
Proposed shape:
```bash
aspire publish -o .generated --verify
```
- Exit `0` — checked-in output matches what publish would produce now.
- Exit non-zero — drift detected (stale / missing / orphaned files), with a diff summary and the exact
command to fix (`aspire publish -o .generated`).
## Motivation
A common first-party workflow is to `aspire publish -o `, commit the generated
Ev2/OneBranch/Bicep/Compose/k8s artifacts, and treat that directory as reviewable, source-of-truth
adjacent output. The invariant "every AppHost change must be followed by regenerating ``" is
enforced only by developer discipline today. The typical failure is slow: change `apphost.cs`, forget
to re-publish, and a downstream pipeline fails on a diff the developer then has to reverse-engineer.
This was prototyped downstream (in a private first-party repo) as a pair of PowerShell + bash scripts
that shell out to `aspire publish` into a temp dir and diff. Review feedback (and the author) landed on:
**this belongs in the CLI, not as duplicated scripts per repo.** Reasons:
- **It's publisher-agnostic.** "Publish to temp, diff against committed output, fail on drift" is
identical for Ev2, Geneva, Docker Compose, Kubernetes, and Bicep. Nothing about it is publisher-specific.
- **The CLI already owns the hard parts.** Output layout, which files are transient vs committable,
and any output-path embedding are owned by the publish pipeline. External scripts have to *guess* at
these (e.g. re-implement `.gitignore` honoring and path normalization), and drift out of sync as
publish behavior evolves.
- **Discovery is already solved.** `aspire publish` resolves the AppHost today; `-o --verify`
inherently means "this AppHost vs this directory," with no separate AppHost/output discovery step.
## Proposed behavior
1. Resolve the AppHost and output path exactly as `aspire publish` does today.
2. Publish into a secure temp directory instead of `-o`.
3. Reconcile the temp output against the existing `-o` directory:
- **stale** — a file exists in both but content differs,
- **missing** — publish produced a file not present in `-o`,
- **orphaned** — `-o` contains a file publish no longer produces.
4. Print a concise, sorted diff summary and the fix command; clean up the temp directory.
5. Exit `0` on an exact match, non-zero otherwise. Never mutate `-o`.
### Design questions to settle
- **Git-awareness / ignored files.** Some published outputs are intentionally not committed (large
binaries, compiled templates). A prototype only compared git-tracked files (via `git check-ignore`).
Options for the CLI:
1. Compare against git-tracked files in `-o` (adds a git dependency to `--verify`),
2. Honor a `.gitignore` inside `-o` (git-aware but scoped),
3. Have publishers declare which outputs are "committable artifacts" and verify only those
(cleanest long-term; more work).
Recommendation: ship (1)/(2) for v1 to match today's behavior; track (3) separately.
- **Output-path embedding.** If any artifact embeds its own absolute output path, a temp-dir publish
differs by path only. A prototype worked around this with path normalization. Better long-term:
make output path-independent. For v1, decide between normalization vs. documenting the limitation.
- **Determinism dependency.** `--verify` is only meaningful if generation is deterministic
(ordering, timestamps, GUIDs scrubbed). Should `--verify` assume this, or actively surface
nondeterministic fields as a distinct diagnostic?
- **Flag vs. subcommand.** `--verify` (or `--check`) on `publish` vs. a dedicated verb. Leaning flag,
since it reuses the full publish argument surface (`--environment`, publisher selection, etc.).
## Acceptance criteria
- `aspire publish -o --verify` exits `0` when `` matches current publish output, non-zero
otherwise, without modifying ``.
- Drift output distinguishes stale / missing / orphaned and prints the exact regenerate command.
- Works across publishers (validated against at least one non-trivial publisher).
- Documented, including CI usage and exit-code semantics.
## Out of scope
- Auto-fixing drift (verify only reports; the developer re-runs publish).
- Making generated output path-independent — related but separate.
- CI wiring for specific repos (that lives in the consuming repos).
Contributor guide
Assessment
This issue has not been assessed yet.