Files
ReleaseKit/docs/roadmap/module-releases.md

490 lines
27 KiB
Markdown

# Roadmap: release individual Go modules
Status: planned feature; scope and ownership decisions resolved. The declaration
source for standalone builds remains an [open decision](#open-decisions).
## Purpose
Implement [ADR 0005](../adr/0005-module-release-scope.md) so a consumer can check,
publish, build, and deliver one selected Go module in a shared repository through
ReleaseKit's API and CLI. CommandKit's core and optional integrations provide the initial
consumer case. Each invocation should require only module selection, product
settings, and a semantic version, with ReleaseKit owning the common mechanics.
This roadmap describes the target and the work from the current baseline. It
does not select a ReleaseKit release version or prescribe a staged implementation
plan. The decisions recorded below govern the planned scope.
## Current baseline
The baseline is ReleaseKit v0.3.0 plus accepted ADR 0005; module selection is
not implemented.
- `ReleaseRequest` selects a repository, product, and version. `Candidate` and
`PublicationResult` expose the version without a distinct module or tag name.
- Candidate capture reads `docs/releases/<version>.md` from the captured commit.
Local and remote uniqueness checks, tag creation, push, and reconciliation use
the version as the Git tag.
- The shared validation path runs tests, selected vet, and package compilation
from the repository directory. Optional content checks inspect the committed
repository tree, including nested-module contents.
- Profiles support shared validation and additional commands. CLI product
checks run from the repository directory with candidate/version context.
- Artifact builds and delivery also use repository/version assumptions. The
consumer Woodpecker template selects `refs/tags/v*` and bootstraps ReleaseKit
from repository-root module files.
The existing candidate capture, declarative validation, exact-object publication,
and outcome reporting provide the foundation. Scope and identity need to be
carried through those paths together.
## Targeted end state
An invocation selects one module and one semantic version. Omitted selection
means the repository-root module. For example:
| Selected directory | Semantic version | Complete tag | Committed release note |
| --- | --- | --- | --- |
| Root | `v0.1.0` | `v0.1.0` | `docs/releases/v0.1.0.md` |
| `integrations/ckconfig` | `v0.2.0` | `integrations/ckconfig/v0.2.0` | `integrations/ckconfig/docs/releases/v0.2.0.md` |
| `integrations/ckprompt` | `v0.1.0` | `integrations/ckprompt/v0.1.0` | `integrations/ckprompt/docs/releases/v0.1.0.md` |
All mandatory and selected optional release checks use the selected module's
scope. Independently released nested modules are excluded from their parent's
automatic validation inputs. Shared file selections follow the
[resolved shared-file rules](#shared-file-validation-inputs). Checks may read
referenced material elsewhere in the captured repository without adding that
material's surrounding files to the input set. The checkout remains the boundary for outside-checkout link checks.
Repository cleanliness, branch, candidate commit, remote endpoint, and publication
guards remain repository-wide. The complete selected tag governs uniqueness,
publication, and recovery. A successful source-tag push remains completion.
Consumers prepare dependencies before offline candidate validation and choose
sibling release ordering. Dependency preparation for delivery targets the
selected module through the existing preparation operation when needed.
The feature covers individual releases. Batch publication, automatic release
selection, dependency-driven versioning, and arbitrary tag templates remain
outside this roadmap. Artifact builds, hosted delivery, and CI use the same
selected module and release identity. Delivery remains asynchronous and supports
both page-only and binary profiles. This work adds no consumer-installation,
proxy-availability, CI, or hosted-delivery publication completion gate.
## Work needed
### 1. Establish a shared module and release identity
**Rationale:** One resolved identity prevents checks and publication from using
different interpretations of the selected module or tag.
**Target:** Distinct repository directory, module directory, semantic version,
and complete tag are available throughout candidate handling and public evidence.
**Work:** Add an optional repository-relative module selection to the public
request and profile, with root defaults. Resolve it against the captured
repository and source appropriate to the operation once at the operation entry
point. Check, publication, and delivery use committed module declarations.
Standalone-build declaration sourcing follows the open decision below.
Pass the resolved identity through the operation's validation, publication,
build, and delivery collaborators. Standalone operations use the same resolution
logic. Derive tags using Go's standard module conventions, including
major-version directories: a `/v2` module stored at `integrations/ckconfig/v2`
uses `integrations/ckconfig/v2.0.0` for `v2.0.0`.
The same module stored at `integrations/ckconfig` uses that complete tag too.
Support both physical layouts and distinguish the module directory from its
tag prefix.
Carry the resolved identity through facade conversions, internal requests,
callbacks, results, and CLI diagnostics. Keep semantic-version validation
separate from full-tag handling. Exact field names belong in API design.
Keep shared identity resolution separate from each operation's prerequisites.
Populate resolved identity in public evidence as resolution succeeds, including
when a later step fails.
### 2. Apply one validation scope
**Rationale:** Consumers should select the release unit once and receive
consistent validation across enabled checks.
**Target:** Tests, vet, package compilation, formatting, distribution checks,
file-size checks, Markdown, and product-check context all correspond to the
selected module and its relevant release materials.
**Work:** Give the existing shared validation path both repository context and
module scope. Run Go commands in the module directory with the existing
offline and workspace-disabled environment. Select committed file inputs once
under the agreed boundary convention and apply that selection to each content
check. Build one ordered input set and retain the full repository lookup
separately for resolving Markdown destinations and existing committed-file
references. A link to another module may be checked for existence or its
target heading without scanning that module's unrelated documents.
Update module-distribution inspection to examine the selected release's inputs.
For example, an unrelated sibling's filesystem replacement should not fail the
selected module's check. Add shared files through the
[resolved selection rules](#shared-file-validation-inputs), carrying the selection
through the public request and profile and applying it to the shared file inputs.
Keep the optional checks optional and retain their existing individual rules,
error identities, cancellation, and cause propagation apart from the intended
scope change. Apply the boundary and path conventions recorded below.
### 3. Bind notes and publication to the resolved identity
**Rationale:** Different modules can use the same semantic version while
publishing distinct source identities and release notes.
**Target:** The selected module's committed note identifies its product and
semantic version. Publication and recovery address its complete tag.
**Work:** Resolve module-local note paths and retain existing title validation
and captured note bytes. Thread the complete tag through local guards, remote
guards, annotated-tag creation, exact-object push, and bounded reconciliation.
Include the module, version, and resolved tag in success and failure evidence
as available, alongside commit, endpoint, phase, state, and tag object. Preserve
the existing publication outcomes and repository checks. Root notes and tags
retain their ordinary forms.
### 4. Carry module identity through builds and delivery
**Rationale:** A selected module should mean the same release unit throughout
ReleaseKit's supported operations.
**Target:** Standalone builds, candidate build checks, direct delivery, and
prebuilt-asset delivery all operate on the selected module. Hosted pages use
its complete tag and module-local note; binaries report its semantic version.
**Work:** Extend build and delivery requests and profile mapping with the
shared module identity. Resolve main packages and module-owned build paths
from the module directory. Prepare build dependencies there, and preserve
existing offline artifact compilation and version smoke checks. Reuse
operation-local Go settings between validation and subsequent builds when
their environment inputs match. Carry module identity through build results
and receipts so prebuilt delivery verifies the selected module, version, tag
where applicable, candidate commit, settings, and artifact hashes. Keep
ordinary local builds usable without requiring a published tag.
Use the full tag for tagged-checkout verification, Gitea release lookup and
creation, and source links. Use the semantic version for binary version injection
and module release titles. Select the module-local note and retain existing
page/asset conflict, retry, and partial-delivery behavior. Test source URLs with
slash-containing tags. Distinct module tags can have separate Gitea pages at the
same commit. A page-only profile continues to require no application binary.
Use the new module-bound receipt format for prebuilt delivery. Consumers
regenerate receipts through the updated build operation as part of the clean
cutover described below.
Normalize build settings once for build execution, receipt binding, and
comparison. Receipt loading owns structure and identity validation; delivery
owns the local content-verification pass before hosted requests. Build-time
hashing records produced bytes independently.
### 5. Support module-aware CI delivery
**Rationale:** Local operations and tagged-source delivery need the same module,
profile, and version selection.
**Target:** The maintained consumer workflow accepts root and nested-module tags,
selects the corresponding profile, and delivers only that module's release.
Tool selection follows the tagged module configuration under
[ADR 0004](../adr/0004-module-selected-tooling.md).
**Work:** Extend tagged-checkout preparation and `ci-deliver` to distinguish the
full event tag from the semantic version. Adapt tag filters, profile selection,
tool bootstrap, and application dependency preparation. Use an explicit
module/profile mapping or equivalent small consumer declaration from the
verified tagged checkout. Resolve the physical module directory and profile
before tool bootstrap, and keep the selection consistent with the derived tag.
Verify the event commit before resolving that mapping and use the tagged
source's selected module files on retries.
Use the same small consumer selection declaration for the launcher and CI.
Bootstrap selects the module and profile; ReleaseKit verifies their consistency
with the derived identity. Reuse the verified checkout after tool bootstrap
with fresh read-only source checks. Standalone `ci-deliver` owns preparation
when it is needed.
For ordinary root tags, use the root profile and module bootstrap defaults.
Consumers using that pattern supply no module/profile mapping declaration.
Preserve temporary bootstrap metadata and existing credential handling. Cover
major-version directory layouts without assuming that trimming the tag always
identifies the physical module directory. Include page-only and binary examples.
### 6. Make API and CLI adoption small and coherent
**Rationale:** The shared release operation should replace consumer publication
logic with small per-module declarations.
**Target:** A shared launcher can select a profile and pass a semantic version;
API consumers express the equivalent operation. Additional checks receive
module context and repository context for explicit composition work.
**Work:** Map profile selection into the shared request, document relative-path
behavior, and provide module/tag context to configured commands. Retain existing
profile selection through `--config`; a separate CLI override for the module is
not needed to satisfy the initial request. Add a maintained multi-module example
showing root and integration profiles and a small shared launcher. Show dependency
preparation before offline validation and sequential sibling releases when a
dependency version must be published first. Keep product checks focused on
consumer assertions instead of repeating the standard suite.
Preserve the [simple root-module experience](#simple-single-module-experience),
including a small
profile, launcher, and CI workflow with no additional required declarations.
### 7. Verify and document the complete behavior
**Rationale:** This changes release identity and validation boundaries, including
the behavior of root modules with nested modules.
**Target:** Representative public API and CLI workflows establish that the
selected module is validated and only its intended tag is published.
**Work:** Extend existing behavioral tests using local repositories and
remotes. Cover root defaults; nested-module failure while the root suite
passes; equal versions across modules; major versions with and without
physical major-version directories; full-tag reuse; and failed or unknown
publication with the correct recovery identity. Exercise optional checks with
violations inside and outside the selected scope, parent selection excluding
independent nested modules, and cross-module Markdown references. Cover
omitted, replacement, and empty shared-file lists; optional default files;
missing explicit files; committed-tree lookup; duplicate inputs; and explicit
files in another module. Verify selection errors when optional file checks are
disabled, and that shared-file selection preserves enabled checks and Go
scope. Verify that repository guards still observe changes outside the
selected module and that module selection does not repeat the standard suite.
Reuse existing failure tests where they already protect unchanged behavior.
Add representative nested-module build/version-injection, new-format receipt
ownership and cutover, Gitea page/asset, and CI tag/profile/tool-selection
coverage. Use local HTTP servers for delivery and isolated synthetic module
proxies for bootstrap tests. Verify that retries use the original module
selection and full tag after the default branch changes. Verify a
representative simple root-module API call, profile, launcher, and CI workflow
using the new interface's defaults. Confirm that routine use adds no required
configuration fields, mapping declarations, or consumer decisions. Give
identity derivation, scope selection, and path conversion focused tests at
their owners. Use representative assembled workflows and reuse existing
failure coverage where the underlying behavior remains shared. Verify
preserved empty lists, contextual findings for equal blobs at different paths,
failure identity, and artifact-verification ownership.
Update GoDoc, CLI/configuration references, check/publication/build/delivery guides,
adoption examples, CI templates, and recovery guidance together. Supply migration
guidance in the next release's documentation, covering API and profile updates,
receipt regeneration, CI mapping, and root-module scope changes, including
exclusion of nested fixture modules from automatic file checks. Document
semantic version versus full tag in note titles and source/licensing links.
ReleaseKit's own maintainer policy continues to describe its root-module release;
consumer guidance explains how
multi-module consumers adapt their policies. Run the normal offline suite and
public API checks for implementation changes; validate documentation links and
examples. Synthetic dependency fixtures use isolated module caches.
## Resolved decisions
### Resolve identity once
Each ReleaseKit operation resolves repository directory, selected module
directory and declaration, semantic version, and complete tag once through
shared resolution logic. Pass that identity through candidate handling,
validation, callbacks, publication, builds, receipts, delivery, and evidence.
Subsystems consume the resolved module and tag. Repository-state, tagged-source,
and artifact verification continue against that identity through their existing
guards.
### Operation prerequisites and identity
Shared resolution supplies module and tag identity to each operation.
Candidate checking and publication retain clean source on the primary branch,
committed release notes, and their existing unused-tag checks. Delivery
retains clean tagged-source verification and supports historical releases and
detached checkouts. Ordinary builds retain their support for modified source
and operate independently of release-note and publication prerequisites.
Composite operations pass resolved identity to their internal collaborators.
Standalone operations use the same resolver at entry. Declaration sourcing for
ordinary builds is the open decision at the end of this document.
### One validation input set and full repository lookup
Construct file inputs when enabled checks or explicit shared-file validation
need them. Compute module boundaries and the ordered, deduplicated input set
once using committed metadata. File checks consume that set. Keep the full
repository lookup available for targeted reads and reference resolution, with scoped reader
ownership and cancellation preserved.
Deduplicate inputs by path. Reuse parsed results by blob identity while retaining
each source path's findings and relative destinations. This preserves the
existing selective caches and avoids retaining all inspected raw content.
### Version and tag boundaries
Keep semantic version and complete tag distinct in internal requests and evidence.
Version injection, artifact filenames, smoke checks, and note titles use the
semantic version. Git publication, recovery, and hosted release identity use
the complete tag. URL construction belongs to each protocol owner, which encodes
the literal tag for its route. Verify slash-containing tags in API and source
links.
### Failure evidence
Populate available module, version, tag, and commit evidence as resolution
progresses. Later failures retain that evidence alongside the operation's
existing phase, endpoint, tag-object, and outcome fields as available. Shared
facade conversions preserve these fields and the underlying error causes.
### Go environment ownership
Lazily discover effective Go settings for the selected module and reuse them
within the operation's validation and build phases when environment inputs are
unchanged. Ownership and reuse are operation-local. Dependency preparation keeps
its separate network-capable environment. Page-only delivery retains its path
without Go-tool discovery or application dependency preparation.
### Build settings, receipts, and asset verification
Normalize build settings once and reuse that representation for execution,
receipt binding, and comparisons. The new receipt format binds module identity
alongside existing source and artifact evidence. Loading validates structure,
identity, and file declarations. Delivery owns the content-verification pass
before hosted requests, for both API and CLI callers.
Build-time hashing records the produced bytes; delivery-time verification
establishes that the selected files still match them. Candidate and managed
delivery output stays outside the whole repository. Ordinary output paths use
the selected module's path base.
### CI bootstrap and checkout ownership
For module-mapped workflows, use one small committed consumer selection
declaration shared by the launcher and CI. Bootstrap verifies the triggering
tag and commit, selects the physical module and profile, and builds the tool
from that module's configuration. ReleaseKit verifies selection against the
derived identity.
The workflow prepares the checkout before tool bootstrap. Subsequent delivery
reuses that checkout with fresh read-only source checks. Standalone CI delivery
performs preparation when needed. Share verification behavior across these
entry paths, keeping root defaults and tagged-source selection on retries.
### Simple single-module experience
Ordinary root-module consumers use the new interface's defaults: omitted module
selection means the repository root, the complete tag equals the semantic
version, notes use `docs/releases/<version>.md`, and validation and builds run
from the root module. Shared-file defaults apply with input deduplication.
Root-tag CI uses the ordinary profile and repository-root tool bootstrap.
Routine use requires no additional configuration fields, module mapping
declarations, or recurring consumer decisions. A representative root-module
profile, launcher, and CI workflow remain equally small after migration.
Consumers supply their existing product facts through the new interface.
One-time API updates and receipt regeneration belong in the next release's
migration guidance under the clean-cutover decision below.
### Module boundaries
Use committed nested `go.mod` locations as module boundaries for ordinary source
directories. This follows Go's module structure and avoids a second registry of
independently versioned products. Vendored material remains part of the selected
module's file-check inputs. Nested fixture modules are excluded from automatic
parent file checks and can be exercised through the parent's tests or product
checks. Document this distinction and verify representative cases.
A repository-level workspace file outside the selected nested module's scope
does not fail that module's workspace-file check merely because it exists.
Shared release materials and referenced destinations receive the targeted
inspection described above.
### Shared file-validation inputs
Provide an optional shared-file list in the public request and profile. Its
selection applies only to shared file-validation inputs. Existing validation
selections control which checks are enabled. Enabled checks retain their rules,
and Go tests, vet, and package compilation retain the selected module's scope.
| Selection | Shared files added automatically |
| --- | --- |
| Omitted list | Repository-root `COPYRIGHT`, `LICENSE`, and `docs/oss/attribution.md`, when present. |
| Explicit list | The listed files replace the defaults. |
| Explicit empty list | No shared files. |
In Go, a nil slice selects the defaults; a non-nil empty slice selects none.
In JSON, omission or `null` selects the defaults; `[]` selects none. Preserve
this distinction through profile mapping and facade conversions, including
slice copying and profile serialization. Resolve the defaults once. Exact field
names belong in API design.
Paths are repository-relative literal file paths. This initial selection
supports individual files. Resolve presence and file identity from the captured
committed tree. Missing explicitly selected files produce a configuration or
validation error identifying the selected path, including when optional file
checks are disabled. Defaults are optional when absent.
Deduplicate the combined module and shared-file inputs by committed path, so a
file already in module scope or repeated in the list is checked once. Explicit
selection may name a file in another module; include that file alone. Reference
resolution continues to inspect destinations using existing rules and leaves
those destinations outside the input set unless independently selected.
### Relative paths and product commands
Anchor module selection and profile lookup at the repository. Anchor
module-owned validation and build paths, including file-size exclusions, and
configured product-command execution at the selected module. Keep diagnostic
file paths repository-relative and expose both directories to product commands.
Markdown destinations remain relative to their source documents.
Normalize selections and resolve path bases at a shared boundary. Translate
file selections and exclusions to canonical repository-relative paths before
checks consume them. Use module-based filesystem paths for execution and output,
and retain repository-relative diagnostic paths through conversions.
Root profiles use the repository-root module by default. Nested-module launchers
use the provided repository context for shared scripts. These conventions keep
per-module profiles self-contained without adding per-check path-base switches.
### Complete operation support
Implement module selection across checking, publication, artifact builds,
hosted delivery, and CI in this feature. The target is a coherent release API
for root and nested modules. CommandKit's current source-only use supplies an
adoption case while the feature supports the full existing release workflow.
Implementation may proceed in stages; feature completion includes all of these
operations. Omitted module selection uses the root module, and source publication
and asynchronous delivery retain their separate outcomes.
### Clean API and receipt cutover
Implement one current API and module-bound receipt format as the complete
supported interface after cutover. Consumers update their calls, profiles, and
CI declarations to that interface and regenerate prior receipts through the
updated build operation. Compatibility shims and legacy receipt readers are
outside the implementation scope.
Migration means guidance supplied in the next release's documentation. That
guidance explains the required consumer changes and the root-module validation
scope change; implementation completion covers the new interface directly.
## Open Decisions
### Declaration source for standalone builds
Candidate checks, publication, and tagged delivery resolve identity from
committed source. Ordinary builds currently permit modified source, including
module-file edits. Select how their identity resolver reads the module
declaration.
**Recommendation:** Read the selected module's working-tree `go.mod` for
standalone builds. Keep candidate and delivery operations tied to committed
declarations. This makes a local build's identity correspond to the files Go
actually compiles and supports development of a changed module path or a new
module directory before commit. The resolver's source is selected by the
operation, with shared derivation logic and existing source-verification
observations. Delivery continues to require a build verified against its
committed tagged source.
**Viable alternative:** Resolve standalone-build identity from the committed
`go.mod`, while allowing ordinary source-file edits during compilation. This
provides uniform committed identity across operations and simplifies declaration
access. It supports builds of modified code when the module declaration is
unchanged. Its cost is that module-path changes and newly created module
directories need a commit before identity can reflect them, and local identity
can differ from the declaration used by Go. Document that limitation explicitly
if this alternative is selected.