repomatic.release.self_pin module

The pin a workflow holds on the upstream toolkit itself.

A workflow reaches the upstream toolkit from a run: command through an inline pin: uvx 'repomatic==1.2.3' show-metadata. That pin moves in lockstep with the uses: refs of the same workflows, whatever the cooldown, so the command also carries a cooldown exemption for the package. On main of the source repository, the workflows run the CLI from the lockfile instead, and the release freeze swaps the one form for the other.

Every spelling of those commands lives here, for each of their writers and checkers: the release freeze and unfreeze, the realignment of repomatic init, the rewrite of sync-workflow-pins, and the checks of lint-repo. Each one takes the package name: repomatic init --upstream-repo can point a repository at a renamed fork, whose package is not repomatic.

repomatic.release.self_pin.cooldown_exemption(package)[source]

The uv flag letting a just-published package install under the cooldown.

Every workflow exports a UV_EXCLUDE_NEWER covering all package resolution (see docs/workflows.md § Install-time cooldown), and it applies to the frozen 'repomatic==X.Y.Z' self-pin like any other requirement. That pin moves in lockstep with the uses: refs pointing at the same tag, so the version it names is always minutes old: without an exemption every downstream repository would fail to resolve it until the window elapsed. A zero-length window sets the cutoff of that one package to “now”, and leaves the rest of the tree gated.

uv exposes no environment variable for --exclude-newer-package, so the exemption has to ride on the command line. That is why it sits beside the pin instead of the workflows declaring it once.

Note

A uvx resolution reads no project configuration at all, so moving the exemption into [tool.uv] or an adjacent uv.toml would not work either: both are ignored (verified against uv 0.12.3). The one knob that does reach it, --config-file / UV_CONFIG_FILE, replaces discovered configuration instead of merging with it: set for a whole CI environment, it silently drops required-version, exclude-newer, dependency-groups and build-backend from every other uv command there, unless a complete mirror of [tool.uv] sits beside it.

Todo

Declare the exemption once, instead of splicing it onto every frozen command line, as soon as uv grows a configuration or environment knob for --exclude-newer-package: astral-sh/uv#20995. Glob exemptions (astral-sh/uv#20788) and pin-based bypasses (astral-sh/uv#19864, astral-sh/uv#18921) could settle it too.

Spelled as the ISO 8601 P0D rather than the "0 day" used in pyproject.toml’s exclude-newer-package table: the flag travels through YAML folded scalars into a shell, where the space in 0 day would need quoting that survives both. P0D needs none.

Caution

Every character here lands on 80-odd already-long workflow lines at freeze time. tests/test_prepare_release.py simulates a freeze and fails if the result breaches the 120-column cap of yamllint, so lengthening this string means reflowing the workflows that no longer fit.

Parameters:

package (str) – The distribution name of the upstream toolkit.

Return type:

str

Returns:

The flag and its value, as one shell-safe string.

repomatic.release.self_pin.local_cli_invocation(package)[source]

How every workflow on main runs the CLI, before the freeze rewrites it.

Resolving from uv.lock rather than from the index is what keeps the cooldown off the critical path here. A lockfile entry is pinned and hash-verified, so it is strictly stronger than a publication-age gate, and it cannot be made unsatisfiable by one: uvx --from . re-resolves [project.dependencies] on every call, and reads neither uv.lock nor [tool.uv] exclude-newer-package, so a floor naming a release younger than the window took every workflow down at once with nowhere to record the bypass.

--frozen uses the lockfile as-is instead of asserting it is current, which is deliberate: --locked would fail every job the moment pyproject.toml drifted ahead of uv.lock, including the sync-uv-lock job whose whole purpose is to close that gap.

Parameters:

package (str) – The distribution name of the upstream toolkit, which is also the name of its command.

Return type:

str

Returns:

The command prefix, ready to take the arguments of the CLI.

repomatic.release.self_pin.frozen_cli_invocation(package, version)[source]

Render the frozen, cooldown-exempt uvx invocation of package.

The one spelling both writers emit: the release freeze writes it whole, and add_cooldown_exemption() converges an exemption-less command onto the same bytes, so frozen_cli_re() has one shape to recognize.

Parameters:
  • package (str) – The distribution name of the upstream toolkit.

  • version (str) – The release the invocation pins.

Return type:

str

repomatic.release.self_pin.frozen_cli_re(package: str) → Pattern[str][source]

Match frozen_cli_invocation() at any version.

What the release unfreeze turns back into local_cli_invocation().

Return type:

Pattern[str]

repomatic.release.self_pin.inline_pin_re(package: str) → Pattern[str][source]

Match an inline {package}==X.Y.Z pin, in any shape a workflow writes it.

The one matcher of the pinned version: the lint-repo lockstep check reads it, and repomatic init and sync-workflow-pins rewrite it, so the check never flags a pin the rewrite cannot fix. Quoted or bare, with or without extras: uvx 'repomatic==1.2.3', --with 'repomatic[cli]==1.2.3', pip install repomatic==1.2.3.

The lookbehind leaves out a longer name ending in package, like papaya-repomatic==1.0. The exemption of cooldown_exemption() never matches: it joins the package to its window with a single =.

Memoized: every caller asks for the same package, once per workflow file.

Return type:

Pattern[str]

repomatic.release.self_pin.find_inline_pins(content, package)[source]

The versions the inline pins of package name, in the order they appear.

Parameters:
  • content (str) – The workflow file text.

  • package (str) – The distribution name of the upstream toolkit.

Return type:

list[str]

repomatic.release.self_pin.align_inline_pins(content, package, version)[source]

Rewrite every inline pin of package onto version, in either direction.

The refs are the source of truth, so a pin ahead of them is drift the same way one behind them is.

Parameters:
  • content (str) – The workflow file text.

  • package (str) – The distribution name of the upstream toolkit.

  • version (str) – The version to pin, without the v of a tag.

Return type:

tuple[str, list[tuple[str, str, str]]]

Returns:

The updated text, and one (package, old, new) triple per pin that moved.

repomatic.release.self_pin.cooldown_exemptions(content, package)[source]

Whether each uvx command pinning package carries its exemption.

Parameters:
  • content (str) – The workflow file text.

  • package (str) – The distribution name of the upstream toolkit.

Return type:

list[bool]

Returns:

One flag per command, in the order they appear.

repomatic.release.self_pin.add_cooldown_exemption(content, package)[source]

Splice the exemption into every uvx command pinning package.

The inline pin moves in lockstep with the uses: refs, regardless of the cooldown, so the version it names can be minutes old. Every workflow exports a UV_EXCLUDE_NEWER covering all resolution, and uvx reads no per-package exemption from the environment or from pyproject.toml, so without the flag on the command line the freshly aligned pin fails to resolve until the window elapses.

Idempotent: a command already carrying the exemption is left untouched.

Parameters:
  • content (str) – The workflow file text.

  • package (str) – The distribution name of the upstream toolkit.

Return type:

str

Returns:

The updated text.