798 lines
50 KiB
Markdown
798 lines
50 KiB
Markdown
# Implementation plan: individual Go module releases
|
||
|
||
Status: complete; all 15 stages implemented and locally verified.
|
||
|
||
The subsequent [remediation review](remediation.md) records post-implementation
|
||
findings, corrections, and their verification. The baseline and stage accounts
|
||
below retain the implementation sequence.
|
||
|
||
## Objective and authority
|
||
|
||
Implement the complete target in the [module-release roadmap](module-releases.md),
|
||
including its resolved decisions, and [ADR 0005](../adr/0005-module-release-scope.md).
|
||
The [CommandKit request](commandkit-feature-request.md) supplies consumer context.
|
||
The implementation covers checking, publication, standalone and composite builds,
|
||
prebuilt receipts, hosted delivery, CLI profiles, launchers, and Woodpecker.
|
||
|
||
This document is an ordered implementation guide for a Luna coding agent.
|
||
Complete Stage 1 through Stage 15 in order. Each stage is a focused implementation
|
||
unit with its own behavioral verification. Intermediate stages represent partial
|
||
implementation; completion means that all stages are complete. Proceed through
|
||
the plan without pilot integrations, intermediate releases, external actions, or
|
||
human review gates. Architecture review and subsequent adoption testing follow
|
||
completion of this plan.
|
||
|
||
Read [development](../development.md) and the applicable policies before working.
|
||
Use the codebase graph for code discovery and inspect the current source before
|
||
editing. Follow the [agent policy](../policy/agents.md) for scoped commits after
|
||
completed nonempty units. Record completion under each stage with a short account
|
||
of the change and verification. Keep behavior documentation aligned as each
|
||
capability lands; the feature roadmap owns the desired scope and policy choices.
|
||
|
||
## Baseline and implementation boundaries
|
||
|
||
The starting point is the v0.3.0 implementation with accepted module-release
|
||
decisions. Current entry points are the root facade's `Check`, `Publish`, `Build`,
|
||
`Deliver`, `PrepareTaggedCheckout`, and `PrepareBuildDependencies`.
|
||
|
||
| Current owner | Change needed |
|
||
| --- | --- |
|
||
| `check.go`, `publish.go`, `build.go`, `delivery.go`, `preparation.go` | Facade-owned selection and evidence types, explicit conversions. |
|
||
| `internal/candidate`, `internal/publish` | Committed module identity, module-local notes, full-tag publication and recovery. |
|
||
| `internal/validation` | One selected input set, full repository reference lookup, selected-module Go execution. |
|
||
| `internal/git`, `internal/preparation` | Literal full-tag operations and shared tagged-source verification. |
|
||
| `internal/gotool`, `internal/build`, `internal/artifact` | Operation-local Go settings, normalized build settings, module-based execution and output. |
|
||
| `internal/delivery`, `internal/gitea` | Committed delivery identity, module-bound artifacts, full-tag hosted identity. |
|
||
| `cmd/releasekit`, `cmd/releasekit/internal/cli` | Profile mapping, command context, receipts, CI event handling. |
|
||
| `examples/woodpecker/`, consumer guides | Root defaults and module-selected bootstrap from tagged source. |
|
||
| `internal/apicheck` | Compiled facade ownership and dependency checks for the current API. |
|
||
|
||
Keep implementation behind the existing facades. `buildinfo` keeps its small
|
||
independent runtime dependency graph. Use the existing pure-Go build baseline,
|
||
standard version injection, target rules, error causes, publication outcomes,
|
||
and delivery recovery behavior. Network-capable preparation remains separate
|
||
from offline validation and compilation.
|
||
|
||
The feature is a clean API and receipt cutover. Update callers and fixtures to
|
||
the current interface directly. Migration is documentation for the next release.
|
||
Implement individual module releases without batch publication, automatic module
|
||
discovery, dependency ordering, arbitrary tag templates, or compatibility shims.
|
||
|
||
## Concrete interface and ownership choices
|
||
|
||
These choices settle routine design details needed to execute the stages. GoDoc
|
||
and canonical references become their owners when the corresponding code lands.
|
||
|
||
### Selection and evidence
|
||
|
||
- Add `ModuleDir string` to `ReleaseRequest`, `BuildRequest`, and
|
||
`DeliveryRequest`. Input values are repository-relative; omission or `.` selects
|
||
the repository-root module. Keep `Version` as the stable semantic version.
|
||
- Add `ModuleDir`, `ModulePath`, and `Tag` to `Candidate`, `PublicationResult`,
|
||
`BuildResult`, and `DeliveryResult`. Result `ModuleDir` is the resolved absolute
|
||
execution directory; `ModulePath` is the declared import path; `Tag` is the
|
||
complete derived Git tag. Retain existing repository, commit, and outcome
|
||
fields, adding `RepositoryDir` to results that currently lack it.
|
||
- Use one private identity value in `internal/releaseidentity`, containing the
|
||
absolute repository and module directories, canonical repository-relative
|
||
module subdirectory, declared module path, semantic version, complete tag, and
|
||
repository-relative note path. Source commit remains captured operation
|
||
evidence. Facades own their exported types and explicitly convert this value.
|
||
- Add `SharedFiles []string` to `ValidationSpec`. Profile syntax is
|
||
`validation.shared_files`; profile module selection is top-level `module_dir`.
|
||
Preserve nil versus non-nil empty slices through all conversions and JSON
|
||
round trips. Existing `--config` selects the profile; the initial CLI adds no
|
||
separate module override.
|
||
- Change `PrepareBuildDependencies` to accept a facade-owned
|
||
`DependencyPreparationRequest` containing `RepositoryDir` and `ModuleDir`.
|
||
Preparation selects the working-tree module directory and performs the
|
||
existing network-capable preparation there. It needs no release version or tag.
|
||
- Replace `TaggedCheckoutRequest.Version` with `Tag`. Add `VerifyOnly bool`:
|
||
the default prepares the checkout, while `true` performs read-only verification
|
||
of an already prepared checkout against `Tag` and `ExpectedCommit`.
|
||
- Add optional `DeliveryRequest.ExpectedTag` for the CI event's literal tag.
|
||
Delivery compares it with the resolved tag before builds or hosted requests.
|
||
Ordinary callers omit it. The field expresses exact event identity.
|
||
|
||
Identity derivation is shared; operation prerequisites remain with their owners.
|
||
Check and publication read declarations from the captured commit. Delivery reads
|
||
committed declarations from its source checkout and verifies the derived tag.
|
||
Standalone builds read working-tree declarations. Composite builds receive the
|
||
owner's already resolved identity. Populate evidence as it becomes available,
|
||
including on errors; an unresolved field remains unset.
|
||
|
||
### CLI events, receipts, and consumer mapping
|
||
|
||
- `check`, `publish`, `build`, and ordinary `deliver` continue to accept a
|
||
semantic `VERSION`. `ci-deliver` accepts a complete `TAG`; its last path
|
||
component supplies the semantic version and its full value supplies
|
||
`ExpectedTag`. The selected profile supplies the physical module directory.
|
||
- Add `ci-deliver --prepared` to select `VerifyOnly`. The maintained workflows
|
||
use it after bootstrap's verified checkout. Standalone `ci-deliver` retains
|
||
checkout preparation by default. Both paths require `--commit` and perform
|
||
fresh read-only tag, HEAD, cleanliness, and expected-commit checks.
|
||
- Add optional `--expected-module DIR` to `publish` and `ci-deliver` for mapped
|
||
launchers. Compare its normalized repository-relative selection with the
|
||
profile's selection before application work. This equality expectation binds
|
||
the bootstrap module to the profile; module selection still comes from the
|
||
profile. Root-only invocations omit it.
|
||
- Keep `.releasekit-build.json` as the private receipt filename. The new schema
|
||
requires `schema_version: 1`, absolute `module_dir`, declared `module_path`,
|
||
and complete `tag`, alongside the existing fields. The current unversioned
|
||
receipt is rejected with regeneration guidance. Implement one schema reader.
|
||
- For the maintained multi-module example, use a small committed
|
||
`release-modules.tsv` shared by the launcher and CI. Each row contains four
|
||
tab-separated fields: selector, event-tag shell pattern, repository-relative
|
||
physical module directory, repository-relative profile path. Use `.` for root.
|
||
A shared POSIX shell helper reads the table for either a selector or an event
|
||
tag. An event must match exactly one row; ambiguity is a selection error.
|
||
Tag patterns select a row; the library remains the authority for tag derivation.
|
||
The maintained example owns this integration declaration.
|
||
Root-only consumers continue without a mapping file or helper.
|
||
|
||
For example, root v0/v1 and root `v2` directory layouts can use separate rows
|
||
with patterns `v[01].*` and `v2.*`. A nested `/v2` module can map
|
||
`integrations/ckconfig/v2.*` to either physical layout. Keep patterns quoted and
|
||
evaluate them as shell patterns without `eval`. Document the table's literal
|
||
tab-separated syntax. No matching pattern changes the derived release tag.
|
||
|
||
## Stage 1: Implement shared identity derivation and literal tag operations
|
||
|
||
**Outcome:** One internal owner derives release identity for both source modes;
|
||
Git primitives accept full tag names independently of semantic-version rules.
|
||
|
||
**Steps:**
|
||
|
||
1. Introduce `internal/releaseidentity` with a selection resolver and shared pure
|
||
derivation. Resolve module paths once from repository-relative input, with
|
||
ordinary normalization and a final location inside the repository. Keep root
|
||
defaults. Require the selected module's own declaration; do not silently
|
||
fall back to an ancestor module or require a repository-root `go.mod` for a
|
||
nested selection.
|
||
2. Provide committed and working-tree declaration readers. Reuse existing Git
|
||
committed-file and reference resolution where needed. Preserve cause and
|
||
cancellation handling. Do not fetch module metadata or infer identity from
|
||
the remote host, a root module declaration, or a directory-name registry.
|
||
3. Extract the module directive with the existing `modfile.ModulePath` helper
|
||
and apply `module.SplitPathVersion`/`module.CheckPathMajor`. Full `go.mod`
|
||
inspection retains its selected validation owner. Retain stable-version
|
||
syntax and use the shared module-path major-version rules.
|
||
Derive the tag prefix from the physical repository-relative directory,
|
||
removing a final physical major-version component only when it equals the
|
||
declaration's slash major suffix. Distinguish a normal directory named `v2`
|
||
from a physical `/v2` layout using the declaration. Retain standard special
|
||
path-major handling supplied by `x/mod`.
|
||
4. Derive `<module>/docs/releases/<version>.md` from the physical directory,
|
||
including a physical major-version directory. Keep tag prefix and note path
|
||
as distinct values. The
|
||
[Go module reference](https://go.dev/ref/mod#vcs-version) and
|
||
[path-major helpers](https://pkg.go.dev/golang.org/x/mod/module#CheckPathMajor)
|
||
define the relevant Go rules.
|
||
5. Replace Git's version-shaped tag validation with literal tag-reference
|
||
validation using Git's reference rules. Rename parameters and internal
|
||
fields that actually represent tags. Preserve exact ref/object operations,
|
||
supported tag object types, and repository-state checks. Semantic-version
|
||
validation stays at release identity boundaries.
|
||
|
||
**Verification:** Owner tests cover root, nested, root physical `v2`, nested
|
||
physical `v2`, `/v2` declarations without physical major directories, a normal
|
||
directory named `v2`, incompatible version majors, normalized selection, missing
|
||
or malformed declarations, and selection outside the repository. Test committed
|
||
versus working-tree readers and full slash-containing Git refs. Update synthetic
|
||
fixtures that need genuine module declarations. Run affected package tests.
|
||
|
||
**Completed:** Added shared committed and working-tree module identity derivation,
|
||
module-local note paths, and literal Git tag-reference validation. Verified root,
|
||
nested, physical-major, and ordinary `v2` layouts, committed versus working-tree
|
||
declarations, and slash-containing local and remote tag operations. `go test ./...`
|
||
passes.
|
||
|
||
## Stage 2: Carry identity through facade types and candidate capture
|
||
|
||
**Outcome:** Candidate capture supplies module-local notes and recoverable identity
|
||
through the public facade.
|
||
|
||
**Steps:**
|
||
|
||
1. Add the selection and evidence fields specified above to facade-owned types
|
||
and their internal counterparts. Use focused conversion helpers where they
|
||
remove repeated evidence construction. Preserve `errors.Is`/`errors.As`
|
||
behavior and existing callback signatures using facade-owned `Candidate`.
|
||
2. In `internal/candidate.Capture`, capture repository state and commit, resolve
|
||
committed identity once, then read the selected module's committed note and
|
||
apply existing product/version title validation. Use the full tag for the
|
||
local unused-tag guard.
|
||
3. Return progressively populated candidate evidence when later capture,
|
||
validation, or recheck work fails. Update the public `Check` conversion so
|
||
it preserves returned evidence alongside the error instead of replacing it
|
||
with an empty candidate.
|
||
4. Pass identity through candidate validation and post-validation callbacks.
|
||
Keep clean `main`, repository-wide status, capture/recheck behavior, and note
|
||
bytes with their current owners. Prepare internal request wiring for later
|
||
validation and build stages without adding alternate legacy paths.
|
||
5. Update compiled API checks and fixtures for the chosen facade declarations.
|
||
Keep internal identity types out of exported signatures and fields.
|
||
|
||
**Verification:** Root notes retain their path and title. A nested candidate uses
|
||
its own note, including a physical major directory. Note/title/tag conflicts and
|
||
later validation failures retain available module, version, tag, and commit
|
||
evidence. An unrelated sibling worktree change still fails the repository guard.
|
||
Run candidate/facade tests and `go run ./internal/apicheck`.
|
||
|
||
**Completed:** Added module selection and identity evidence to the facade and
|
||
internal request/result types. Candidate capture now reads the selected
|
||
committed declaration and module-local note, guards the complete local tag, and
|
||
preserves available evidence through callbacks and later failures. Verified
|
||
nested physical-major capture, sibling cleanliness, facade error evidence,
|
||
`go test . ./internal/candidate`, and `go run ./internal/apicheck`.
|
||
|
||
## Stage 3: Publish and reconcile the complete selected tag
|
||
|
||
**Outcome:** Publication addresses one module's exact tag and retains its current
|
||
known-failure, confirmed, and unknown outcomes.
|
||
|
||
**Steps:**
|
||
|
||
1. Thread captured identity through `internal/publish`; use `Tag` for remote
|
||
uniqueness, annotated-tag creation, exact-object push, and reconciliation.
|
||
Continue using `Version` for product/version messages where it is semantic.
|
||
2. Preserve the authoritative endpoint, expected push URL comparison, remote
|
||
branch/commit guards, local rechecks, bounded reconciliation, and tag object
|
||
evidence. These checks verify state against the captured identity and do not
|
||
invoke identity derivation again.
|
||
3. Populate `PublicationResult` identity before later failures and preserve it
|
||
through facade conversions. Recovery output identifies the full tag and
|
||
authoritative endpoint rather than constructing a ref from the version.
|
||
|
||
**Verification:** Publish two modules with the same version at the same captured
|
||
commit to a temporary local remote; only their separate intended tags appear.
|
||
Reject reuse of the full tag. Add representative nested-tag rejected-push and
|
||
unknown-outcome evidence cases. Reuse existing endpoint and lost-acknowledgment
|
||
tests for unchanged recovery mechanics. Run publication and Git tests.
|
||
|
||
**Completed:** Publication now uses the captured full tag for remote uniqueness,
|
||
local annotated-tag creation, exact-object push, and reconciliation. Recovery
|
||
errors identify the full tag and authoritative endpoint, while publication
|
||
results retain identity through preflight and later failures. Verified two
|
||
modules publishing the same version at one commit, full-tag reuse rejection,
|
||
nested rejected and unknown outcomes, and `go test . ./internal/git ./internal/publish`.
|
||
|
||
## Stage 4: Construct the selected committed file inputs
|
||
|
||
**Outcome:** One ordered path set defines automatic validation inputs, while the
|
||
full committed repository remains available for reference lookup.
|
||
|
||
**Steps:**
|
||
|
||
1. Add scope selection beside `CandidateTree` metadata handling. Select entries
|
||
beneath the module directory, excluding descendants belonging to nested
|
||
committed `go.mod` boundaries. Boundary detection uses locations and does
|
||
not parse unrelated declarations. Treat vendored contents as selected-module
|
||
material; vendored `go.mod` entries do not establish exclusion boundaries.
|
||
2. Preserve the complete repository index and committed resolver separately.
|
||
Add a selected-input view used by checks; avoid replacing the complete tree
|
||
with a filtered map that breaks cross-module links or committed symlinks.
|
||
3. Resolve `SharedFiles` once against committed entries. Apply the roadmap's
|
||
optional defaults, replacement list, explicit empty list, missing-explicit
|
||
errors, and explicit cross-module file selection. Literal paths identify
|
||
individual files; metacharacters have no selection semantics. Retain existing
|
||
symlink/reference behavior where applicable.
|
||
4. Merge selected module entries and shared files in stable repository-path
|
||
order, deduplicating by committed path. Preserve per-path context when
|
||
different paths share a blob. A referenced destination joins the input set
|
||
only through independent module or explicit-file selection.
|
||
5. Create inputs only when enabled file checks or explicit shared-file validation
|
||
require them. An explicit missing file fails even with all file checks off;
|
||
a default-only request with no file checks needs no file-input construction.
|
||
Keep lazy blob access, operation context, cancellation, and reader cleanup.
|
||
|
||
**Verification:** Scope tests cover root and nested modules, independent sibling
|
||
and nested fixture modules, vendor inclusion, optional defaults, replacement,
|
||
empty, missing explicit, duplicates, and selected files in another module.
|
||
Use committed files rather than worktree presence. Show that full lookup still
|
||
resolves an excluded module's destination. Metadata-only selection starts no
|
||
batch content reader. Run validation tree/scope tests.
|
||
|
||
**Completed:** Added a sorted, deduplicated committed input view for the selected
|
||
module and explicit shared files while retaining full-tree reference lookup.
|
||
Nested module boundaries exclude their descendants, vendored modules remain
|
||
included, and shared-file defaults, replacement, empty selection, and missing
|
||
committed files follow the roadmap. Verified root and nested scopes, worktree-only
|
||
file rejection, cross-module references, lazy content reads, `go test ./internal/validation ./internal/candidate .`,
|
||
and `go run ./internal/apicheck`.
|
||
|
||
## Stage 5: Apply selected inputs to every optional file check
|
||
|
||
**Outcome:** Formatting, workspace, local replacement, file-size, and Markdown
|
||
checks use the same selected inputs with their existing individual rules.
|
||
|
||
**Steps:**
|
||
|
||
1. Update checker iteration to consume the selected-input view from Stage 4.
|
||
Retain the full repository resolver for targeted source and destination reads.
|
||
An unrelated sibling declaration or root workspace file outside a nested
|
||
module's inputs does not independently fail that module's check.
|
||
2. Translate module-relative file-size exclusions once into canonical
|
||
repository-relative paths before checker execution. Preserve existing literal
|
||
file/directory exclusion matching, limits, findings, and check order.
|
||
3. Retain formatter selection, symlink treatment, committed replacement
|
||
inspection, Markdown syntax and fragment rules, boundary observations,
|
||
source locations, cause propagation, and the existing per-blob caches.
|
||
Diagnostics remain repository-relative; Markdown links remain relative to
|
||
each source document.
|
||
4. Keep content-reader cleanup attributed to the last check that actually read
|
||
content. Do not reintroduce full raw-content retention or per-check tree
|
||
scans merely to carry module scope.
|
||
5. Give explicit shared-file selection errors a public validation identity
|
||
`shared-files`, with the selected repository-relative path and cause. Add its
|
||
facade conversion and CLI presentation through the existing failure model.
|
||
Shared-file selection itself enables no content check.
|
||
|
||
**Verification:** Place real violations inside and outside selection and show
|
||
that only in-scope inputs fail each selected checker. Cover shared files under
|
||
enabled checks, module-relative exclusions, and a cross-module Markdown heading
|
||
read that leaves unrelated destination documents unchecked. Equal Markdown blobs
|
||
at different paths keep distinct relative-link findings. Confirm selection
|
||
errors with content checks disabled and preserve cancellation/cleanup regressions.
|
||
Run affected validation and facade tests.
|
||
|
||
**Completed:** Formatting, workspace, local-replacement, file-size, and Markdown
|
||
checks now iterate the selected committed inputs. Targeted reads still resolve
|
||
against the full repository tree. File-size exclusions are translated from the
|
||
selected module to repository-relative paths, and shared-file failures preserve
|
||
their public check, path, and cause. Updated file-size guidance and verified
|
||
in-scope and out-of-scope findings, shared inputs, cross-module anchors, reader
|
||
cleanup, facade conversion, `go test ./internal/validation .`, and
|
||
`go run ./internal/apicheck`.
|
||
|
||
## Stage 6: Execute Go validation in the module with reusable local settings
|
||
|
||
**Outcome:** One selected standard suite runs in the module, and later composite
|
||
builds can reuse its effective Go environment.
|
||
|
||
**Steps:**
|
||
|
||
1. Change validation Go command directories to resolved `ModuleDir`. Preserve
|
||
offline, readonly, workspace-disabled behavior and selected race, full vet,
|
||
target compilation, output, and command ordering.
|
||
2. Introduce a small operation-local lazy Go-tool owner alongside the existing
|
||
`gotool.Tool`. Candidate validation and subsequent build callbacks share it;
|
||
delivery and standalone builds own separate instances. It acquires settings
|
||
only when a Go command needs them.
|
||
3. Reuse the discovered tool while effective environment inputs are unchanged.
|
||
Retain a snapshot of inherited environment and the persistent Go settings
|
||
source used for discovery; after consumer callbacks, compare those inputs
|
||
and rediscover if they changed. Use simple value/file comparisons within
|
||
this owner. Keep preparation's network-capable settings separate. Introduce
|
||
neither a public session object nor a process-global tool cache.
|
||
4. Pass the private owner through internal candidate/build coordination. Preserve
|
||
repository-wide state rechecks and the single shared validation invocation.
|
||
|
||
**Verification:** A failing nested test suite prevents its release while root
|
||
tests pass. An unrelated sibling failing suite does not run. Selected vet and
|
||
compile targets use the same module and existing baseline. Observe one standard
|
||
suite and reused discovery in an unchanged validation/build operation; changing
|
||
environment inputs through an additional callback triggers fresh settings.
|
||
Run Go-tool, validation, and candidate coordination tests.
|
||
|
||
**Completed:** Go validation commands now run from the captured module
|
||
directory. Candidate checks and publication share a lazy operation-local Go
|
||
settings owner with their later build callbacks; standalone and managed delivery
|
||
builds acquire separate owners. The owner reuses discovery while the inherited
|
||
environment and selected `GOENV` file stay unchanged, and refreshes after
|
||
consumer callbacks when either changes. Nested-module fixtures now include
|
||
packages for selected-module validation. Verified one suite and shared discovery
|
||
through candidate build coordination, environment and persistent-file refresh,
|
||
nested-suite failure and sibling isolation, selected vet/compile command
|
||
directories and compile baseline, `go test ./...`, and
|
||
`go run ./internal/apicheck`.
|
||
|
||
## Stage 7: Build the selected module and bind output evidence
|
||
|
||
**Outcome:** Standalone and composite builds use the correct declaration source,
|
||
version injection, path base, and identity.
|
||
|
||
**Steps:**
|
||
|
||
1. Make public standalone `Build` resolve working-tree identity once, then call
|
||
an internal build path that consumes resolved identity. Candidate and managed
|
||
delivery builds call that path with committed identity and their Go-tool
|
||
owner. They do not call standalone resolution as a nested operation.
|
||
2. Consolidate normalized build settings in `internal/artifact`, including main
|
||
package, binary name, and normalized target pairs. Reuse this representation
|
||
for execution, evidence, later receipt binding, and settings comparisons.
|
||
Preserve supported target and artifact-name rules.
|
||
3. Build main packages from `ModuleDir`; resolve relative ordinary output there,
|
||
with default `<module>/dist`. Keep absolute output usable. Candidate and
|
||
managed delivery output, including disposable native smoke output, stays
|
||
outside the entire repository through the existing temporary-directory owner.
|
||
4. Use semantic `Version` for linker injection, filenames, and exact native
|
||
version checks. Populate build identity before later errors and retain partial
|
||
artifacts, produced-byte digests, compiler evidence, and source observations.
|
||
An empty target set performs no compiler or Go-environment discovery.
|
||
5. Preserve standalone modified-source support and existing `SourceVerified`
|
||
observations. A build does not require a note, unused tag, clean `main`, or
|
||
publication. Delivery still requires verified committed tagged source.
|
||
|
||
**Verification:** Build and run a nested native consumer using `buildinfo` and
|
||
cross-compile one supported second target. Filenames and output contain the
|
||
semantic version and retain ordinary filename components. Cover dirty declaration
|
||
changes and a new uncommitted module directory for standalone builds, committed
|
||
identity for composites, empty targets, and partial failures. Confirm temporary output is
|
||
outside the repository even with a nested selection and source-local `TMPDIR`.
|
||
Run build and runtime-boundary tests.
|
||
|
||
**Completed:** Standalone builds now resolve working-tree module identity once;
|
||
candidate and managed delivery builds consume their committed identity and Go
|
||
tool owner through the resolved build path. Normalized build settings are shared
|
||
by execution and delivery evidence. Builds run from `ModuleDir`, use module-based
|
||
output paths, and keep temporary native smoke output outside the repository.
|
||
Verified nested native version output, a second cross target, dirty declarations,
|
||
new uncommitted nested modules, empty targets, temporary-output fallback and
|
||
cleanup, full `go test ./...`, `go run ./internal/apicheck`, and `git diff --check`.
|
||
|
||
## Stage 8: Deliver module-local notes and assets to full-tag pages
|
||
|
||
**Outcome:** Page-only, managed-build, and API prebuilt delivery share committed
|
||
module identity and existing recovery behavior.
|
||
|
||
**Steps:**
|
||
|
||
1. Capture checkout commit and resolve committed identity once in delivery;
|
||
inspect the derived literal tag against that checkout. Compare optional
|
||
`ExpectedTag`. Keep historical lightweight tags, detached checkouts, clean
|
||
source, and the existing post-build source recheck.
|
||
2. Read and validate the module-local committed note. Populate delivery evidence
|
||
as resolution progresses, including build and hosted failures. Use Stage 7's
|
||
resolved build path for managed builds.
|
||
3. Bind prebuilt results to repository, module directory, declared module path,
|
||
version, tag, commit, and existing artifact/settings evidence. Require
|
||
`SourceVerified` for binary delivery and preserve caller ownership of files.
|
||
4. Give Gitea requests a distinct full-tag field; stop applying stable-version
|
||
syntax to that field. Use the literal tag for hosted tag lookup, release
|
||
lookup/create, and conflicts. Titles remain from the version-bearing note.
|
||
Encode tags at HTTP/source-route owners and verify escaped slash behavior.
|
||
5. Preserve one local digest-and-size verification before hosted requests,
|
||
deterministic manifests, remote exact comparisons, additive retries, and
|
||
existing failure states. Page-only delivery performs no application build,
|
||
Go environment discovery, application download, receipt, or manifest work.
|
||
|
||
**Verification:** Local HTTP fixtures receive correctly encoded nested tags and
|
||
their semantic-version note titles. Two module tags at one commit create
|
||
separate pages. Cover nested managed builds and reject an API prebuilt result
|
||
for another module before hosted requests. Reuse established tampering, conflict,
|
||
partial/unknown, historical-tag, and cleanup tests. Run delivery/Gitea tests.
|
||
|
||
**Completed:** Delivery now captures checkout identity once, derives the
|
||
selected module's committed tag and note path, compares an optional expected
|
||
full tag, and returns module evidence through local and hosted failures. Managed
|
||
and prebuilt binary results bind to the same repository, module, version, tag,
|
||
commit, settings, and source verification; the CLI carries that identity through
|
||
its existing private receipt. Gitea uses the literal full tag and escapes
|
||
slashes in hosted routes. Verified two module pages at one commit, nested notes
|
||
and slash tags, mismatched expected tags and prebuilt identities, preserved
|
||
delivery recovery cases, `go test ./...`, `go run ./internal/apicheck`, and
|
||
`git diff --check`.
|
||
|
||
## Stage 9: Replace receipts with the module-bound schema
|
||
|
||
**Outcome:** CLI prebuilt delivery uses one current receipt format and one
|
||
delivery-owned content verification pass.
|
||
|
||
**Steps:**
|
||
|
||
1. Implement the schema specified above in CLI receipt read/write and public
|
||
build conversions. Keep the existing private filename and write/partial-build
|
||
lifecycle. Require all identity fields, even for a root build.
|
||
2. Validate the schema version, field structure, source identity relationships,
|
||
normalized settings, artifact declarations, and receipt/output location.
|
||
Check internal tag/module/version consistency through shared pure derivation
|
||
using recorded identity, without resolving a new operation's source.
|
||
3. Reject old/unversioned or unknown-schema receipts with guidance to regenerate
|
||
through the updated build command. Do not migrate or accept their old shape.
|
||
4. Remove artifact content hashing from receipt loading and adjacent prebuilt
|
||
declaration/comparison work. Loading validates declared paths and digests;
|
||
Stage 8 delivery verifies actual bytes and sizes before any hosted request.
|
||
Build-time hashing records produced bytes independently.
|
||
5. Reuse normalized settings in profile-to-receipt comparisons. Keep structural
|
||
validation distinct from source verification and actual asset verification.
|
||
|
||
**Verification:** Round-trip a root and nested receipt. Reject old schema,
|
||
malformed identity, and mismatched module or settings. A loaded receipt with
|
||
changed or missing artifact bytes fails during delivery before hosted requests.
|
||
Protect verification ownership through an observable read boundary rather than
|
||
tests tied to private helper choreography. Run receipt and CLI prebuilt tests.
|
||
|
||
**Completed:** Replaced the unversioned receipt with schema version 1 and pure
|
||
recorded-identity checks for module path, physical directory, version, and full
|
||
tag. Receipt loading now validates structure and normalized settings without
|
||
opening artifacts; delivery remains responsible for verifying artifact bytes
|
||
and sizes before hosted requests. Old and unknown schemas require regeneration.
|
||
Verified root and nested receipts, malformed identity and settings, changed and
|
||
missing artifacts, `go test ./...`, `go run ./internal/apicheck`, and
|
||
`git diff --check`.
|
||
|
||
## Stage 10: Map profiles and product commands to module operations
|
||
|
||
**Outcome:** Local check, publication, and build commands express the new API with
|
||
small profiles and module context.
|
||
|
||
**Steps:**
|
||
|
||
1. Add strict profile decoding and mapping for `module_dir` and
|
||
`validation.shared_files`. Omission/null versus explicit empty selections
|
||
survive loading, copying, serialization, and facade mapping. Do not use
|
||
`omitempty` serialization that collapses explicit empty shared-file lists.
|
||
2. Keep profile lookup relative to the repository, including `--config`, and
|
||
pass module-relative owned paths to the common normalization boundary. Avoid
|
||
resolving identity a second time in CLI mapping.
|
||
3. Run configured product commands with `Candidate.ModuleDir` as CWD. Keep
|
||
existing version/commit context and add `RELEASEKIT_REPOSITORY_DIR`,
|
||
`RELEASEKIT_MODULE_DIR`, `RELEASEKIT_MODULE_PATH`, and `RELEASEKIT_TAG`.
|
||
Supply captured context without modifying the parent process environment.
|
||
4. Update check/publication/build diagnostics and recovery text to identify module,
|
||
semantic version, and full tag as available. Preserve existing native exit
|
||
classifications and presentation of underlying validation failures.
|
||
Implement `publish --expected-module` as a profile-selection comparison using
|
||
the shared path normalization, without another declaration resolution.
|
||
5. Apply the dependency preparation request cutover and its facade conversions.
|
||
Preparation resolves selection without release-note or publication prerequisites.
|
||
|
||
**Verification:** CLI profiles select root/nested API requests; explicit empty
|
||
lists survive round trips. Product commands observe module CWD and both contexts.
|
||
Relative profile lookup remains repository-based and build output module-based.
|
||
Missing explicit files fail without enabling checks. Verify nested failure
|
||
diagnostics and retain a minimal root profile with no new required fields.
|
||
Run CLI profile, command, preparation, and compiled API checks.
|
||
|
||
**Completed:** Profiles now map `module_dir` and `validation.shared_files`,
|
||
preserving nil versus explicit empty selections through JSON and facade copies.
|
||
Candidate commands run from the selected module with repository, module, path,
|
||
version, commit, and full-tag context. `publish --expected-module` compares
|
||
normalized profile selection before callbacks, and delivery/build map the profile
|
||
module through their facades. Dependency preparation now accepts a
|
||
repository/module request and runs in that module. Verified nested CLI checks and
|
||
build selection, explicit missing shared files, mismatch rejection before
|
||
product commands, module-targeted preparation, `go test ./...`,
|
||
`go run ./internal/apicheck`, and documentation whitespace checks.
|
||
|
||
## Stage 11: Implement full-tag CI delivery and prepared-checkout verification
|
||
|
||
**Outcome:** The CLI can prepare a complete event tag or reuse a verified
|
||
bootstrap checkout, then deliver the selected module.
|
||
|
||
**Steps:**
|
||
|
||
1. Update `ci-deliver` parsing to accept full `TAG`, required expected commit,
|
||
and optional `--prepared`. Validate literal ref syntax and stable terminal
|
||
version separately; ordinary commands still accept semantic versions.
|
||
2. Implement `TaggedCheckoutRequest.Tag` and `VerifyOnly` in preparation. Share
|
||
read-only verification between both modes; default preparation fetches the
|
||
exact tag and detaches its expected commit, then verifies. Verify-only checks
|
||
matching tag, HEAD, clean repository, and expected commit without fetch or
|
||
checkout. Preserve default remote selection and existing failure causes.
|
||
3. Load the profile from the prepared source using repository-based path rules.
|
||
Compare optional `--expected-module` with its normalized selection before
|
||
application dependency preparation; mismatch is a profile-selection error.
|
||
Pass its module selection, parsed semantic version, and event `ExpectedTag`
|
||
into delivery. Delivery owns committed identity resolution and exact mapping
|
||
verification. Keep ordinary `deliver` free of checkout/download preparation.
|
||
4. Prepare application dependencies in the selected module only for binary
|
||
profiles. Continue reading delivery credentials only for delivery; preparation
|
||
and configuration failures retain their existing CLI classifications.
|
||
5. Update prebuilt CLI delivery to compare the new receipt and normalized profile
|
||
settings, then delegate actual source/asset validation to the facade.
|
||
|
||
**Verification:** Standalone CI delivery prepares a nested event tag. Prepared
|
||
mode succeeds offline without repeat fetch or checkout and rejects wrong commit,
|
||
dirty source, missing/moved tag, and inconsistent profile module. Verify detached
|
||
historical delivery, module-targeted dependencies for binaries, and page-only
|
||
application-preparation omission. Run preparation and CLI delivery tests.
|
||
|
||
**Completed:** CI delivery now accepts literal full tags and derives the stable
|
||
semantic version from the final path component. Tagged checkout preparation
|
||
shares read-only tag, HEAD, expected-commit, and cleanliness verification with
|
||
the offline `--prepared` mode. CI profiles load from that checkout, optional
|
||
`--expected-module` is compared before application dependency preparation, and
|
||
the event tag is passed through delivery for exact identity checks. Binary
|
||
dependency preparation remains module-targeted and page-only profiles skip it.
|
||
Verified nested tag preparation, prepared CLI delivery without a remote, wrong
|
||
commit and dirty/missing/moved tag rejection, profile parsing, full `go test
|
||
./...`, `go run ./internal/apicheck`, and `git diff --check`.
|
||
|
||
## Stage 12: Preserve the small root-module workflow
|
||
|
||
**Outcome:** Existing root-only consumers retain a small launcher, profile, and
|
||
module-selected CI bootstrap under the current interface.
|
||
|
||
**Steps:**
|
||
|
||
1. Update `examples/woodpecker/tag-delivery.yml` and the self-bootstrap source
|
||
example to use prepared CI delivery after exact event checkout verification.
|
||
Keep root module files and default root profile selection.
|
||
2. Retain temporary alternate module metadata outside the repository, tagged-source
|
||
tool selection, workspace/toolchain controls, compatible Go image selection,
|
||
and credential handling. Tool bootstrap's network access remains separate
|
||
from offline application compilation.
|
||
3. Keep ReleaseKit's own `scripts/release`, root profile, and maintainer procedure
|
||
consistent where affected. Root consumers need no module map, extra required
|
||
configuration, or additional routine decisions.
|
||
4. Update root workflow tests for the receipt/API cutover and prepared mode.
|
||
Reuse existing synthetic tool modules and isolated module cache helpers.
|
||
|
||
**Verification:** Execute the root launcher/API/profile representative flows and
|
||
both maintained root workflow harnesses. Bootstrap and retry select tooling from
|
||
the original tagged module after the default branch changes. Confirm subsequent
|
||
delivery performs read-only checks without repeating checkout preparation.
|
||
Use local remotes/proxies and temporary `GOMODCACHE`, never live CI or credentials.
|
||
|
||
**Completed:** Both maintained root Woodpecker templates now verify and detach
|
||
the exact event tag before invoking `ci-deliver --prepared`, so delivery repeats
|
||
the source checks without fetching or changing checkout state. Updated workflow
|
||
tests assert the prepared flag while retaining local proxy coverage for the
|
||
tagged ReleaseKit selection, self-bootstrap, retry behavior, and source-only and
|
||
binary delivery. Verified the root profile and compiled API boundaries, the
|
||
maintained workflow harnesses, `go test ./...`, and `git diff --check`.
|
||
|
||
## Stage 13: Add a shared multi-module declaration and local launcher example
|
||
|
||
**Outcome:** Consumers select modules through small declarations with one maintained
|
||
helper shared by local release commands and CI.
|
||
|
||
**Steps:**
|
||
|
||
1. Add `examples/multi-module/` with root and integration profiles, the specified
|
||
TSV mapping, and POSIX shell selection helper. Include a physical-major
|
||
directory selection to demonstrate the distinction between module and tag.
|
||
2. Have the helper select by local selector or full event tag and return the
|
||
physical directory/profile. Share parsing and selection rather than copying
|
||
cases into separate launcher and workflow code. Keep unknown selectors and
|
||
ambiguous events as configuration errors.
|
||
3. Add a small shared launcher that chooses the row, bootstraps the tool from the
|
||
selected module's local configuration using temporary alternate module files,
|
||
and invokes publication with repository context, `--config`,
|
||
`--expected-module`, and semantic version. Module selection comes from the
|
||
profile; the expectation verifies table/profile agreement. The library
|
||
validates derived release identity.
|
||
4. Supply dependency preparation guidance before offline checks and illustrative
|
||
sequential core/integration publication when a sibling version must exist.
|
||
The launcher does not infer dependency order or repeat the standard suite.
|
||
|
||
**Verification:** Run selection and launcher harnesses with local temporary
|
||
repositories and isolated dependency proxies. Cover root/nested selection,
|
||
physical-major mapping, unknown/ambiguous selection, quoted paths, and profile
|
||
agreement. Verify the selected module supplies the tool dependency and publication
|
||
receives its semantic version/profile. Shell patterns never become arbitrary
|
||
library tag templates. Update example guidance with the implemented format.
|
||
|
||
**Completed:** Added the multi-module profiles, four-column tab-separated
|
||
mapping, shared POSIX selector, and launcher. The mapping covers root and
|
||
nested integration tags plus a physical `/v2` module layout. The launcher
|
||
copies selected module metadata outside the checkout, builds the ReleaseKit
|
||
version selected by that module, and passes the mapped profile and
|
||
`--expected-module` to publication. Guidance covers per-module dependency
|
||
preparation and sequential releases where a sibling dependency is required.
|
||
Verified selector and ambiguity behavior, unknown selections, paths with
|
||
spaces, the physical-major row, and an isolated local-proxy launcher run that
|
||
proved the selected module's tool version and publication arguments. Also
|
||
passed `sh -n`, `go test ./...`, `go run ./internal/apicheck`, and
|
||
`git diff --check`.
|
||
|
||
## Stage 14: Bootstrap and deliver mapped module tags in Woodpecker
|
||
|
||
**Outcome:** A maintained multi-module workflow uses the same declaration and
|
||
tagged module selection for local publication, delivery, and retries.
|
||
|
||
**Steps:**
|
||
|
||
1. Add a multi-module Woodpecker example under `examples/multi-module/`, with tag
|
||
filters for its root and nested selections. Keep the simpler root template.
|
||
2. Verify the literal event tag and peeled event commit, detach and check clean
|
||
source, then load the mapping/helper from that verified checkout. Select the
|
||
physical module and profile before tool bootstrap. Do not derive physical
|
||
module location merely by removing the final tag component.
|
||
3. Copy the selected module's `go.mod`/`go.sum` to temporary bootstrap metadata
|
||
outside the whole checkout; run bootstrap from that module. Build the
|
||
ReleaseKit dependency chosen there, then invoke `ci-deliver --prepared` with
|
||
repository, selected profile, expected physical module, expected commit, and
|
||
full event tag.
|
||
4. Exercise page-only and binary declarations through the same workflow. Keep
|
||
source verification, dependency preparation, and credential handling with
|
||
their established owners. A retry reads the triggering release's mapping,
|
||
profile, module files, and selected tool rather than the default branch.
|
||
|
||
**Verification:** Workflow harnesses cover nested source-only and binary delivery,
|
||
root fallback in the separate simple template, and both physical layouts of a
|
||
major-version module. Change default-branch mapping and tool versions after a
|
||
tag and verify retry still uses its original selection. Fail inconsistent mapping
|
||
through the library's exact tag check. Isolate every synthetic dependency consumer
|
||
with test-owned `GOMODCACHE`; use local HTTP fixtures and local Git remotes.
|
||
|
||
**Completed:** Added the multi-module Woodpecker workflow with full root and
|
||
nested tag filters, exact peeled-tag commit verification, and selection from the
|
||
verified checkout. Bootstrap uses the mapped physical module's committed Go
|
||
metadata and profile, then invokes prepared CI delivery with the expected module,
|
||
commit, and full tag. Added the binary core v2 example and documented committed
|
||
module checksums for clean binary delivery. Workflow harnesses cover nested
|
||
page-only and binary delivery, physical and virtual `/v2` module layouts, a retry
|
||
after default-branch mapping and tool changes, and an inconsistent mapping
|
||
rejected by exact event-tag validation. Tests use local Git remotes, local HTTP
|
||
delivery fixtures, a local module proxy, and test-owned `GOMODCACHE`. Verified
|
||
`go test ./...`, `go run ./internal/apicheck`, shell syntax, and `git diff --check`.
|
||
|
||
## Stage 15: Complete consumer documentation and verify the whole cutover
|
||
|
||
**Outcome:** The supported interface, examples, migration guidance, and behavioral
|
||
evidence describe the complete implemented feature.
|
||
|
||
**Steps:**
|
||
|
||
1. Update GoDoc, [CLI](../cli.md), [configuration](../config.md),
|
||
[consumer guides](../consumers/), [Gitea integration](../integrations/gitea.md),
|
||
internal owner documents, examples, and recovery guidance at their canonical
|
||
owners. Explain module selection, semantic version versus full tag, declared
|
||
versus physical module identity, note/source/license links, path bases,
|
||
boundaries, shared-file semantics, preparation, and receipt ownership.
|
||
2. Write the next release's pending notes and migration guidance.
|
||
A release version is not selected by this feature;
|
||
keep the draft under roadmap ownership until release preparation assigns its
|
||
`docs/releases/<version>.md` destination. Include API/profile updates, the
|
||
preparation signature and tag field changes, receipt regeneration, CI mapping
|
||
and prepared mode, and root-module exclusion of nested fixture module files.
|
||
Keep immutable historical release notes intact.
|
||
3. Verify ReleaseKit's own root-module release procedure still satisfies its
|
||
adopted policy. Consumer guidance explains module-local note adaptation;
|
||
this feature does not change the source-push completion point or introduce
|
||
live-service adoption/release gates. Correct stale baseline facts in owner
|
||
documents touched by the feature, including the declared Go minimum.
|
||
4. Audit the acceptance coverage below. Add only missing representative assembled
|
||
flows or owner-level cases with a concrete remaining risk. Reuse existing
|
||
recovery, cancellation, HTTP conflict, and facade-boundary tests; avoid a
|
||
Cartesian product of every module layout, check, target, and failure.
|
||
5. Run the normal offline `go test ./...` suite and
|
||
`go run ./internal/apicheck` from the repository root. Validate affected local
|
||
documentation links/fragments and examples, and run `git diff --check`.
|
||
Record the final results and feature completion in the roadmap/plan, then
|
||
commit the completed unit. Completion is the implementation and local
|
||
evidence; tagging, deployment, live CI, and consumer rollout remain later work.
|
||
|
||
**Verification:** Match every acceptance row to concrete test or review evidence,
|
||
record the full-suite and compiled API check results, and confirm that the
|
||
implemented examples and canonical documentation agree with the current API.
|
||
|
||
**Completed:** Updated facade GoDoc, consumer and CLI/configuration guidance,
|
||
Gitea and internal ownership documents, recovery guidance, and maintained
|
||
examples for module selection, full tags, path bases, shared files, preparation,
|
||
and receipt ownership. Added pending next-release notes with migration guidance
|
||
and corrected the current Go minimum facts. Release preparation promoted those
|
||
notes to [the v0.4.0 release note](../releases/v0.4.0.md).
|
||
Audited acceptance coverage against
|
||
the stage-owned tests and reviewed ReleaseKit's root release procedure against
|
||
the adopted release policy; no new release gates were added. Verified
|
||
`go test ./...`, `go run ./internal/apicheck`, local links and heading fragments
|
||
in 13 changed Markdown documents, multi-module shell/config/module syntax, and
|
||
`git diff --check`.
|
||
|
||
### Acceptance coverage audit
|
||
|
||
| Requirement | Primary stage/evidence owner |
|
||
| --- | --- |
|
||
| One resolution per operation, committed versus working-tree declarations, major layouts, version/tag distinction | Stages 1, 2, 7; identity tests and composite coordination. |
|
||
| Root defaults, own-module declaration, no root declaration required for nested selection | Stages 1, 2, 12; resolver and representative root/nested workflows. |
|
||
| Module-local notes; equal versions at one commit; only intended tag; full-tag reuse and recovery | Stages 2, 3; candidate/publication local Git fixtures. |
|
||
| Repository-wide guards and progressive failure evidence | Stages 2, 3, 8; facade conversion and operation failure tests. |
|
||
| One selected Go suite, vet/compile/module CWD, offline environment reuse | Stage 6; validation and Go-tool tests. |
|
||
| Nested boundaries, fixtures, vendor, shared defaults/replacement/empty, committed presence, missing explicit files without checks | Stages 4, 5, 10; scope, checker, and profile round-trip tests. |
|
||
| Path deduplication, equal-blob contextual findings, cross-module lookup without expanded inputs, module-relative exclusions | Stages 4, 5; selected-view and reference tests. |
|
||
| Existing selective caches, reader cancellation and cleanup attribution | Stages 4, 5; retained regression coverage. |
|
||
| Semantic binary versions, module output paths, temporary output outside the whole repository, empty/partial builds | Stage 7; real native consumer and cross-compilation evidence. |
|
||
| New-only receipt schema and module/settings/source binding; one local delivery content pass | Stages 8, 9, 11; receipt/prebuilt/tamper fixtures. |
|
||
| Distinct hosted pages, slash tags and source links, page-only and binary delivery, unchanged partial/retry outcomes | Stage 8; HTTP fixtures and retained recovery tests. |
|
||
| Repository/profile versus module path bases; both product-command contexts; simple root configuration | Stages 10, 12; command/profile/root workflow tests. |
|
||
| Exact event checkout, prepared read-only reuse, physical-major mapping, tagged-source retries and tool selection | Stages 11–14; preparation and workflow harnesses. |
|
||
| Facade ownership, independent `buildinfo`, documentation and migration guidance, complete offline suite | Stages 2, 7, 10, 15; compiled API checks and final review. |
|
||
|
||
No required product decisions remain open. The interface choices above provide
|
||
the starting specification; local implementation details may evolve within the
|
||
feature roadmap and repository policies.
|