repomatic.setup_guide module

Build and manage the setup guide issue.

Backs the setup-guide command: composes the repository-settings checks from repomatic.lint_repo and the PAT permission probes from repomatic.github.token with the setup-guide-* templates into a single issue body, then drives the issue lifecycle. Each setup step renders as a collapsible section whose open/closed state and emoji reflect the check outcome, and the issue closes only once every verifiable step passes.

repomatic.setup_guide.CANNOT_VERIFY = '\n\n> [!NOTE]\n> This setting could not be verified: `REPOMATIC_PAT` is missing the **Administration: Read-only** permission. Update the token with the pre-filled link in the first step. The setting may well be correct already, but nothing here can confirm it.'

Note appended to a step whose probe could not run.

Both settings it covers are read through Administration-scoped endpoints, so a PAT issued without that permission answers 403 and the check lands on None. Saying so in the step beats dropping it: dropping also hid the token gap itself, since the missing permission had no other symptom.

class repomatic.setup_guide.GuideContext(config, repo, has_pat, has_notifications_pat, has_virustotal_key, has_cloudflare_api_token)[source]

Bases: object

Everything the steps read, resolved once per run.

The expensive lookups (PAT permission probes, pyproject.toml) are cached properties, so a step that never asks never pays and two steps asking the same question share one answer.

config: Config

The resolved [tool.repomatic] configuration.

repo: str | None

Repository in owner/repo form, or None when undetectable.

has_pat: bool

Whether REPOMATIC_PAT is configured.

has_notifications_pat: bool

Whether REPOMATIC_NOTIFICATIONS_PAT is configured.

has_virustotal_key: bool

Whether VIRUSTOTAL_API_KEY is configured.

has_cloudflare_api_token: bool

Whether CLOUDFLARE_API_TOKEN is configured.

property md: Metadata[source]

CI and project context, for the repository identity fields.

property has_changelog: bool[source]

Whether the configured changelog exists on disk.

property nuitka_active: bool[source]

Whether this project compiles binaries with Nuitka.

property pypi_package_name: str | None[source]

The PyPI name to register a Trusted Publisher for, if any.

Gated on is_python_package rather than on package_name being set: a uv virtual project declares [project] name purely to carry dependencies, so the name alone says nothing about whether anything is ever published. Asking those projects to register a publisher points them at a PyPI name they do not own, for a workflow file they do not have.

property pat_results: PatPermissionResults | None[source]

The PAT permission probe results, or None when unrunnable.

property missing_permissions_section: str[source]

Warning table naming the permissions the configured PAT lacks.

property token_ok: bool

Whether a PAT is configured and every permission probe passed.

property cloudflare_secrets_ok: bool

Whether the Cloudflare Pages deploy can authenticate.

The token alone settles it: the account it belongs to is derived from it at run time, even when it is scoped to nothing but Cloudflare Pages: Edit, so there is no second identifier to configure and nothing else to ask for here.

property cloudflare_token_name: str

Suggested name for the deploy token, carrying the month it was made.

Cloudflare’s token list shows what a token can do and never how old it is, while the rotation procedure turns entirely on telling the incumbent from its replacement. Stamping the month into the name is what makes a token approaching its one-year expiry obvious at a glance, and what lets the two coexist unambiguously during a handover.

Recomputed per run, so the name the guide suggests stays current while the step is still open. It stops moving once the issue closes, which is the point at which the body is no longer rewritten.

deploys_to(target)[source]

Whether this repository publishes its site to target.

One host’s setup step is the other’s noise, and the guide asks about exactly the one site.deploy names: a Cloudflare-hosted project has no GitHub Pages source to set, and the probe for it answers 404 forever. Routes through repomatic.config.deploys_to(), the same predicate the lint-repo audit reads.

Return type:

bool

property dependabot_ok: bool

Whether vulnerability alerts are confirmed enabled.

Piggybacks the Dependabot alerts permission probe, which only answers 200 when the alerts themselves are on.

probe_settings(check)[source]

Run a repository-settings check, or report it as failed.

Without a PAT or a repository there is nothing to read, and the step is reported incomplete rather than indeterminate: the reader still has to perform it.

Return type:

bool | None

probe_trusted_publisher()[source]

Whether PyPI provenance confirms the Trusted Publisher entry.

Needs no PAT: the probe hits the public PyPI integrity API.

Return type:

bool | None

class repomatic.setup_guide.SetupStep(placeholder, title, template, probe=<function SetupStep.<lambda>>, applies=<function SetupStep.<lambda>>, args=<function SetupStep.<lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False)[source]

Bases: object

One step of the setup guide, declared once and read by every phase.

The guide used to spell each step out four times: a probe, a render, a template keyword and a clause of the close gate. Keeping the four in sync was manual, and the applicability guards were duplicated between the probe and the render. One entry per step now drives all four.

placeholder: str

The $name this step’s rendered block fills in setup-guide.md.

title: str

Heading shown in the collapsible section’s <summary> line.

template: str

Template rendered as the section’s body.

probe()

Read the step’s completion state. Tri-state, per CheckResult.

applies()

Whether this repository needs the step at all.

A step that does not apply renders nothing and satisfies its gate, so a non-Sphinx project is never asked about Pages.

