Publish fce as a NuGet tool and split lib/cli release trains#107
Merged
Conversation
Make FirstClassErrors.Cli publishable as a .NET tool (PackAsTool, ToolCommandName "fce", PackageId FirstClassErrors.Cli) so the error-documentation generator can be installed with `dotnet tool install` and run in CI, instead of requiring consumers to build it from source. The GenDoc worker the tool spawns as a separate process is bundled inside the tool package (tools/<tfm>/any) by the existing copy targets and resolved at runtime from AppContext.BaseDirectory; a packed tool was installed from a local feed and `fce generate` produced a catalog end to end. The package embeds the same SPDX SBOM as the two library packages, so pack.sh packs three projects now and its SBOM-present assertion covers the tool too; the release.yml comment is updated to match. No symbol package is produced for the tool (IncludeSymbols overridden): a .snupkg serves referenced libraries, not an executed tool. Publishing requires a nuget.org trusted-publishing policy for the new package id FirstClassErrors.Cli, as already required per published package. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2
The NuGet README led with the concept but understated what the package concretely delivers. Surface, honestly and without hype, the features that sell it: errors as values or exceptions, the two-audience message model that cannot leak internals (mapped onto RFC 9457), the 16 bundled Roslyn analyzers, zero runtime dependencies on .NET Standard 2.0, and the companion Testing package. Give the CI/CD documentation generation its own section now that the generator ships as the installable `fce` tool, with a concrete install-build-generate snippet so the "documentation as a build artifact" story is turnkey rather than a single passing mention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2
The CI/CD guide told readers to run `fce generate` without saying how to obtain the generator. Now that it ships as the FirstClassErrors.Cli tool, add the `dotnet tool install` step and a concrete install-build-generate snippet, so the pipeline it describes is reproducible. Mirrored in the French translation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2
Version and release the fce CLI independently of the library. release.yml now triggers on two tag families — lib-v* (FirstClassErrors + FirstClassErrors.Testing, in lockstep) and cli-v* (FirstClassErrors.Cli) — resolves the train and version from the tag prefix (or the workflow_dispatch component input) and packs only that train, so a lib release never republishes the CLI and vice versa. pack.sh takes a lib|cli scope selecting which projects to pack. Release notes are generated per train from Conventional Commit scopes (release-notes.sh: core, analyzers, testing for lib; cli, gendoc for cli), replacing --generate-notes so one train's notes never carry the other's commits; unscoped infrastructure commits are excluded. Checkout fetches full history and tags for that lookup, and the dry-run rehearses both trains' packing and note generation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2
fce is versioned independently of the library, so its NuGet page needs to state the relationship: it reads the FirstClassErrors documentation contract, a minor fce release adds support for a new contract while a major one changes fce's own surface, and at run time it binds to the target's own FirstClassErrors version. Set expectations for the 0.x preview (keep fce and the library on the same preview line) and pre-announce the 1.0 behaviour where fce declares the contract versions it supports. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2
Stamp the library assembly with an internal DocumentationContractVersion (starting at 1): the version of the shape fce reads by reflection — the [ProvidesErrorsFor] / [DocumentedBy] attributes, the DescribeError DSL, and the documentation model. It is bumped only when a change breaks that contract, independently of the library's SemVer. Every library published from now on carries the marker, so a future fce can identify the contract a target was built against, and treat an unmarked assembly as version 1, without relying on the library's package version — which the CLI's decoupled build does not preserve on its bundled copy. The attribute is internal diagnostic metadata for the tooling, not public API, and inert until the generator's compatibility gate reads it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 47203b9d06
ℹ️ 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".
…rors-nuget-sr1aiq # Conflicts: # .github/workflows/release.yml
release-notes.sh ran `git log` against the tag being released. On a workflow_dispatch publish the tag does not exist yet — gh release create makes it after the notes are built — so git log failed with "ambiguous argument" and the manual release path never reached release creation. A tag push was unaffected: the tag already exists. Take an optional end-ref (the commit) as the range's upper bound, defaulting to the tag so the dry-run's two-argument calls keep working; release.yml now passes $GITHUB_SHA. The tag name still serves only to exclude the tag being created from the previous-same-train tag lookup, so it need not resolve as a ref. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2
…rors-nuget-sr1aiq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Restructure the release process so the library (
FirstClassErrors+FirstClassErrors.Testing) and the CLI tool (FirstClassErrors.Cli) version and release independently — each with its own cadence and version, separate Git tags (lib-v*.*.*/cli-v*.*.*), train-scoped release notes, and selective packaging — and refresh the NuGet presentation.Type of change
Changes
FirstClassErrors.Cli.csproj— packaged as a .NET tool (PackAsTool,ToolCommandName fce,PackageId, description, tags, links, icon, README, embedded SBOM). Thefcedocumentation generator is now a publishable package; the GenDoc worker it spawns travels bundled inside the tool.release.yml— accepts acomponentinput and parses train-prefixed tags (lib-v*.*.*/cli-v*.*.*), resolves the train and version, and packs only that train (combined with the existing pre-release-flag handling).release-dryrun.yml— full-history checkout (fetch-depth: 0); rehearses both trains' packaging and note generation so neither path first runs in production.tools/packaging/pack.sh— takes alib/cliscope and packs only that train's projects.tools/packaging/release-notes.sh(new) — train-scoped release notes filtered by Conventional Commit scope (core,analyzers,testingfor lib;cli,gendocfor cli), so one train's notes never list the other's commits.FirstClassErrors/README.nuget.md— rewritten to surface concrete features (errors as values or exceptions, the two-audience message model, structured diagnostics, living documentation, the 16 bundled analyzers, zero runtime dependencies).FirstClassErrors.Cli/README.nuget.md(new) — NuGet page for thefcetool: install, usage, CI integration, and a compatibility note.DocumentationContractVersionAttribute.cs(+ test) — internal assembly marker stamping the documentation-contract version (1), independent of the library's SemVer, so a futurefcecan detect an unsupported contract; an unmarked assembly reads as version 1.doc/OperationalIntegration.en.md/.fr.md— document installing and runningfcein CI; French kept in sync.Testing
Run locally on this branch (Release), and re-run by CI on this PR:
dotnet build FirstClassErrors.sln— 0 warnings, 0 errorsdotnet test FirstClassErrors.sln— 609 passed, 0 failedFirstClassErrors.Analyzers.UnitTests) — 51 passedAlso verified:
pack.sh lib/pack.sh cliproduce their packages with the SBOM embedded; train-scoped release-notes isolation; and the packagedfcetool, installed from a local feed, generates an error catalog end to end.Documentation
doc/updateddoc/README.fr.md) updated if user-facing behavior changedDocumentationContractVersionAttributeis internal — no public API change. French was kept in sync where it applies (doc/OperationalIntegration.fr.md, alongside its English counterpart);doc/README.fr.mdand the rootREADME.mdwere not changed — only the NuGet-package READMEs, which have no French variant.Related issues
None — this arose from a working session, not a tracked issue.
https://claude.ai/code/session_01JejR9K9ABeQ8LqB5fwGeM2