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

20 KiB

Roadmap: release individual Go modules

Status: planned feature; scope decisions resolved.

Purpose

Implement ADR 0005 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. 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 committed module declaration once at the operation entry point. 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.

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. Retain repository access 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, 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. 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.

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.

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. 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, 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.

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.

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. 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.

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.