Files
ReleaseKit/docs/roadmap/releasekit.md

16 KiB
Raw Blame History

ReleaseKit: shared release process

Status: The v0.2.0 implementation, policy adoption, and architecture remediation are complete. Audita's module-selected local and CI integration is committed at 17421bc; broader consumer adoption and live deployment remain.

This roadmap records the shared release-process opportunity identified in PromptKit, ConfigKit, Audita, Notarius, Narratio, and Scriptorium on 2026-09-29, and the remaining adoption goals. The internal package inventory records implemented capability owners and behavioral evidence. The architecture policy and ADR 0001 own the facade boundaries and rationale. The initial foundation, policy adoption, and remediation plans are preserved in Git history; the current v0.2.0 completion evidence is in the implementation plan.

Opportunity

All six repositories follow the same base release policy. Their release scripts repeat stable-version parsing, candidate and release-note checks, local and remote tag checks, validation of one captured commit, explicit Git publication, and recovery guidance for uncertain pushes. They differ in publication approach, tag type, validation commands, and artifact delivery. The mechanics of embedding a release version and cross-building an application are further candidates for standardization.

If the projects deliberately standardize publication approach and tag type, ReleaseKit can own most of the repeated release-process code. The current six repositories contain 942 lines of release shell scripts and 742 lines of dedicated release tests, or 1,684 lines total. A directional estimate is that roughly 1,000–1,250 lines could leave those repositories: about 450–500 lines of publication scripts, 150–250 lines of checks and builders, and 400–500 lines of repeated publication tests. ReleaseKit would gain its own implementation and tests, so this is a reduction in local code, not a net line-count saving.

Selected publication standard

  1. Prepare and commit a version-matched note at docs/releases/<version>.md.
  2. Publish the candidate commit to authoritative main before invoking the release command.
  3. Run one command that checks the candidate and unused stable version, runs that repository's offline candidate validation once, and rechecks mutable state.
  4. Create an annotated tag at the validated commit with a message derived from the checked-in note. Push only that exact tag object to the authoritative endpoint. A successful tag push completes the source release.
  5. Treat hosted release pages and binary assets as asynchronous delivery with their own status. Do not poll CI or require artifact availability to declare the source release complete.

This would replace Audita's atomic branch-and-tag push, ConfigKit's and Notarius's branch push during the release command, and Audita's, Notarius's, and Narratio's lightweight tags. Those changes require updates to each repository's adopted release policy and procedure; they are not merely code cleanup.

Architecture

ADR 0001 owns the accepted facade decision. The architecture policy owns the adopted responsibility and dependency rules, including the runtime version boundary. The root releasekit package provides the public facade for candidate validation, source publication, selected-target builds, and tagged-source delivery. The Gitea page and asset-delivery client is integrated through that facade and the CLI. The separate public buildinfo package provides lightweight runtime version access.

Each facade owns its public named types and documented contracts. Public operations, inputs, outcomes, errors, and callbacks express deliberately selected consumer needs. Handles retain private implementation state; explicit forwarding and conversions connect the public contracts to internal representations.

Git operations, Go toolchain execution, Gitea integration, and orchestration bookkeeping belong under Go's internal/ boundary. Dependency direction runs from the facades into internal implementation, with internal collaborators using contracts defined inside that boundary. Implementation packages can evolve while the supported public contracts retain their compatibility obligations.

The buildinfo facade keeps runtime version access lightweight. Applications read their embedded version through its supported accessor. ReleaseKit's builder and runtime implementation own the linker symbol and injection mechanism together.

The buildinfo facade follows three constraints:

  • Its public API exposes only runtime information justified by consumer needs.
  • Its implementation uses the standard library and a small internal dependency graph independent of release orchestration.
  • Dependency checks protect that boundary, and build-and-run integration tests verify that the builder injects the selected version and the public accessor reports it correctly.

The release executable calls the orchestration facade and handles command parsing, output, and exit codes. Library errors and outcomes report meaningful publication state, including partial progress and uncertainty. Go declarations and GoDoc own exact public API contracts; additional public packages and extension interfaces require a deliberate decision grounded in durable consumer needs.

