Skip to content

Migrate from ReleaseSourceConfig

forge.ReleaseSourceConfig was removed in v0.12.0 and replaced by forge.Endpoint. If your build broke on undefined: forge.ReleaseSourceConfig, this page is the whole migration.

Two ways to finish this migration greenly and incorrectly

Most of this change fails loudly: the type is gone, so the compiler finds every call site for you. Two mistakes do not. Both compile, both lint, both pass tests, and neither produces an error at runtime.

  1. Passing a pre-scoped config subtree to SettingsFromConfig. It takes your root config, because the endpoint resolves its own section. Hand it a subtree and the adapter looks for gitlab.gitlab.auth.value, finds nothing, resolves no credential, and says nothing about it.
  2. Joining the endpoint's three fields into one string. Host legitimately contains : and /, so any type:host:name spelling collides with its own payload.

Both are covered below. If you read nothing else, read those two sections.

The short answer

The old type carried six fields. Three were connection identity, and they are what Endpoint keeps. The other three were not, and went to where they already belonged.

// before, v0.11.x
src := forge.ReleaseSourceConfig{
    Type:    "gitlab",
    Host:    "gitlab.example.com",
    Owner:   "acme",
    Repo:    "tool",
    Private: true,
    Params:  map[string]string{"url_template": "..."},
}

// after, v0.12.0 and later
ep := forge.Endpoint{
    Type: "gitlab",
    Host: "gitlab.example.com",
    Name: "",           // which configured source of this type; see below
}

provider, err := factory(ctx, ep, cfg)   // cfg is your ROOT config
rel, err := provider.GetLatestRelease(ctx, "acme", "tool")

Owner and Repo move onto the operation, which already took them.

Where each field went

Was Now Why
Type Endpoint.Type unchanged; it still selects the provider
Host Endpoint.Host unchanged; empty still means the provider's default
Owner a parameter of every operation it was read by nothing. An unread second copy of a value already passed to each method
Repo a parameter of every operation, plus direct's tool_name key read once, as direct's tool-name fallback
Private gone; nothing replaces it read once, by forge-bitbucket. See below
Params the provider's own configuration subtree read only by direct and forge-bitbucket
Endpoint.Name new. Selects which configured source of a type this is

Name is the new field, and it is the reason for the change

Type and Host together do not always identify a connection. Two credentials for one host are two sources, and so are two direct sources of a provider that never looks at Host at all — which is the defect that forced this: an ordinary configuration could not tell two direct sources apart, and the second silently got the first one's url_template.

Name scopes the configuration subtree a provider reads: bare <type> when empty, <type>.<name> otherwise. Leave it empty and you get the behaviour you had.

It must not contain a dot. Endpoint.Validate rejects one, because a dotted name nests a level deeper than intended in any config backend treating dots as path separators.

Trap 1: SettingsFromConfig takes the ROOT config

This is the mistake to watch for, and forge's own guidance calls it the second-most-common mistake against this API.

// WRONG — compiles, lints, resolves no credential, reports nothing
settings := gitlab.SettingsFromConfig(ep, forge.SubConfig(cfg, "gitlab"))

// RIGHT
settings := gitlab.SettingsFromConfig(ep, cfg)

The endpoint resolves its own section, because which subtree a source reads is part of what the endpoint means. Pre-scope it yourself and the adapter looks one level too deep — gitlab.gitlab.auth.value — finds nothing, and returns settings whose credential source contributes nothing.

It can appear to work

Having resolved nothing from configuration, the credential falls through to the well-known environment variable. So a machine that happens to have GITLAB_TOKEN set behaves correctly, and the failure only shows up somewhere that does not — typically CI, typically later, and typically as a permission error rather than a configuration one.

Nothing checks this at runtime today. Verifying it is your job during the migration: after constructing a provider, confirm a credential actually resolved. Checking what resolved has the pattern.

Trap 2: never join the three fields into one string

// WRONG — Host contains ':' and '/', so this collides with its own payload
key := ep.Type + ":" + ep.Host + ":" + ep.Name

// RIGHT — Endpoint is comparable. Use it directly.
seen := map[forge.Endpoint]string{}
seen[ep] = "..."

Keep the three fields separate end to end: separate configuration keys, separate flags, separate struct fields. A Host of gitlab.example.com:8443/path makes any inline spelling ambiguous, and a sibling module hit exactly this addressing a KMS key, where service:keyid is unusable because a key ARN is itself full of colons.

Endpoint being comparable is the point of the change — it can be a map key, so you do not need a string form.

Params moved into configuration

direct is the provider this affects most. Its seven Params keys became ordinary configuration under its subtree, joined by tool_name, which Repo used to supply:

[direct]
url_template          = "https://example.com/{tool}/{version}/{tool}_{os}_{arch}.tar.gz"
tool_name             = "mytool"
pinned_version        = "v1.2.3"
version_url           = "https://example.com/{tool}/latest"
version_key           = "tag_name"
version_format        = "json"
checksum_url_template = "https://example.com/{tool}/{version}/checksums.txt"
signature_url_template = "https://example.com/{tool}/{version}/checksums.txt.sig"

There is no compatibility window. The old Params keys are not read at all. A tool that upgrades without migrating gets its notice from one diagnostic: url_template is required for the direct release provider, whose hint names the configuration key it expects and says the Params route was removed.

Two direct sources are now two endpoints with different Name values, reading direct.<name> each, which is the case the old type could not express.

Private is gone and nothing replaces it

It was read by forge-bitbucket alone and ignored everywhere else, and the check it fed moved to the operation. When a request is refused and no credential was resolved, forge-bitbucket attaches guidance naming the variables to set, because that is what a private repository looks like from outside.

It is a better error, not an earlier one. Whether the repository you go on to ask about is private is not knowable at construction, because one connection serves both.

If you need to fail early, check for a credential yourself at the layer that actually knows.

A worked example

go-tool-base performed this migration in !422, across a consumer with several sources and a real configuration stack — 20 files. It also removed props.ReleaseSource.Params outright rather than shimming it, which is the same call this module made and for the same reason.

Its reasoning is written up as go-tool-base spec 0192, which is the better read if you are deciding how to migrate rather than copying what changed.

See also