Go releases
This documentation targets v0.3.2, published at commit 7279ee0.
Consult the release list
for published tags. The release workflow assigns each release commit and tag
after the authorized merge; verify publication rather than inferring it from
a merge alone.
v0.3.2 stabilizes
constructor-planning diagnostics for duplicate aliased existing registrations
when registration order changes. Duplicate registrations still fail.
v0.3.1, at
50688c6, fixes concurrent resolution so cancellation from failed-result cleanup
alone does not retry ordinary construction failures. Genuine construction
cancellation still permits retry, and cleanup errors remain inspectable. It also
adds a compiled package-loading/constructor-planning recipe to the unpublished
recipes module.
v0.3.0, at 71bb21e, introduced opt-in concepts.ParseDotNetGUID conversion targeting .NET 10.0.12
(#28), with strict UUID
defaults unchanged. It also introduced source-distributed enum and complex-key JSON
contract evidence (#17,
#18), not Go enum or
complex-key implementations, runtime or schema support. The root remains
standard-library-only; ecosystem recipes remain unpublished. Earlier releases
include v0.2.0 at 532d218 and v0.1.0 at e50913e.
Experimental releases remain v0.x until an approved stable launch. New public
capabilities use minor intent; a minor version may contain breaking changes,
with migration notes.
Development and release intent
Section titled “Development and release intent”Feature work uses develop, focused conventional commits and verified pushes.
Collect release-bound changes in a develop → main release PR with the
appropriate intent label. Consumers use published tags for released APIs and may
pin pushed develop commits through Go pseudo-versions for unreleased APIs;
communicate pins and releases in
coordination issue #3.
The initial v0.1.0 bootstrap sequencing is complete, not a rule for every release.
Every PR has exactly one of major, minor, patch or no-release. While
v0.x, use minor for additive public APIs or breaking experimental API changes
and patch for compatible fixes. Describe breaking changes explicitly. Dependabot
PRs use no-release; a dependency change requiring publication needs a deliberate
release-bound maintainer change.
The merged PR body becomes the GitHub Release notes verbatim. Follow the
contribution guide and PR template; keep verification and
review notes in a PR comment. A no-release setup PR neither publishes a version
nor automatically closes its referenced issue.
Publication
Section titled “Publication”A push to main runs Go and Markdown gates before preparing a release. The
release action creates the immutable vX.Y.Z tag and GitHub Release only after
its job succeeds. Dependent jobs read back the release and tag target, fetch the
exact version through proxy.golang.org, verify the module archive and request
its pkg.go.dev page. Documentation rendering is asynchronous.
There is no separate registry upload or Go publishing credential. GitHub tags
are Go module versions; the workflow uses its scoped GITHUB_TOKEN for GitHub
writes. The root module keeps the canonical lowercase module path
github.com/cratis/fundamentals.go. Release source must build without a developer
workspace, sibling checkout, secrets or local replacements. Root releases keep
using the automatic Publish workflow; nested modules use the separate manual
path below.
Wait for Publish to finish before merging another release-bound PR: GitHub concurrency can replace a pending run even when running jobs are not cancelled.
Major versions
Section titled “Major versions”GO_RELEASE_MAJOR_CEILING defaults to 0. Set it to 1 only for an explicitly
approved v1 launch and refresh the PR policy check. A major release must be
reviewed and merged by a human. Values above 1 are rejected: Go v2+ requires a
/vN module path, changed imports and a separately reviewed release design.
Neither release workflow publishes prereleases. The ceiling applies to both
root and nested modules; approving v1 for one module does not approve v1 for
another. Review every major release separately.
Add a nested module
Section titled “Add a nested module”Use a separate module when tools or an approved optional integration need external dependencies. The root stays standard-library-only. Integration modules must first meet the standard-interfaces-and-recipes-first criteria in decision 0001.
.github/go-modules.json is the single allow-list.
Each nested entry has an explicit dir and boolean publish policy.
recipes is unpublished; the following steps apply to publishable modules:
-
Create
tools/go.modorintegrations/<name>/go.modwith module pathgithub.com/cratis/fundamentals.go/<directory>. Use lowercase relative directories; modules cannot contain other modules. -
Require a published stable root version, such as the released
v0.3.2. Pseudo-versions, workspaces andreplacedirectives are not a substitute. If the tool needs new root APIs, release the root first. -
Add the exact directory to
nestedin the same change. For example, the following illustrative configuration includes two publishable modules and the unpublished recipes:{"module": "github.com/cratis/fundamentals.go","nested": [{"dir": "tools", "publish": true},{"dir": "integrations/example", "publish": true},{"dir": "recipes", "publish": false}]} -
Have a repository administrator extend the active version tags ruleset before the module’s first release. Its existing
refs/tags/v*pattern covers only root versions: addrefs/tags/<directory>/v*, for examplerefs/tags/tools/v*orrefs/tags/integrations/example/v*. Keep both update and deletion protection, with no exclusions or bypass actors. Adding a module to the allow-list includes this administrative step; do not publish until it is complete. The nested preflight requires that explicit prefix in this ruleset rather than trying to infer coverage from broader wildcard patterns. -
Run
python3 .github/scripts/go_modules.py matrixfrom the repository root. It rejects unlisted or missing modules, wrong module identities, nonportable paths and everyreplacein publishable modules and the root, including dependency-to-dependency replacements.python3 .github/scripts/go_modules.py dependenciesadditionally downloads each publishable module’s required root version through the public proxy to prove it exists. -
Run the contribution guide’s Go gates inside each module, with
GOWORK=off. CI derives its matrix from the same allow-list: root plus every nested module gets build, vet, test, race, lint, tidy and govulncheck. The shared Go toolchain matrix applies to all modules; adding a module must not silently raise it.
Root go test ./... does not visit nested modules. A module’s go.sum is its own;
commit tidy changes there, not in the root. Do not add placeholder modules merely
to reserve an allow-list entry.
Unpublished recipes
Section titled “Unpublished recipes”The recipes module is CI-tested source, not a versioned dependency for consumers.
Its explicit publish: false entry includes it in every Go gate, with GOWORK=off,
but makes the nested release preflight refuse it, including retry attempts.
Unpublished modules are exempt from the released-root requirement but may replace
only the unversioned canonical root module with its relative path to the repository
root. The root always remains standard-library-only and replacement-free.
Recipes require the root at the placeholder v0.0.0 and replace it with ../,
so each run tests the checked-out library, not a previously released version.
Their third-party dependencies and checksums live only in recipes/go.mod and
recipes/go.sum. Do not tag or publish recipes, add a workspace, or use its
local replacement as proof that a published integration can be installed.
See ecosystem recipes for their supported boundaries.
Release one nested module
Section titled “Release one nested module”Nested publication is deliberately manual and one module per release PR and
merge commit. The pinned Cratis/release-action supports version and
tag-prefix, but has no path/monorepo input: its predecessor discovery can see
root versions, and its already-released check is repository-wide by commit.
The manual preflight therefore calculates the version from only that module’s
stable GitHub Releases and passes an explicit version to the action. The action
still owns tag and release creation in its success-only post hook.
- Prepare a dedicated PR to
main. Give it exactly the titlerelease(tools): minor, substituting the allow-listed directory andpatch,minorormajor. For an integration, use e.g.release(integrations/example): patch. - Apply the single intent label
no-release. For this manual path it suppresses automatic root publication; the title declares the nested bump. Do not add a root bump label, and do not mix root changes needing publication into this PR. Its body is the nested module’s release notes, following the same release-note rules as root releases. Use only an optional## Summaryand the applicable## Added,## Changed,## Fixed,## Removed,## Securityor## Deprecatedsections, with user-facing change bullets. Put bare issue references at the end of their delivering bullets. NoCloses/Fixes/Refslines, hidden issue references, test-plan or verification sections, or relative links. Put review/test details in comments. - Merge only after checks pass. Wait for automatic Publish to finish its
intentional root no-op. Keep
mainat that merge commit until the nested release finishes; merge another dedicated PR to release another module. - In Actions → Publish nested module → Run workflow, choose
main, enter the directory, the matching bump and that PR’s number. The workflow rejects a PR not merged at this exact commit, incorrect intent, unlisted or unpublished modules, Dependabot, an exceeded major ceiling or a conflicting release at this SHA. It reruns the Go and Markdown gates before publication. Before calling release-action, it validates the body with the repository’s checker using--label <title-bump> --strict --base main. This is release-bound validation even though the real PR label remainsno-release; no label is changed. The diff comparison uses the merge commit’s first parent, so it checks the delivered change rather than an empty diff against already-mergedmain. An unavailable rules fetch or unchecked diff is a failure, not a warning-only release. The preflight also reads the repository rulesets through the GitHub API and refuses publication unless version tags protects the module’s explicit prefix. Ruleset reads need no additional token permissions for this public repository; an API failure blocks release, with no manual override. Administrators must audit bypass actors manually when extending the ruleset: the API can omit that list for tokens without administrative permissions. - Confirm the release, actual tag target and proxy indexing jobs succeed.
The first minor creates
<directory>/v0.1.0; the first patch creates<directory>/v0.0.1. Later bumps use only this module’s stable releases. An existing next-version tag without a release is an error, never overwritten.
The workflows share a non-cancelling publish concurrency group. Do not queue several releases: GitHub may replace an older pending run. Rerun failed jobs to retry indexing. A full rerun finds the same module release at the same SHA and skips creation rather than incrementing again. A different module at that SHA fails, so an action no-op cannot masquerade as an independent release.
Consume a nested module
Section titled “Consume a nested module”Git tags include the directory; Go module versions do not. Once the
illustrative tools/v0.1.0 tag has been released, a consumer uses:
GOWORK=off go get github.com/cratis/fundamentals.go/tools@v0.1.0GOWORK=off GOPROXY=https://proxy.golang.org \ go mod download -json github.com/cratis/fundamentals.go/tools@v0.1.0For an integration tagged integrations/example/v0.2.0, use
github.com/cratis/fundamentals.go/integrations/example@v0.2.0.
Do not query the proxy with @tools/v0.1.0: a slash is not a valid Go proxy
version. The nested workflow verifies the prefixed Git tag against the release
commit, then indexes/downloads the canonical module@vX.Y.Z in a fresh cache,
without checking out this repository. No nested module is published by this
workflow change itself.
Mirror this pattern
Section titled “Mirror this pattern”Arc.Go and Chronicle.Go can adopt the allow-list, policy scripts, self-tests,
per-module build matrix and nested publish workflow. Change module to the
repository’s canonical lowercase root path and adapt the test fixture identity.
The root dependency guard in go_modules.py is Fundamentals-specific: preserve
your own core dependency policy rather than copying that restriction blindly.
Keep the root publisher’s existing identity guard specific to its repository.
A tools module currently using a root pseudo-version must move to a stable,
publicly retrievable root tag before it can pass these gates.
Run the offline proof with:
python3 -B -m unittest discover -s .github/scripts -p 'test_*.py' -vIt creates throwaway Git/module fixtures outside the checkout, exercises the same layout/matrix CLI used by CI, and tests independent version planning, PR intent, retries, conflicts, major ceilings and tag-ruleset coverage without publishing. Offline fixtures exercise the actual strict checker CLI against its bundled reviewed rules, including prohibited bodies and hidden issue references. Actual public-proxy installation still requires a real authorized release.
Recovery
Section titled “Recovery”If proxy indexing fails after publication, rerun the failed jobs. A full rerun
resolves the existing stable release for the same commit and indexes that tag
without bumping again. Investigate conflicting tags or releases; never move or
delete a published tag. Correct a bad release with a new version and, where
appropriate, a Go retract directive.
Public-proxy installation and pkg.go.dev visibility can be verified only after
a release exists. Central documentation publication is enabled: this
repository is registered in Cratis/Documentation, and with
DOCUMENTATION_ENABLED=true a change under Documentation/** on main
dispatches a site build that publishes these pages at
cratis.io/fundamentals/go. The
dispatch uses PAT_DOCUMENTATION and is independent of Go module indexing.