Compiled-export and dependency checks protect facade ownership. External-package behavioral tests protect error identity, value ownership, callback lifetime, and observable outcomes across forwarding and conversions.

Shared tool boundary

Provide a versioned ReleaseKit executable backed by a Go library. Existing shell entry points can become short invocations of that executable. A library alone would still require a caller in every shell-driven repository.

ReleaseKit should own:

  • stable-version parsing and committed-note identity checks;
  • authoritative Git endpoint resolution, candidate capture, clean main and remote-branch agreement checks, and unused local/remote tag checks;
  • one validation invocation bound to the captured candidate, followed by cheap rechecks before publication;
  • annotated-tag creation and verification, explicit tag-object push, and one bounded authoritative-ref check after a failed or ambiguous push;
  • a standard version accessor in the public buildinfo facade, backed by release-build injection of the selected tag;
  • a cross-build API that accepts the application's main package and selected targets, initially Linux amd64, Linux arm64, and macOS arm64 (Go's darwin/arm64), including an empty set for source-only projects;
  • consistent release build flags and asset naming, plus a host-runnable build that checks the executable's reported version; and
  • Git publication tests using temporary local remotes, including remote advancement, rejected pushes, lost acknowledgments, and unavailable remotes.

Each repository should retain its product name, compatibility decisions, candidate-specific checks, main package, selected build targets, delivery declaration, and a small test of its ReleaseKit wiring. For example, PromptKit exercises its consumer examples. Applications would adapt their existing version output to use the buildinfo accessor; one agreed version command or flag would let ReleaseKit smoke-test a host-runnable build the same way. Cross-built binaries would use the same injection mechanism, but compilation alone would not establish that they run correctly on another platform. ReleaseKit should run the selected checks and builds without silently adding binary gates to source-only projects.

The build API should own the mechanics rather than require each application to provide a custom linker symbol or shell loop. The initial target list reflects the current projects, not a permanent limit on future platforms. It should support both candidate cross-build checks and artifact builds from the tagged source, using the same version and target selection.

Woodpecker and Gitea delivery

A common tag-triggered Woodpecker workflow can run ReleaseKit against the tagged source. Repository-local YAML should declare the pinned tool image and credentials; each repository's ReleaseKit configuration should declare its delivery destination and any binary main package and target set. ReleaseKit can own version injection, cross-builds, checksum generation, and delivery. All projects should receive hosted Gitea release pages. Source-only repositories declare zero binary assets and deliver the committed release note without a binary build or checksum manifest.

The shared delivery command should create or find the release for the tag, use the checked-in note, and add declared binary assets and their checksum manifest where applicable. A retry should inspect existing assets, verify matching content, add only missing items, and stop on a conflict. An existing checksum manifest must remain consistent with any later addition. The release and its attachments are separate remote operations, so partial delivery must be expected and reported clearly.

Audita, Narratio, and Scriptorium currently use the Woodpecker release plugin with file-exists: skip and overwrite: false. Those settings avoid deliberate replacement by filename, but they do not prove that a rebuilt file has the same bytes as a previously published file. Content comparison would be a reason to implement a shared Gitea delivery client rather than rely solely on the plugin's skip behavior.

Hosted pages are selected for source-only projects, including PromptKit, ConfigKit, Notarius, and ReleaseKit. Adoption adds a page deliverable and delivery credentials to their procedures while preserving successful tag publication as source completion. The initial implementation should support both page-only and binary delivery through the same facade and command.

Failure states

ReleaseKit should report source publication and artifact delivery separately:

Event Result
Candidate validation or a pre-push guard fails No release was published; correct and validate the candidate again.
Tag push reports success Source release is complete; delivery may still be pending.
Tag push fails or its acknowledgment is lost Query the authoritative ref once. Confirm success only if the exact intended tag object is present on the intended commit; otherwise report failed or unknown status and retain the local tag.
Woodpecker or Gitea delivery fails after tag publication Keep the source release successful. Retry missing delivery from the immutable tag after checking existing content.
Published source or content needs correction Publish a new version; never replace content under the published version.

