go-core-currency¶
Checks, on a release merge request, that every Go family core the
project's go.mod files require is at that core's latest release. A family
is a core module and the modules built on it, such as a provider
abstraction and its adapters. A member that releases while still requiring
the previous core ships against a core that has moved on, and has to be
released again once someone notices.
The cores come from a family feed you point the feed input at: a small
JSON document listing each family and its core's module path. The format is
on Family feed, and
Publish a family feed sets one up.
Which modules are members is never listed anywhere: the job reads it from
go.mod, so a member nobody remembered to list is still checked.
Estate modules, optionally. Set first_party to your module-path prefix
and every other module under it that the project requires directly is checked
too. Those are estate findings: stale, deprecated or moved is always a
warning, in every mode, because a pinned leaf is far more often deliberate than
a pinned core. See
spec 0103.
Advisory by default. A stale core exits 3, which the job tolerates, so it
shows as a warning and blocks nothing until you set mode: enforce. A feed or
proxy the job cannot read also exits 3, in every mode: an outage never blocks
a release, and never passes as green either. See
spec 0102.
Jobs¶
| Job | What it runs |
|---|---|
go-core-currency |
Loads the feed, walks every go.mod in the checkout (nested modules included; vendor/, testdata/ and node_modules/ skipped), and finds the latest release of each core a module requires, indirect requirements included: from pkgsite's /v1/module/{module} and from go_proxy's @latest, taking the higher. allow_failure: exit_codes: [3]. |
Inputs¶
| Input | Type | Default | Description |
|---|---|---|---|
feed |
string | required | The family feed: a URL (https://…, file://…) or a path inside the repository. |
mode |
string | warn |
warn: a stale core is a warning. enforce: a stale core fails the job, which blocks the merge under "pipelines must succeed". off: no job. |
image |
string | go-tools:v0.5.0 |
Image the check runs in. It needs only python3; no Go toolchain is invoked. |
stage |
string | test |
GitLab CI stage. |
go_proxy |
string | https://proxy.golang.org |
Go module proxy asked for each core's @latest. |
first_party |
string | "" |
Space-separated module-path prefixes (gitlab.com/acme/). A direct requirement under one that is not a core is checked as an estate module, never blocking. Empty checks family cores only. |
pkgsite |
string | https://pkg.go.dev |
pkg.go.dev, whose /v1/module/{module} gives each core's latest version. Empty asks the proxy alone. |
if |
string | a colophon Release MR | Gating rules:if: expression. The default matches a merge-request pipeline whose source branch starts colophon/release/. Point it at your own release branch if you release another way. |
Outcomes¶
warn (default) |
enforce |
|
|---|---|---|
| every required core is current, or none is required | exit 0 | exit 0 |
| a required core is behind its latest release | exit 3, warning | exit 1, failed |
| an estate module is behind, deprecated or moved | exit 3, warning | exit 3, warning |
| a required core is deprecated or moved | exit 3, warning | exit 3, warning |
the feed or the proxy cannot be read, or the feed's schema_version is not 1 |
exit 3, warning | exit 3, warning |
feed is empty |
exit 1 | exit 1 |
A stale core prints the fix. It is a bump on the target branch, never on the release branch, which your release tool then re-cuts:
STALE ./go.mod: gitlab.com/phpboyscout/go/chat v0.30.0, latest is v0.31.0 (family chat)
(cd . && go get gitlab.com/phpboyscout/go/[email protected] && go mod tidy)
Deprecated and moved modules¶
The job trusts a "latest" version only if that version's own go.mod declares
the module being checked. A renamed repository can otherwise serve the new
name's tags under the old path: the proxy has answered @latest for a renamed
module with a tag whose go.mod names its successor, and a go get of that
version fails. So the job walks the candidates down to the newest release that
really is the module.
That go.mod is also where deprecation lives, as it is for the go command:
DEPRECATED ./go.mod: gitlab.com/phpboyscout/go/chat-platform (estate): renamed to gitlab.com/phpboyscout/go/comms, which continues this module from its next release.
A module whose later tag declares another path, without a // Deprecated:
comment, is reported as MOVED … continues as <path>. Both are warnings, never
blocks: moving off a module is a migration, not a version bump.
What is not checked¶
- The core's own module, so the core's own release passes.
- A core whose module lives in the same repository. A sibling module there
(a
cli/beside the framework it ships) requires the core's previous release on every release merge request, because the release being cut is not tagged yet. - A core replaced by a local path, or by a different module.
- Families whose
ecosystemis notgo, or whose core has no module. - Indirect requirements on estate modules. The module in between chose them, and minimal version selection already takes the highest. Cores are checked direct and indirect.
- A first-party module no source knows, which is taken to be private and listed as skipped.
Limits¶
- A release nobody has fetched is invisible. The Go proxy, its index and
pkg.go.dev learn of a version only when something requests it, so an
unfetched tag is missed (never invented) until then; chat v0.32.0 went
twelve hours unseen. The
colophoncomponent requests each Go tag it cuts once, for exactly this. Separately, the proxy's@latestis cached and has trailed the index by close to an hour, which is why pkg.go.dev is read first. - A release merge request that passed stays mergeable. If the core releases after the release merge request's pipeline ran, the merge gate still reads that green pipeline. Re-run it to re-check.
- Siblings are not compared. Only the cores are; a member requiring an old release of another member is not reported.
Usage¶
include:
- component: gitlab.com/phpboyscout/cicd/[email protected]
inputs:
feed: https://example.com/families.json
A single project can keep its feed in the repository instead: