repomatic.init.upgrade module¶

Pick the upstream pin repomatic init writes, and move it with --upgrade.

A downstream repository runs the reusable workflows of the release its thin callers pin. init decides that pin: the running release, held back by the [tool.repomatic] minimum-release-age cooldown when the release is too fresh, and never moved below the pin the repository already carries. init --upgrade picks the newest release past the cooldown instead, runs init as that release, and describes the move for the pull request that carries it.

This module is the only reader of the running version and of the commit SHA of its tag, so a test stages a release in one place.

repomatic.init.upgrade.BREAKING_BULLET_RE = re.compile('^[ \\t]*[-*][ \\t]+(?P<bullet>\\*\\*(?:Breaking|Deprecated):\\*\\*.*)$', re.MULTILINE)¶

A release-notes bullet announcing a breaking change or a deprecation.

The changelog convention opens these bullets with a bold **Breaking:** or **Deprecated:** label, and each GitHub release body repeats its changelog section verbatim. The upgrade report lifts them above the full notes, because they are the entries a repository may have to act on.

repomatic.init.upgrade.INIT_WARNING_RE = re.compile('^[ \\t]*warning:.*$', re.IGNORECASE|re.MULTILINE)¶

A warning line in the output of repomatic init.

Matches the warning: lines the configuration loader logs, like an unknown [tool.repomatic] key, and the Warning: lines of the closing summary.

repomatic.init.upgrade.default_version_pin()[source]¶

Derive the default version pin from __version__.

Strips any .dev0 suffix and prefixes with v. For example, "5.10.0.dev0" becomes "v5.10.0".

Return type:

str

repomatic.init.upgrade.pins_running_release(version)[source]¶

Whether the pin version names the release the running build heads for.

Compared on the release tuple, so a development build matches the release it is heading for: the pin drops the .devN suffix the running version carries, and string equality would never match on a dev checkout.

Parameters:

version (str) – A pin, in its vX.Y.Z tag spelling.

Return type:

bool

repomatic.init.upgrade.github_yaml_texts(root)[source]¶

Read every workflow and composite-action file under root, once.

Parameters:

root (Path) – Root of the repository to scan.

Return type:

dict[Path, str]

Returns:

The text of each file github_yaml_paths() lists, keyed by path. A file that cannot be read is skipped.

repomatic.init.upgrade.highest_upstream_pin(texts, repo)[source]¶

The highest upstream uses: pin the given workflow texts carry.

Read off the files github_yaml_texts() returns, the same files sync-workflow-pins bumps. Stragglers left behind at an older pin therefore converge upward onto the repository-wide maximum, matching how sync-action-pins treats a slug pinned at more than one version.

Only repo is matched, not every slug in UPSTREAM_REPO_SLUGS: a caller still naming a pre-rename slug is a tombstone init rewrites, not a pin to preserve.

Parameters:
  • texts (Iterable[str]) – The contents of the workflow and composite-action files.

  • repo (str) – Upstream owner/repo whose refs are read.

Return type:

UpstreamRefPin | None

Returns:

The highest pin found, or None for a repository carrying none.

repomatic.init.upgrade.resolve_default_pin(config, *, repo='kdeldycke/repomatic', today=None, warnings=None, floor=None)[source]¶

Resolve the upstream pin, holding a fresh release back by cooldown.

Returns the (version, commit_sha) init stamps into thin-caller uses: refs. In the common case, and on any datasource failure, this is the running repomatic version paired with its build-time SHA. Only when adopting a release still inside the [tool.repomatic] minimum-release-age window does the pin step back to the newest cooldown-cleared release (see _select_cooldown_pin), resolving that tag’s SHA afresh.

Important

The cooldown may hold back an adoption. It may never rewrite a pin the repository already carries, which is what floor records.

init is the only writer of these refs: sync-action-pins skips every slug in UPSTREAM_REPO_SLUGS, and ACTION_PIN_RE does not even match a subpath-carrying reusable-workflow ref. So a downstream repository adopts a new repomatic release one way: a newer init runs. A maintainer runs one by hand, and the CI sync-repomatic job runs one through init --upgrade, which picks the release itself (see resolve_upgrade_target()) and bypasses this function. Two things follow.