Consumer adoption target

All six projects should use the same selected publication, runtime version, build, and delivery conventions where those responsibilities apply. Repository entry points should contain only tool invocation and product-specific settings or checks. Shared mechanics and their repeated tests belong in ReleaseKit.

Application runtime code should obtain version information through buildinfo. Source-only libraries need no runtime import or automatic binary deliverable. Projects declaring binaries should use the common target selection and version injection; hosted delivery should use content-aware retries for both page-only and binary releases. Existing published tags, notes, pages, and assets retain their historical contents.

Consumer policy and procedure changes should describe the deliberately adopted standard and its compatibility impact. Adoption should demonstrate reduced local infrastructure and one coherent release model.

Representative migration review

Read-only reviews of the available PromptKit and Narratio checkouts were verified on 2026-10-06 at 4b8169d and 8afb49c; both worktrees were clean. These are migration assessments, not changes to either consumer.

Consumer Retain in its profile and product checks Adaptation and procedure changes
PromptKit, source-only Product identity; its Summary, Compatibility, Upgrade, and Changes release-note sections; checks that exercise the three maintained Go examples and their product outputs. No runtime buildinfo import or binary targets are needed. Its current release scripts total 209 lines. The projected deletion is about 120–160 lines of shared publication and generic validation machinery; retain the note-content rule and product-example checks as a configured additional check plus a small wiring test. PromptKit already pushes main before publication and creates annotated tags. Its release policy and procedure still need to add the hosted note page, delivery credentials, module-selected CI invocation, and separate delivery status and recovery.
Narratio, binary Product/module identity, the ./cmd/narratio main package, narratio executable name, Linux amd64, Linux arm64, and macOS arm64 targets, and the narratio <version> command. Keep the module-graph check (go mod tidy -diff) if it remains useful as a product-specific candidate check. Its release, check, asset-builder, and helper scripts total 304 lines; two dedicated release test files total 741 lines. Projected deletions are about 220–280 shell lines and 450–650 repeated test lines, leaving a small consumer wiring test and any retained module/note checks. Replace the private linker-version variable with buildinfo.Version() while preserving the current command output. Its policy and procedure need to change lightweight tags to annotated tags, state that a successful tag push completes source publication, and move plugin-based page/assets delivery to the shared versioned CI command with content-aware retries and separately provisioned delivery credentials.

The deletion figures are estimates from the reviewed files, not measured migration results or a promise that each line will disappear. Product checks, version output, target choices, and at least one ReleaseKit integration check remain with each consumer. The existing cross-project estimate of 1,000–1,250 potentially removable lines remains directional until migrations are completed. The self-contained ReleaseKit fixtures exercise a meaningful source-only compatibility check through local Git publication, and a buildinfo binary consumer with selected targets through local HTTP delivery. These are separate publication and delivery operations.

Remaining work

  • Use the v0.1.0 baseline for initial consumer integration under the maintainer procedure. Consumers that adopt the maintained tag workflow select ReleaseKit through the tagged source's go.mod rather than a separate workflow version value.
  • Choose the broader consumer integration test plan and migration sequence. Evaluate API clarity, product-specific checks, and the infrastructure removed from each consuming repository.
  • Provision delivery credentials and activate consumer Woodpecker workflows as part of deployment. The tagged consumer's module configuration selects the ReleaseKit version used by the maintained workflow.
  • Verify live Gitea delivery and Woodpecker execution. Existing verification uses local Git and HTTP fixtures; no live deployment has been verified.
  • Keep ReleaseKit executable binary distribution deferred until deliverables are deliberately selected. Selecting binaries would require delivery declarations, target checks, automation responsibilities, and corresponding release policy and procedure updates.
  • Evaluate a maintained ReleaseKit CI image or specialized validation/build settings when a demonstrated consumer need justifies their maintenance cost under ADR 0002.

Reviewed implementations