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**: 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.'¶

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, has_cloudflare_r2_keys)[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.

has_cloudflare_r2_keys: bool¶

Whether CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY are both 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 pat_scope: CheckResult | None[source]¶

The PAT repository-scope probe result, or None when unrunnable.

property scope_warning: str¶

Warning naming another repository the configured PAT covers.

Empty unless the probe failed. A probe that could not conclude answers None, and the reader has nothing to act on then: the token step stays closed, as it does for a pass.

property token_ok: bool¶

Whether a PAT is configured, permitted and limited to this repository.

The token step asks for this repository and nothing else, so a token that the scope probe finds on another repository has not done that step.

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.

property cloudflare_r2_token_name: str¶

Suggested name for the R2 upload token, dated for the same reason as cloudflare_token_name: it carries the same one-year expiry.

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_cloudflare_r2', title='Configure the R2 upload keys', template='setup-guide-cloudflare-r2', 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.

Each step’s template takes the shape of the REPOMATIC_PAT step: one lead sentence saying what to set and why, then numbered actions in the order they run. An action gives the command, then at most one alternative, written Or by hand: when it goes through the web UI. A form’s values go in a field table. A note only warns against a wrong action or names the cost of skipping one. Reasons and other routes live in docs/, or in a comment beside the step.

repomatic.setup_guide.manage_setup_guide(config, *, has_pat, has_notifications_pat, has_virustotal_key, has_cloudflare_api_token=False, has_cloudflare_r2_keys=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.

  • has_cloudflare_r2_keys (bool) – Whether both R2 upload secrets are configured.

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

Return type:

None