bool HasErrors { get; }
Source
Gets whether the report contains errors.
Command runner abstraction for git and GitHub CLI calls.
Task<CommandResult> RunAsync(CommandInvocation invocation, CancellationToken cancellationToken)
Source
Runs a process and captures stdout and stderr.
invocationCommand invocation.cancellationTokenCancellation token.Command result.
Process command runner used by the default CLI.
Clock abstraction used by tests to make generated release dates deterministic.
DateOnly TodayUtc()
Source
Gets today's UTC date.
UTC date.
System clock used by the default CLI.
Mutable YAML sidecar metadata for release-note documentation pages.
Sidecars are dictionary-backed so unknown fields are preserved. Tagged-release conversion overwrites release-owned keys (title, summary, order, breadcrumbs, and trust) and leaves unrelated keys untouched.
Task<ReleaseSidecar> LoadAsync(string path, CancellationToken cancellationToken)
Source
Loads sidecar metadata from YAML.
pathSidecar path. Expected content is a YAML mapping at the document root.cancellationTokenCancellation token honored during file I/O.Loaded sidecar whose unknown keys are preserved for later serialization.
The current release flow expects the unreleased sidecar to contain documentation keys such as title, summary, page_type, nav_group, order, breadcrumbs, and trust. Missing keys are tolerated because ToTaggedRelease writes the release-owned fields. Malformed YAML is wrapped in ReleaseToolException with release-sidecar-invalid; I/O and cancellation failures bubble to the shared command diagnostic layer.
string ToTaggedRelease(SemVer version, DateOnly date)
Source
Converts unreleased metadata into tagged-release metadata.
versionTagged release version.dateRelease date.Tagged release sidecar YAML using underscored key naming for generated CLR-backed values.
This method mutates the loaded dictionary before serializing. It replaces release-owned metadata with final tagged-release values and preserves unrelated keys from the source sidecar. Callers should not reuse the instance for another version after conversion.
string UnreleasedTemplate()
Source
Creates reset unreleased metadata for the next release cycle.
Canonical unreleased sidecar YAML with title, navigation, trust metadata, and source guidance.
The template is complete enough for docs harvesting without requiring the previous sidecar. It intentionally resets release-specific trust metadata back to provisional language.
string UpdatePublicPublishedReleaseNotes(string content, string releasePath)
Source
Updates release note paths for rows classified as public and publishable.
contentExisting package index YAML.releasePathNew repository-relative release note path.Updated YAML content.
This method edits YAML line-by-line so existing comments and ordering survive a release PR. It expects the package index's current shape: package entries begin with two spaces and - project:, fields are indented with four spaces, and classification, publish_decision, release_notes_path, and order stay inside one package block. Different indentation or nested structures require updating this editor. Output is normalized to LF line endings with one trailing LF so release PR diffs are stable across platforms.
Immutable command invocation for release-owned external processes.
ExecutableExecutable name or absolute path.ArgumentsArgument list passed without shell evaluation.WorkingDirectoryWorking directory used for the process.TimeoutOptional wall-clock timeout. When omitted, release commands use the default bounded timeout.Captured command result.
Release readiness result.
bool HasErrors { get; }
Source
Gets whether the report contains errors.
Release preparation result.
Machine-readable release manifest.
Package release note path update recorded in the release manifest.
Serializable diagnostic record for release manifests.
ReleaseDiagnosticRecord FromDiagnostic(ReleaseDiagnostic diagnostic)
Source
Creates a serializable diagnostic record.
diagnosticSource diagnostic.Serializable record.
Structured publish outputs for GitHub Actions.
Maintainer-facing release evidence summary rendered in command reports and workflow outputs.
PathRepository-relative evidence bundle path.SchemaEvidence bundle schema.StatusDraft or tag-bound validation status.SubjectSha256Stable subject digest for the evidence bundle.DocsReleaseManifestSha256Optional AppSurface Docs archive manifest digest referenced by the evidence bundle.CatalogExactTreePathOptional catalog exact tree path referenced by the evidence bundle.TagCommitOptional tag commit validated at publish time.AttestationAttestation requirement state.Parsed release CLI options shared by every command.
Minimal SemVer 2.0 model used by release automation.
SemVer Parse(string value)
Source
Parses a release version and rejects leading-v tags, build metadata, and invalid SemVer shapes.
valueVersion string supplied by the user.The parsed version.
bool IsStable { get; }
Source
Gets whether the version is a stable SemVer identity.
bool IsProtectedPrereleaseWorkflowCompatible { get; }
Source
Gets whether this prerelease version can trigger the protected prerelease package workflow.
string TagName { get; }
Source
Gets the annotated git tag expected for this version.
string Build(SemVer version, DateOnly date, string unreleased)
Source
Converts the living unreleased note into a tagged release note.
versionRelease version rendered in the heading and generated comment.dateRelease date rendered with invariant yyyy-MM-dd formatting.unreleasedUnreleased Markdown content. Canonical input starts with an exact # Unreleased heading.Tagged release Markdown with a generated comment header and a trailing newline.
The method first parses Markdown to catch syntax problems, but it does not use the returned syntax tree to rewrite content. It then replaces only the exact top-level # Unreleased heading and two known narrative phrases using ordinal matching. Variants in casing or wording are left unchanged. Output is deterministic apart from the supplied version and date, uses Environment.NewLine for generated sections, and trims trailing whitespace from the source body. Callers should run release readiness checks first because duplicate headings, missing phrases, or concurrently edited Markdown are not treated as errors.
string ResetUnreleased(SemVer previousVersion)
Source
Creates the next-cycle unreleased proof artifact.
previousVersionVersion that just moved into tagged release files.Canonical unreleased Markdown for the next cycle, including the previous version reference and a trailing newline.
This reset intentionally discards the prior living-release body after it has been copied into a tagged release note. It preserves the expected section order for future checks: overview, shaping work, included changes, and migration watch.
Task<PackageIndexSummary> LoadAsync(string path, CancellationToken cancellationToken)
Source
Loads a package index summary from YAML.
pathPackage index path.cancellationTokenCancellation token.Package index summary.
IReadOnlyList<PackageIndexEntry> PublicPublishedPackages { get; }
Source
Gets public package rows whose publish decision is publish.
Package manifest root shape used by the release tool.
List<PackageIndexYamlEntry> Packages { get; init; }
Source
Gets the package rows.
Package manifest row shape used by the release tool.
string Project { get; init; }
Source
Gets the project path.
string Classification { get; init; }
Source
Gets the classification string.
string? PublishDecision { get; init; }
Source
Gets the publish decision string.
string? ReleaseNotesPath { get; init; }
Source
Gets the release notes path.
Package row included in a release manifest.
CLI entry point for AppSurface release preparation and publishing validation.
Task<int> Main(string[] args)
Source
Launches the release CLI with process IO streams.
argsCommand-line arguments supplied to the process.Process exit code where 0 indicates success.
Task<int> RunAsync(string[] args, TextWriter standardOut, TextWriter standardError, string currentDirectory, CancellationToken cancellationToken = default, ICommandRunner? commandRunner = null, IReleaseClock? clock = null)
Source
Runs the release CLI against supplied IO streams and an explicit working directory.
argsCommand-line arguments, including command and options.standardOutWriter that receives reports, structured output, and help.standardErrorWriter that receives diagnostic envelopes and invalid usage.currentDirectoryDirectory used to resolve repository-relative defaults.cancellationTokenCancellation token for file and process work.commandRunnerOptional command runner seam used by tests.clockOptional clock seam used by tests.0 for success; otherwise a non-zero exit code.
AppSurface Console root module for the release cockpit.
Per-invocation execution context supplied by the CLI entry point.
CurrentDirectoryDirectory used to resolve default repository-relative paths.string RenderCheck(ReleaseCheckResult result)
Source
Renders a check report.
resultCheck result.Markdown report.
The report shape is stable for workflow comments and maintainer review: # Release readiness report, a summary bullet list, ## Generated files, optional ## Release evidence bundle, ## Errors, then ## Warnings. Empty diagnostics render as - None. Generated file paths and diagnostic codes are wrapped in inline code; diagnostic text is not escaped beyond normal Markdown rendering. Consumers should key off headings and diagnostic codes rather than line numbers.
string RenderPreparation(ReleasePreparationResult result)
Source
Renders a prepare report.
resultPreparation result.Markdown report.
Preparation reports begin with the check report contract, then append a manual review gate, optional evidence summary, and either ## Dry-run plan or ## Files written based on ReleasePreparationResult.DryRun. Paths are repository-relative bullets. This distinction is the only dry-run marker in the report, so callers that publish the report should preserve that heading.
string RollForward(string changelog, SemVer version, DateOnly date, string releasePath)
Source
Resets the compact Unreleased ledger and inserts a tagged changelog section after it.
changelogExisting changelog content. Canonical input contains a single ## Unreleased heading.versionRelease version inserted as ## {version} - yyyy-MM-dd.dateRelease date rendered with invariant yyyy-MM-dd formatting.releasePathRepository-relative release note path linked from the new section.Updated changelog content with a reset Unreleased section and the tagged section inserted or appended.
The algorithm is intentionally text-based to preserve surrounding Markdown. The detailed release narrative lives in releases/unreleased.md before preparation and releases/v{version}.md after preparation; the changelog keeps only the durable compact ledger. If ## Unreleased is missing, the canonical compact section is appended before the new tagged section. If the first-release placeholder follows ## Unreleased, that placeholder block is replaced. Duplicate release sections are not de-duplicated; callers should run readiness checks before calling this method. Malformed heading hierarchies and concurrent changelog edits can therefore produce surprising placement, so this helper should only be used on the repository's canonical changelog shape.
Validates release readiness without mutating repository files.
bool FailOnWarningsOption { get; set; }
Source
Gets a value indicating whether check should fail on warning diagnostics.
bool AllowExistingTargetsOption { get; set; }
Source
Gets a value indicating whether check may review already-generated release artifacts.
Generates the coordinated release pull request payload.
Validates tag state and emits GitHub Release workflow outputs.
string? Tag { get; set; }
Source
Gets the annotated release tag to publish.
string? GitHubOutputPath { get; set; }
Source
Gets an optional GitHub Actions output file.
Shared CliFx option surface and diagnostic handling for release commands.
ValueTask ExecuteWithDiagnosticsAsync(IConsole console, Func<ReleaseOptions, CancellationToken, Task<int>> executeAsync)
Source
Runs command logic with release diagnostic rendering.
consoleCliFx console.executeAsyncCommand implementation.A task that completes after command execution.
ReleaseServices CreateServices(ReleaseOptions options)
Source
Creates service objects for the resolved repository root.
optionsResolved command options.Workspace, checker, preparation, and publishing services.
string? ResolveTag(SemVer version)
Source
Resolves the command tag, when relevant.
versionParsed release version.The tag value, or null for commands that do not use a tag.
string? ResolveGitHubOutputPath(string repoRoot)
Source
Resolves the GitHub Actions output path, when relevant.
repoRootResolved repository root.The output path, or null for commands that do not write workflow outputs.
Task WriteReportAsync(ReleaseOptions options, string rendered, TextWriter standardOut, CancellationToken cancellationToken)
Source
Writes the rendered command report to stdout and to the optional report path.
optionsResolved command options.renderedRendered Markdown report.standardOutStandard output writer.cancellationTokenCancellation token.A task that completes after the report is written.
string? VersionText { get; set; }
Source
Gets the SemVer 2.0 release version without a leading v.
string? DateText { get; set; }
Source
Gets the release date for prepare, formatted as YYYY-MM-DD.
bool DryRun { get; set; }
Source
Gets a value indicating whether the command should avoid repository mutations or publishing.
string? RepositoryRoot { get; set; }
Source
Gets the repository root. Defaults to the current directory.
string? ReportPath { get; set; }
Source
Gets an optional readiness report output path.
string CommandName { get; }
Source
Gets the command name used by release validation logic.
bool FailOnWarnings { get; }
Source
Gets whether this command should turn warning diagnostics into a failing exit code.
bool AllowExistingTargets { get; }
Source
Gets whether this command may review already-generated release artifacts.
Command services bound to a resolved workspace.
WorkspaceWorkspace path helper.CheckerReadiness checker.PreparationRelease preparation workflow.PublishingRelease publishing workflow.JSON serializer configuration for release artifacts.
JsonSerializerOptions Options { get; }
Source
Gets indented camel-case JSON options.
Creates release artifacts from the living unreleased note.
Task<ReleasePreparationResult> PrepareAsync(ReleaseOptions options, CancellationToken cancellationToken)
Source
Generates release files or, in dry-run mode, returns the planned edits.
optionsRelease command options. Date defaults to the injected clock when omitted.cancellationTokenCancellation token.Preparation result containing readiness diagnostics and planned or written repository-relative paths.
Preparation is a deterministic repository-file rewrite: it runs readiness checks, reads the unreleased note and sidecar, builds versioned release artifacts, rolls CHANGELOG.md, updates public published package note paths, resets unreleased files, and records diagnostics in the release manifest. Dry-run mode performs all reads and rendering but does not write files. The method does not create git branches, tags, commits, package artifacts, or GitHub Releases; workflows own those operations. Callers should treat any readiness errors as blocking and should avoid running against a dirty or concurrently modified tree. Writes are sequential rather than transactional. If the local process fails after some files are written, rerun git status and remove or revert the partial generated artifacts before retrying so create-only target checks do not stop the next run.
Creates and validates checked-in release evidence bundles.
Release evidence is repository consistency evidence, not a signature or hosted-build attestation. The bundle ties together release-owned files, package release-note paths, optional docs archive catalog fields, and split commit identities so release preparation can be reviewed in a pull request and publishing can validate the same bundle at the annotated tag commit.
ReleaseEvidenceBundle BuildDraft(ReleaseWorkspace workspace, SemVer version, string releaseClassification, DateOnly date, string? contentSourceCommit, string releaseNoteContent, string releaseSidecarContent, string releaseManifestContent, IReadOnlyList<PackagePathUpdate> packagePathUpdates)
Source
Builds a draft release evidence bundle for release preparation.
Task<ReleaseEvidenceValidationResult> ValidatePreparedAsync(ReleaseWorkspace workspace, SemVer version, string releaseClassification, string? contentSourceCommit, CancellationToken cancellationToken)
Source
Validates a checked-in release evidence bundle in the current worktree.
ReleaseEvidenceValidationResult ValidateTag(SemVer version, string releaseClassification, string tag, string tagCommit, string releaseNoteJson, string releaseSidecarJson, string releaseManifestJson, string evidenceJson)
Source
Validates release evidence read from an annotated tag.
DI-backed execution runtime for release CliFx commands.
Task RunAsync(string[] args, Action<ConsoleOptions>? configureOptions = null)
Source
Runs release commands through the shared AppSurface command-service primitive.
argsCommand-line arguments.configureOptionsOptional console configuration.A task that completes when command execution finishes.
Validates release inputs and computes release readiness diagnostics.
Task<ReleaseCheckResult> CheckAsync(ReleaseOptions options, CancellationToken cancellationToken)
Source
Runs local release readiness checks.
optionsRelease command options.cancellationTokenCancellation token.Readiness result with errors, warnings, and generated paths.
Repository path helper for release-owned files.
Paths are rooted under RepositoryRoot and accept slash-separated repository-relative inputs. PathFor rejects rooted paths and traversal that would escape the repository. Use IsUnderPath when checking paths that come from command-line input, temporary files, or other untrusted sources.
string ReleaseNotePath(SemVer version)
Source
Gets the absolute path for a release note file.
versionRelease version.Absolute release note path.
string ReleaseSidecarPath(SemVer version)
Source
Gets the absolute path for a release note sidecar file.
versionRelease version.Absolute sidecar path.
string ReleaseManifestPath(SemVer version)
Source
Gets the absolute path for a release manifest file.
versionRelease version.Absolute manifest path.
string ReleaseEvidencePath(SemVer version)
Source
Gets the absolute path for a release evidence bundle file.
versionRelease version.Absolute release evidence bundle path.
string PathFor(string relativePath)
Source
Resolves a repository-relative path and verifies that the result stays inside the repository root.
relativePathRepository-relative path using slash separators and no leading root.Absolute path under RepositoryRoot.
ArgumentExceptionThrown when relativePath is rooted or traverses outside the repository.string DisplayPath(string path)
Source
Formats an absolute path as a slash-normalized repository-relative path.
pathAbsolute path.Repository-relative path when possible.
bool IsUnderPath(string root, string path)
Source
Determines whether a path is under the supplied root.
rootRoot path.pathCandidate path.true when the path is equal to or below the root.
string RepositoryRoot { get; }
Source
Gets the absolute repository root.
string ChangelogPath { get; }
Source
Gets the absolute changelog path.
string UnreleasedPath { get; }
Source
Gets the absolute unreleased note path.
string UnreleasedSidecarPath { get; }
Source
Gets the absolute unreleased sidecar path.
string PackageIndexPath { get; }
Source
Gets the absolute package index manifest path.
string TemplatePath { get; }
Source
Gets the absolute tagged-release template path.
Prevents trimming coverage from depending on generated process-start wiring.
Validates tag state and produces GitHub Release workflow outputs.
Task<PublishOutputs> PublishAsync(ReleaseOptions options, CancellationToken cancellationToken)
Source
Validates an existing annotated tag and extracts release notes from the tag commit.
optionsPublish command options. The version and tag must match, and stable versions are blocked until stable package publishing is protected.cancellationTokenCancellation token.Structured workflow outputs for GitHub Release creation.
PublishAsync is create-only: it verifies annotated tag shape, reachability from origin/main, prerelease package publication, absence of an existing GitHub Release, and presence of releases/v{version}.md in the tag commit. The tag commit must also contain the release sidecar, release manifest, and release evidence bundle; missing or invalid tag-bound artifacts fail fast before a GitHub Release is created. The method writes the tag's release note to a temporary file so workflows can pass a stable notes path to GitHub's release action.
Task WriteOutputsAsync(PublishOutputs outputs, ReleaseOptions options, CancellationToken cancellationToken)
Source
Writes publish outputs to a GitHub Actions output file when requested.
outputsPublish outputs.optionsRelease command options. ReleaseOptions.GitHubOutputPath must be a file path, not a root directory.cancellationTokenCancellation token.Scalar outputs use name=value. Multiline outputs use GitHub's delimiter form. Existing files are appended to match GITHUB_OUTPUT behavior.
Diagnostic envelope with a stable code and reader-actionable context.
ReleaseDiagnostic Error(string code, string problem, string cause, string fix, string docs)
Source
Creates an error diagnostic.
ReleaseDiagnostic Warning(string code, string problem, string cause, string fix, string docs)
Source
Creates a warning diagnostic.
string Render()
Source
Renders the diagnostic envelope for CLI stderr.
Human-readable diagnostic envelope.
Exception that carries a structured release diagnostic.
ReleaseDiagnostic Diagnostic { get; }
Source
Gets the structured diagnostic.