A pin equal to the running version is not a decision to gate. Without a newer release to adopt, the CI sync-repomatic job runs init at the pinned version itself, so base equals floor; re-judging it there downgrades the repository once a week after each hand-bump.

A pin below the running version is a skew. init renders caller content from the running version, so a ref naming an older release ships that content against an older reusable-workflow surface, which GitHub rejects as soon as the two disagree (see test_thin_caller_workflow_call_inputs_stay_minimal). Returning such a pin is therefore only half a decision: resolve_workflow_pin() reads it back and, when the repository already carries workflows, tells init to skip regenerating them so the tree stays coherent at the pin it keeps. A first-time adoption has no tree to keep, so there the skew stands as the only alternative to writing no workflows at all.

Parameters:
  • config (Config) – Repomatic config supplying the minimum-release-age window.

  • repo (str) – Upstream owner/repo whose releases gate the pin.

  • today (date | None) – Reference date for the cooldown; defaults to the current UTC date.

  • warnings (list[str] | None) – When provided, a cooldown note is appended here (in addition to being logged), so init can surface it in its final summary rather than only mid-run.

  • floor (UpstreamRefPin | None) – The highest upstream pin already committed downstream, from highest_upstream_pin(). None for a repository carrying none, the one case the cooldown may step back freely.

Return type:

tuple[str, str | None]

Returns:

(version_pin, commit_sha). commit_sha is None when no SHA can be resolved, leaving a bare tag pin.

class repomatic.init.upgrade.WorkflowPin(version, commit_sha, adopts_content=True)[source]¶

Bases: object

The upstream pin init writes into the thin callers it generates.

version: str¶

The pin, in its vX.Y.Z tag spelling.

commit_sha: str | None¶

The commit SHA paired with version, or None for a bare tag pin the next sync-action-pins run re-hardens.

adopts_content: bool = True¶

Whether the workflows take the content of the running release.

False when the cooldown holds the pin below the running release on a tree that already carries workflows. init then leaves the workflows as they are, at the pin they already have.

repomatic.init.upgrade.resolve_workflow_pin(config, *, version, cooldown, repo, floor, warnings)[source]¶

Resolve the pin init writes, and whether the workflows move with it.

An explicit version keeps the build-time SHA and bypasses the cooldown. Without one, the pin derives from the running release, held back by resolve_default_pin() when cooldown is set.

A pin the cooldown held below the running version cannot carry this version’s caller content. init renders bodies from the running wheel and substitutes only the ref, so writing them beside an older uses: pin ships half an adoption: the trigger blocks, concurrency groups and env: of the new release, against the reusable-workflow surface of the old one. Declining the pin has to decline the content with it, or the cooldown buys nothing while the caller half lands anyway, silently.

Only a repository that already carries workflows (a floor) has somewhere to stand: leaving them untouched keeps a coherent tree at floor. A first-time adoption has no such fallback, since skipping would write no workflows at all, and is the one case resolve_default_pin() documents the skew as unavoidable.

Parameters:
  • config (Config) – Repomatic config supplying the minimum-release-age window.

  • version (str | None) – An explicit pin, in its vX.Y.Z tag spelling, or None.

  • cooldown (bool) – Hold a fresh running release back by the cooldown.

  • repo (str) – Upstream owner/repo whose releases gate the pin.

  • floor (UpstreamRefPin | None) – The highest upstream pin already committed downstream.

  • warnings (list[str]) – Cooldown notes are appended here, for the init summary.

Return type:

WorkflowPin

class repomatic.init.upgrade.UpgradeTarget(version, released, previous=None, held_back=None)[source]¶

Bases: object

The release init --upgrade runs as, chosen before any file is written.

version: str¶

Bare version to run init as.

released: str¶

PyPI upload date of version, as YYYY-MM-DD. Empty when the datasource does not list the release pinned on disk.

previous: str | None = None¶

Bare version of the highest upstream pin on disk before the run, or None for a repository carrying none.

held_back: Candidate | None = None¶

The newest release above version that the minimum-release-age window still holds back: the one a later run adopts.

property in_process: bool¶

Whether the running release is the target, so no hand-off is needed.

repomatic.init.upgrade.resolve_upgrade_target(config, output_dir, *, repo='kdeldycke/repomatic', today=None)[source]¶

Pick the release init --upgrade runs as.

