Skip to content

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 ecosystem is not go, 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 colophon component requests each Go tag it cuts once, for exactly this. Separately, the proxy's @latest is 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:

      feed: .gitlab/families.json