args()

Template variables, defaulting to the pair almost every step wants.

gates_closure: bool = True

Whether this step’s outcome can hold the issue open.

False for the two steps with nothing to probe (immutable releases, the final verification), which would otherwise wedge the issue open forever.

tolerates_unknown: bool = False

Whether an indeterminate probe (None) satisfies the gate.

True for the settings read through Administration-scoped endpoints: a PAT without that permission answers 403, and a reader has no way to satisfy a check that cannot run, so it must not block the issue closing. Everywhere else None is treated as incomplete, keeping the step prompting.

explains_unverifiable: bool = False

Append CANNOT_VERIFY to the body when the probe answered None.

The reader is looking at a setting they were told to configure, so the difference between “verified” and “nobody could look” belongs on screen.

outcome(ctx)[source]

The step’s state as the section renders it.

None survives only where tolerates_unknown says an unreadable probe is not the reader’s fault; elsewhere it collapses to incomplete so the section stays open.

Return type:

bool | None

render(ctx)[source]

Render this step’s collapsible section, empty when it does not apply.

Return type:

str

satisfied(ctx)[source]

Whether this step lets the issue close.

A step that does not apply, or that gates nothing, is always satisfied.

Return type:

bool

repomatic.setup_guide.SETUP_STEPS: tuple[SetupStep, ...] = (SetupStep(placeholder='step_token', title='Create and configure the token', template='setup-guide-token', probe=<function <lambda>>, applies=<function SetupStep.<lambda>>, args=<function <lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='step_dependabot', title='Configure Dependabot settings', template='setup-guide-dependabot', probe=<function <lambda>>, applies=<function SetupStep.<lambda>>, args=<function SetupStep.<lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='immutable_releases_step', title='Enable immutable releases', template='immutable-releases', probe=<function <lambda>>, applies=<function <lambda>>, args=<function <lambda>>, gates_closure=False, tolerates_unknown=True, explains_unverifiable=False), SetupStep(placeholder='step_branch_ruleset', title='Protect the main branch', template='setup-guide-branch-ruleset', probe=<function <lambda>>, applies=<function SetupStep.<lambda>>, args=<function <lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='step_fork_pr_approval', title='Require approval for fork PR workflows', template='setup-guide-fork-pr-approval', probe=<function <lambda>>, applies=<function SetupStep.<lambda>>, args=<function SetupStep.<lambda>>, gates_closure=True, tolerates_unknown=True, explains_unverifiable=True), SetupStep(placeholder='step_sha_pinning_required', title='Require SHA pinning for GitHub Actions', template='setup-guide-sha-pinning-required', probe=<function <lambda>>, applies=<function SetupStep.<lambda>>, args=<function SetupStep.<lambda>>, gates_closure=True, tolerates_unknown=True, explains_unverifiable=True), SetupStep(placeholder='step_pypi_trusted_publisher', title='Register the PyPI Trusted Publisher entry', template='setup-guide-pypi-trusted-publisher', probe=<function <lambda>>, applies=<function <lambda>>, args=<function <lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='step_pages_source', title='Set GitHub Pages deployment source to GitHub Actions', template='setup-guide-pages-source', probe=<function <lambda>>, applies=<function <lambda>>, args=<function SetupStep.<lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='step_cloudflare_pages', title='Configure the Cloudflare Pages credentials', template='setup-guide-cloudflare-pages', probe=<function <lambda>>, applies=<function <lambda>>, args=<function <lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='step_virustotal', title='Configure VirusTotal scanning (optional)', template='setup-guide-virustotal', probe=<function <lambda>>, applies=<function <lambda>>, args=<function SetupStep.<lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='step_notifications_pat', title='Create and configure the notifications token', template='setup-guide-notifications-pat', probe=<function <lambda>>, applies=<function <lambda>>, args=<function SetupStep.<lambda>>, gates_closure=True, tolerates_unknown=False, explains_unverifiable=False), SetupStep(placeholder='step_verify', title='Verify the setup', template='setup-guide-verify', probe=<function SetupStep.<lambda>>, applies=<function SetupStep.<lambda>>, args=<function SetupStep.<lambda>>, gates_closure=False, tolerates_unknown=False, explains_unverifiable=False))

Every step of the setup guide, in the order the issue body lists them.

repomatic.setup_guide.manage_setup_guide(config, *, has_pat, has_notifications_pat, has_virustotal_key, has_cloudflare_api_token=False, repo)[source]

Render the setup guide issue body and drive the issue lifecycle.

Walks SETUP_STEPS: each step probes its own state, renders its collapsible section, and reports whether it lets the issue close. The issue closes only when every applicable gating step passes.

Parameters:
  • config (Config) – The resolved [tool.repomatic] configuration.

  • has_pat (bool) – Whether REPOMATIC_PAT is configured.

  • has_notifications_pat (bool) – Whether REPOMATIC_NOTIFICATIONS_PAT is configured.

  • has_virustotal_key (bool) – Whether VIRUSTOTAL_API_KEY is configured.

  • has_cloudflare_api_token (bool) – Whether CLOUDFLARE_API_TOKEN is configured.

  • repo (str | None) – Repository in owner/repo format; permission and settings checks are skipped when None.

Return type:

None