That is the newest final, non-yanked release on PyPI older than the [tool.repomatic] minimum-release-age window, and never one below the upstream pin the repository already carries: a release pinned by hand inside the window stays. PyPI is the datasource because uvx installs from it, so a version whose publication failed, with a tag but no wheel, is never picked.

Parameters:
  • config (Config) – Repomatic config supplying the window and both opt-outs.

  • output_dir (Path) – Root of the target repository, read for its current pin.

  • repo (str) – Upstream owner/repo; its name is the PyPI package.

  • today (date | None) – Reference date for the cooldown; defaults to the current UTC date.

Return type:

UpgradeTarget | None

Returns:

The target, or None when the upgrade does not apply: turned off by upstream-pin.sync or workflow.sync, run from a build that is not a release, or no release has cleared the window.

repomatic.init.upgrade.upgrade_command(version, init_args, *, config, repo='kdeldycke/repomatic', today=None)[source]¶

Build the command running init as another repomatic release.

--no-cooldown hands the child the decision resolve_upgrade_target() already took. Left to judge the window again, the child reads GitHub release dates where the parent read PyPI ones, and a split verdict near the limit writes the new configs but leaves the callers at the old release.

The window reaches the resolution as --exclude-newer, which gates the child’s dependency tree like any other install. The package itself carries the self-pin exemption, because it already cleared this repository’s window: with that window disabled, no cutoff is passed, and the workflow’s own UV_EXCLUDE_NEWER would refuse the release.

Parameters:
  • version (str) – Bare version to run.

  • init_args (Sequence[str]) – Arguments for the child init, after --no-cooldown.

  • config (Config) – Repomatic config supplying the minimum-release-age window.

  • repo (str) – Upstream owner/repo; its name is the package and the command.

  • today (date | None) – Reference date for the cutoff; defaults to the current UTC date.

Return type:

list[str]

Returns:

The command line.

repomatic.init.upgrade.run_upgrade(command, output_dir)[source]¶

Run the init hand-off built by upgrade_command().

Runs from output_dir, where the child finds [tool.repomatic] as the parent did. Both output streams are merged in the order they were written, then captured, because upgrade_report() quotes the warnings the child printed: a config key the new release no longer knows shows up nowhere else.

Parameters:
Return type:

CompletedProcess[str]

Returns:

The finished process, its merged output in stdout.

repomatic.init.upgrade.hand_off_upgrade(target, config, *, repo, output_dir, flags=())[source]¶

Run init as the release target names, in a child process.

The child gets the upstream repository and the output directory of this run, then flags: the deletion flags this run was given, so the child prunes what this run would have.

Parameters:
Return type:

CompletedProcess[str]

Returns:

The finished process, its merged output in stdout.

repomatic.init.upgrade.upgrade_invite(previous, adopted)[source]¶

Render the invitation to review a move of the upstream pin.

Shared by every pull request that moves the pin, the init --upgrade report and a sync-workflow-pins lockstep bump, so both hand over the same launcher.

Parameters:
  • previous (str) – Bare version the pin moved from.

  • adopted (str) – Bare version the pin moved to.

Return type:

str

Returns:

The upgrade-invite section, ending on the skill launcher.

repomatic.init.upgrade.upgrade_report(target, output_dir, config, *, init_output='', repo='kdeldycke/repomatic', today=None)[source]¶

Describe the upgrade a run just applied, for its pull request body.

Reads the pin back from disk rather than trusting target, since what the run wrote is what the pull request carries. The sections, in reading order:

  • The version move, with its compare link and release date.

  • Every **Breaking:** and **Deprecated:** bullet of the releases in between.

  • The warnings the run printed, then its whole output, collapsed.

  • The release notes, one collapsed block per release.

  • The invitation to run the repomatic-upgrade review.

  • The newer release the window still holds back, if any.

Parameters:
  • target (UpgradeTarget) – The target resolve_upgrade_target() picked.

  • output_dir (Path) – Root of the target repository, read for the new pin.

  • config (Config) – Repomatic config supplying the minimum-release-age window.

  • init_output (str) – The output of a hand-off run, empty for an in-process one.

  • repo (str) – Upstream owner/repo whose releases are described.

  • today (date | None) – Reference date for relative dates; defaults to the current UTC date.

Return type:

tuple[str, str] | None

Returns:

(adopted, markdown), or None when the run moved no pin forward.