diff --git a/README.md b/README.md index 7903ceb..6893900 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Stave is created by [Ben Ranford](https://github.com/ben-ranford) and is ## Installation -Requires **Go 1.22 or later**. In your Go module, install the release candidate: +Requires **Go 1.22 or later**. In your Go module, install the documented version: @@ -45,13 +45,17 @@ go get github.com/ben-ranford/stave@v1.0.0-rc.2 ### Deliberate limits -Stave has not reached general availability. Promotion requires published, -immutable Lopper proving-client evidence for parity and rollback. The SSH, -Bubble Tea, and Lip Gloss adapters remain internal until they have independent -module versions and tags; begin with the root module. See the +A stable tag requires published, immutable Lopper proving-client evidence for +parity and rollback before GA promotion. The SSH, Bubble Tea, and Lip Gloss +adapters remain internal until they have independent module versions and tags; +begin with the root module. See the [compatibility policy](docs/compatibility.md) and [security contract](docs/security.md) before integrating consequential agent actions. +Development checkouts may contain unreleased changes. Pin a published tag +before deploying consequential agent actions; use the development branch for +evaluation only. + ## Quick start In an empty directory, run `go mod init example.com/hello-stave`, then use the diff --git a/docs/client-adoption.md b/docs/client-adoption.md index ddd019a..597ab37 100644 --- a/docs/client-adoption.md +++ b/docs/client-adoption.md @@ -5,11 +5,11 @@ theme, actions, and effects. Stave provides the semantic UI and runtime contracts that let those choices work consistently across human and automated interfaces. -The current root module release candidate is `v1.0.0-rc.2` . -It is not a GA promise; published Lopper parity and rollback evidence remains -the promotion gate. Begin with the root module only. The nested SSH, Bubble Tea, -and Lip Gloss modules are internal and are not yet supported consumer -dependencies. Pin this candidate after its release tag is published. +This guide tracks root module version `v1.0.0-rc.2` . +Begin with the root module only. The nested SSH, Bubble Tea, and Lip Gloss +modules are internal and are not yet supported consumer dependencies. Pin a +published release tag before deployment. For Lopper promotion, verify the +published parity and rollback evidence described in the [release runbook](releasing.md). For a compiled first semantic tree, use the [root quick start](../README.md#quick-start). diff --git a/docs/releasing.md b/docs/releasing.md index 70fa72d..9488d7a 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -4,7 +4,7 @@ The root Go module is the only published Stave package. The nested SSH, Bubble Tea, and Lip Gloss modules remain internal until they receive independent versions and tags. -## Publish the prepared release candidate +## Publish the prepared release tag Read the candidate from `.release-please-manifest.json`; publish it from the hardened final `main` commit, never from an older release-please pull request @@ -24,6 +24,7 @@ merge commit. 2. On the final `main` commit, wait for a fresh successful required-check run. Record its commit SHA and confirm it contains the intended candidate manifest, changelog, and release workflow. + Before deriving a stable tag, complete the GA promotion gate below. 3. Derive the release tag from the manifest and tag that exact SHA. Do not move or recreate the tag after it is pushed: @@ -41,9 +42,11 @@ merge commit. ``` 4. The tag triggers [the release workflow](../.github/workflows/release.yml). - It validates the tag, runs `make ci` and `make release-contract`, then - publishes a prerelease with `CHANGELOG.md`, `LICENSE`, and the performance - evidence artifact. Wait for that workflow and verify all published assets. + It validates the tag and runs `make ci`. A prerelease tag then runs + `make release-contract` and publishes a prerelease; a stable tag runs + `make release-ga-contract` and publishes a non-prerelease release. Both + release paths attach `CHANGELOG.md`, `LICENSE`, and the performance evidence + artifact. Wait for that workflow and verify all published assets. 5. Change repository visibility only after the scheduler-boundary proof in step 1 passes. Then run a controlled real public-fork pull request and prove it uses the hosted untrusted path before approving general outside @@ -55,11 +58,11 @@ merge commit. go list -m -json "github.com/ben-ranford/stave@${release_tag}" ``` -7. After the tag, prerelease, assets, and public Go resolution are verified, - replace `autorelease: pending` with `autorelease: tagged` on the original - release-please pull request. `skip-github-release: true` delegates - publication to the tag workflow, so release-please does not make that label - transition itself. +7. After the tag, its matching release channel, assets, and public Go + resolution are verified, replace `autorelease: pending` with + `autorelease: tagged` on the original release-please pull request. + `skip-github-release: true` delegates publication to the tag workflow, so + release-please does not make that label transition itself. Release Please updates the annotated candidate versions in `README.md` and `docs/client-adoption.md` for future release pull requests. Keep the diff --git a/release-please-config.json b/release-please-config.json index 50a4644..ea51bc9 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -3,9 +3,6 @@ "packages": { ".": { "release-type": "go", - "versioning": "prerelease", - "prerelease": true, - "prerelease-type": "rc", "include-component-in-tag": false, "extra-files": [ "README.md", diff --git a/requirements/release_please_test.go b/requirements/release_please_test.go index 66f15d2..0d66387 100644 --- a/requirements/release_please_test.go +++ b/requirements/release_please_test.go @@ -10,13 +10,10 @@ import ( "testing" ) -var releaseCandidateVersion = regexp.MustCompile(`^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-rc\.([1-9][0-9]*)$`) +var releaseVersion = regexp.MustCompile(`^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-rc\.([1-9][0-9]*)|\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`) type releasePleasePackageConfig struct { ReleaseType string `json:"release-type"` - Versioning string `json:"versioning"` - Prerelease bool `json:"prerelease"` - PrereleaseType string `json:"prerelease-type"` IncludeComponentInTag bool `json:"include-component-in-tag"` ExtraFiles []string `json:"extra-files"` ExcludePaths []string `json:"exclude-paths"` @@ -29,11 +26,31 @@ type releasePleaseConfig struct { func TestRootReleasePleaseConfiguration(t *testing.T) { root := rootReleasePleasePackage(t) assertRootReleasePleasePackage(t, root) + assertReleasePleaseUsesDefaultVersioning(t) assertReleasePleaseCandidateDocuments(t, root) assertReleasePleaseManifestAndChangelog(t) assertReleasePleaseWorkflow(t) } +func assertReleasePleaseUsesDefaultVersioning(t *testing.T) { + t.Helper() + rawConfig, err := os.ReadFile("../release-please-config.json") + if err != nil { + t.Fatal(err) + } + var config struct { + Packages map[string]map[string]json.RawMessage `json:"packages"` + } + if err := json.Unmarshal(rawConfig, &config); err != nil { + t.Fatal(err) + } + for _, setting := range []string{"versioning", "prerelease", "prerelease-type"} { + if _, exists := config.Packages["."][setting]; exists { + t.Fatalf("root release must use Release Please default versioning; remove %q", setting) + } + } +} + func rootReleasePleasePackage(t *testing.T) releasePleasePackageConfig { t.Helper() @@ -56,8 +73,8 @@ func rootReleasePleasePackage(t *testing.T) releasePleasePackageConfig { func assertRootReleasePleasePackage(t *testing.T, root releasePleasePackageConfig) { t.Helper() - if root.ReleaseType != "go" || root.Versioning != "prerelease" || !root.Prerelease || root.PrereleaseType != "rc" { - t.Fatalf("root release must remain a Go rc prerelease: %+v", root) + if root.ReleaseType != "go" { + t.Fatalf("root release must remain a Go package: %+v", root) } if root.IncludeComponentInTag { t.Fatal("root release tags must not include a component") @@ -96,8 +113,8 @@ func assertReleasePleaseManifestAndChangelog(t *testing.T) { if err := json.Unmarshal(rawManifest, &manifest); err != nil { t.Fatalf("decode %s: %v", manifestPath, err) } - if !releaseCandidateVersion.MatchString(manifest["."]) { - t.Fatalf("root manifest must contain a non-zero rc prerelease version, got %q", manifest["."]) + if !releaseVersion.MatchString(manifest["."]) { + t.Fatalf("root manifest must contain an rc baseline or stable SemVer version, got %q", manifest["."]) } changelogPath := filepath.Join("..", "CHANGELOG.md") changelog, err := os.ReadFile(changelogPath) @@ -124,24 +141,28 @@ func assertReleasePleaseWorkflow(t *testing.T) { } } -func TestReleaseCandidateVersionContract(t *testing.T) { +func TestReleaseVersionContract(t *testing.T) { for version, want := range map[string]bool{ "1.0.0-rc.1": true, "2.14.3-rc.42": true, "0.0.0-rc.1": true, + "1.0.0+build.7": true, + "1.0.0+": false, + "1.0.0+build..7": false, + "1.1.0": true, + "2.14.3": true, "01.0.0-rc.1": false, "1.01.0-rc.1": false, "1.0.01-rc.1": false, "1.0.0-rc.0": false, "1.0.0-rc.01": false, - "1.0.0": false, "1.0.0-beta.1": false, "v1.0.0-rc.1": false, "1.0.0-rc.1+build.7": false, } { t.Run(version, func(t *testing.T) { - if got := releaseCandidateVersion.MatchString(version); got != want { - t.Fatalf("release candidate validation for %q = %t, want %t", version, got, want) + if got := releaseVersion.MatchString(version); got != want { + t.Fatalf("release version validation for %q = %t, want %t", version, got, want) } }) }