From 8552f92ddb67e1dc1781d821888fc04e57fbc06f Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sat, 10 Oct 2026 14:27:47 +0000 Subject: [PATCH] Separate example setup from synthetic test releases --- cmd/releasekit/example_source_test.go | 54 +++++++++++++++++ .../multi_module_woodpecker_test.go | 7 +++ docs/consumers/adoption.md | 18 +++++- docs/consumers/delivery.md | 5 ++ docs/roadmap/next-release-notes.md | 15 +++++ docs/roadmap/remediation.md | 11 +++- examples/multi-module/README.md | 59 +++++++++++++++++++ examples/multi-module/go.mod | 2 - .../multi-module/integrations/ckconfig/go.mod | 2 - .../multi-module/integrations/core/v2/go.mod | 2 - 10 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 cmd/releasekit/example_source_test.go diff --git a/cmd/releasekit/example_source_test.go b/cmd/releasekit/example_source_test.go new file mode 100644 index 0000000..dec9dd1 --- /dev/null +++ b/cmd/releasekit/example_source_test.go @@ -0,0 +1,54 @@ +package main + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestMultiModuleExampleSetupUsesCurrentSourceTool(t *testing.T) { + root := cliRepositoryRoot(t) + repository := newCLIRepository(t) + copyExampleTree(t, filepath.Join(root, "examples", "multi-module"), repository) + const module = "gitea.maximumdirect.net/eric/ReleaseKit" + environment := append(os.Environ(), "GOTOOLCHAIN=local", "GOWORK=off", "GOPROXY=off", "GOSUMDB=off", "GOFLAGS=") + goCommand := func(directory string, args ...string) string { + t.Helper() + command := exec.Command("go", args...) + command.Dir, command.Env = directory, environment + output, err := command.CombinedOutput() + if err != nil { + t.Fatalf("go %q: %v\n%s", args, err, output) + } + return string(output) + } + selections := []struct { + directory, profile, product, version string + }{ + {".", "releasekit-root.json", "Example Consumer", "v1.2.3"}, + {"integrations/ckconfig", "releasekit-ckconfig.json", "Example Consumer Integration", "v1.2.3"}, + {"integrations/core/v2", "releasekit-core-v2.json", "Example Consumer Core v2", "v2.1.0"}, + } + for _, selected := range selections { + directory := filepath.Join(repository, filepath.FromSlash(selected.directory)) + if selected.directory != "integrations/core/v2" { + writeRepositoryFile(t, repository, selected.directory+"/source.go", "package candidate\n") + } + goCommand(directory, "mod", "edit", "-require="+module+"@v0.0.0", "-replace="+module+"="+root, "-tool="+module+"/cmd/releasekit") + goCommand(directory, "mod", "tidy") + writeRepositoryFile(t, repository, selected.directory+"/docs/releases/"+selected.version+".md", "# "+selected.product+" "+selected.version+"\n\nSource verification fixture.\n") + } + cliGitRun(t, repository, "add", ".") + cliGitRun(t, repository, "commit", "-m", "Select current source in disposable example") + for _, selected := range selections { + output := goCommand(filepath.Join(repository, filepath.FromSlash(selected.directory)), "tool", "releasekit", "check", "--config", selected.profile, selected.version) + if !strings.Contains(output, "is valid") { + t.Fatalf("source-selected check output=%s", output) + } + } + if status := cliGitOutput(t, repository, "status", "--porcelain"); status != "" { + t.Fatalf("source verification changed committed fixture: %s", status) + } +} diff --git a/cmd/releasekit/multi_module_woodpecker_test.go b/cmd/releasekit/multi_module_woodpecker_test.go index 8230801..43ff222 100644 --- a/cmd/releasekit/multi_module_woodpecker_test.go +++ b/cmd/releasekit/multi_module_woodpecker_test.go @@ -316,6 +316,13 @@ func newWorkflowRepository(t *testing.T, example string) string { t.Fatal(err) } copyExampleTree(t, example, repository) + for _, selection := range []struct{ directory, module string }{ + {".", "example.com/multi-module-consumer"}, + {"integrations/ckconfig", "example.com/multi-module-consumer/integrations/ckconfig"}, + {"integrations/core/v2", "example.com/multi-module-consumer/integrations/core/v2"}, + } { + writeRepositoryFile(t, repository, selection.directory+"/go.mod", workflowModuleFile(selection.module, "v0.2.197")) + } cliGitRun(t, repository, "init", "--initial-branch=main") cliGitRun(t, repository, "config", "user.name", "ReleaseKit Test") cliGitRun(t, repository, "config", "user.email", "releasekit@example.test") diff --git a/docs/consumers/adoption.md b/docs/consumers/adoption.md index 896f4df..c72a7a2 100644 --- a/docs/consumers/adoption.md +++ b/docs/consumers/adoption.md @@ -9,12 +9,16 @@ maintainers without adding imports to application or library code. From the consumer module's root, select the ReleaseKit version to adopt: ```sh -go get -tool gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@v0.2.0 +: "${RELEASEKIT_TOOL_VERSION:?Set the released ReleaseKit version to adopt}" +go get -tool "gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@${RELEASEKIT_TOOL_VERSION}" go mod tidy go tool releasekit version ``` -Replace `v0.2.0` with the intended release. Go records the executable package in +Select a release that implements the API, profile, and workflow you are adopting. +The module-release interface requires a release containing that feature; +pre-feature versions through v0.3.0 use their corresponding historical interface. +Go records the executable package in a `tool` directive and the module version in a `require` directive. The tool declaration retains the dependency through `go mod tidy`. Commit the resulting `go.mod` and `go.sum` changes. To upgrade, repeat the command with the chosen @@ -38,7 +42,8 @@ Consumers importing the root `releasekit` API or required imports and select the module version: ```sh -go get gitea.maximumdirect.net/eric/ReleaseKit@v0.2.0 +: "${RELEASEKIT_TOOL_VERSION:?Set the released ReleaseKit version to adopt}" +go get "gitea.maximumdirect.net/eric/ReleaseKit@${RELEASEKIT_TOOL_VERSION}" go mod tidy ``` @@ -52,3 +57,10 @@ supports both tool and ordinary package dependencies. It builds ReleaseKit from the tagged consumer's module selection, so the committed module files also govern CI delivery and retries. Registering the CLI as a tool is sufficient for consumers that use ReleaseKit only for maintainer commands. + +Upgrade the module-selected tool and workflow together. For a tag created before +that upgrade, retry with the workflow matching the tool recorded at that tag. +Keep its immutable module files and tag intact. See the +[delivery guide](delivery.md#woodpecker-tag-workflows) for tagged-source bootstrap +and the [example's source verification](../../examples/multi-module/README.md#local-source-verification) +for testing an unreleased checkout in a disposable consumer repository. diff --git a/docs/consumers/delivery.md b/docs/consumers/delivery.md index 5fc71e0..0967cf2 100644 --- a/docs/consumers/delivery.md +++ b/docs/consumers/delivery.md @@ -185,6 +185,11 @@ a different toolchain. The tag's `go.mod` must select ReleaseKit as a module dependency. For CLI-only consumers, use the versioned Go tool dependency described in the [adoption guide](adoption.md). Ordinary package dependencies also work with this template. + +Select a tool release implementing this workflow's interface before adopting it; +the [adoption guide](adoption.md#ci-adoption) owns coordinated tool/workflow +upgrades. Retries of older tags use the workflow matching their recorded tool. + Configure the Woodpecker repository secret `releasekit_gitea_token`; the template maps it to `RELEASEKIT_GITEA_TOKEN` for delivery. The repository needs a `delivery` profile. Page-only and binary behavior both use this template; the diff --git a/docs/roadmap/next-release-notes.md b/docs/roadmap/next-release-notes.md index 98166a3..cd3e618 100644 --- a/docs/roadmap/next-release-notes.md +++ b/docs/roadmap/next-release-notes.md @@ -64,6 +64,13 @@ Update callers, profiles, receipts, and CI declarations together for this clean interface cutover; compatibility shims and legacy receipt readers are not provided. +Select this feature's ReleaseKit release in each consumer module before adopting +the new profiles and workflows. Upgrade its tool declaration and workflow +together: pre-feature tools through v0.3.0 require their historical profiles and +flags. Historical tag retries use the workflow matching the tool recorded at +that tag. Preserve the immutable +tag and module files when retrying. + - Set `ModuleDir` in Go requests or `module_dir` in profiles for a nested module. The selected directory must contain its own `go.mod`; module paths and release tags are read from that declaration, not inferred from a folder @@ -102,3 +109,11 @@ For current behavior, see the [API and consumer guides](../consumers/), [CLI contract](../cli.md), [configuration reference](../config.md), [Gitea integration](../integrations/gitea.md), and [ReleaseKit release procedure](../release.md). + +## Release preparation + +Once this release's version is selected, record it in the maintained multi-module +example's tool and require directives, and move these notes to its release-note +path. Example setup currently selects a compatible version explicitly; disposable +fixtures can verify current source before publication. Synthetic dependency +versions belong to generated test metadata and local fixture proxies. diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index db0c001..4fba306 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -293,7 +293,16 @@ coverage. ## Issue 9: Separate synthetic bootstrap versions from maintained examples (P2) -Status: open. Category: broken adoption example and incomplete cutover guidance. +Status: complete. Category: broken adoption example and incomplete cutover guidance. + +**Completion:** Maintained module skeletons no longer pin synthetic releases. +Example setup explicitly selects compatible released tooling and documents source +verification in disposable checkouts. Workflow fixtures generate their synthetic +declarations. Current-source tool registration and all three example profiles, +including native version injection, pass offline; mapped launchers/workflows and +tagged-tool retries retain isolated fixtures. Adoption and migration guidance +cover coordinated tool/workflow upgrades and historical retries. The pending +release notes record concrete example pins as normal version-selection work. **Evidence:** All three maintained [multi-module declarations](../../examples/multi-module/) require ReleaseKit diff --git a/examples/multi-module/README.md b/examples/multi-module/README.md index 7faa747..f298ec6 100644 --- a/examples/multi-module/README.md +++ b/examples/multi-module/README.md @@ -12,6 +12,28 @@ the launcher from that checkout. The sample modules declare Go 1.26.0, matching the current ReleaseKit consumer minimum. +## Select the release tool + +The module files are setup skeletons. Set `RELEASEKIT_TOOL_VERSION` to a released +ReleaseKit version containing module-release support, then register that tool in +each consumer module before running the launcher or workflow: + +```sh +: "${RELEASEKIT_TOOL_VERSION:?Select a ReleaseKit release with module-release support}" +for module_dir in . integrations/ckconfig integrations/core/v2; do + go -C "$module_dir" get -tool "gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@${RELEASEKIT_TOOL_VERSION}" + go -C "$module_dir" mod tidy +done +``` + +Review and commit the generated tool, dependency, and checksum declarations. +Release preparation records the chosen compatible version in these maintained +module files. The feature roadmap leaves that release version to release +preparation. The [adoption guide](../../docs/consumers/adoption.md) owns tool +registration and coordinated tool/workflow upgrades. + +## Module mapping and publication + `release-modules.tsv` is literal tab-separated text with four columns: 1. Local selector. @@ -71,3 +93,40 @@ page-only. Before enabling the workflow, configure each selected profile's `delivery` destination and add the Woodpecker repository secret `releasekit_gitea_token`; the workflow exposes it as `RELEASEKIT_GITEA_TOKEN` only in the delivery step. + +Retries use the selected tag's module files and matching workflow. Tags whose +tool predates this interface use their corresponding historical workflow. + +## Local source verification + +For verification before a compatible release is published, copy this example +into a disposable consumer checkout. Set `RELEASEKIT_SOURCE` to the absolute +path of a prepared ReleaseKit checkout, then select its source in those fixture +modules: + +```sh +: "${RELEASEKIT_SOURCE:?Set the absolute ReleaseKit checkout path}" +for module_dir in . integrations/ckconfig integrations/core/v2; do + go -C "$module_dir" mod edit \ + -require=gitea.maximumdirect.net/eric/ReleaseKit@v0.0.0 \ + -replace="gitea.maximumdirect.net/eric/ReleaseKit=$RELEASEKIT_SOURCE" \ + -tool=gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit + GOTOOLCHAIN=local GOWORK=off go -C "$module_dir" mod tidy +done +``` + +Here `v0.0.0` is a fixture placeholder resolved by the local replacement. +Keep these declarations in the disposable checkout. Add representative product +source and each selected product's module-local release note, then commit the +fixture and run the source-selected tool without publishing: + +```sh +go tool releasekit check --config releasekit-root.json v1.2.3 +go -C integrations/ckconfig tool releasekit check --config releasekit-ckconfig.json v1.2.3 +go -C integrations/core/v2 tool releasekit check --config releasekit-core-v2.json v2.1.0 +``` + +These commands use the repository-root profile paths even when invoked from a +child module. The core profile also verifies the native binary and injected +version. Prepared dependencies allow this verification with `GOPROXY=off` and +`GOSUMDB=off`. diff --git a/examples/multi-module/go.mod b/examples/multi-module/go.mod index 9e38f83..2b0b7ab 100644 --- a/examples/multi-module/go.mod +++ b/examples/multi-module/go.mod @@ -1,5 +1,3 @@ module example.com/multi-module-consumer go 1.26.0 - -require gitea.maximumdirect.net/eric/ReleaseKit v0.2.197 diff --git a/examples/multi-module/integrations/ckconfig/go.mod b/examples/multi-module/integrations/ckconfig/go.mod index f7ca7a6..dcbdb22 100644 --- a/examples/multi-module/integrations/ckconfig/go.mod +++ b/examples/multi-module/integrations/ckconfig/go.mod @@ -1,5 +1,3 @@ module example.com/multi-module-consumer/integrations/ckconfig go 1.26.0 - -require gitea.maximumdirect.net/eric/ReleaseKit v0.2.197 diff --git a/examples/multi-module/integrations/core/v2/go.mod b/examples/multi-module/integrations/core/v2/go.mod index f1bf440..ec85367 100644 --- a/examples/multi-module/integrations/core/v2/go.mod +++ b/examples/multi-module/integrations/core/v2/go.mod @@ -1,5 +1,3 @@ module example.com/multi-module-consumer/integrations/core/v2 go 1.26.0 - -require gitea.maximumdirect.net/eric/ReleaseKit v0.2.197