repomatic.config module¶
Configuration schema and loading for [tool.repomatic] in pyproject.toml.
Defines the Config dataclass, its TOML serialization helpers, and the
load_repomatic_config function that reads, validates, and returns a typed
Config instance.
- class repomatic.config.CacheConfig(dir='', github_release_ttl=604800, github_releases_ttl=86400, max_age=30, npm_ttl=86400, pypi_ttl=86400)[source]¶
Bases:
objectNested schema for
[tool.repomatic.cache].- dir: str = ''¶
Override the binary cache directory path.
When empty (the default), the cache uses the platform convention:
~/Library/Caches/repomaticon macOS,$XDG_CACHE_HOME/repomaticor~/.cache/repomaticon Linux,%LOCALAPPDATA%\repomatic\Cacheon Windows. TheREPOMATIC_CACHE_DIRenvironment variable takes precedence over this setting.
- github_release_ttl: int = 604800¶
Freshness TTL for cached single-release bodies (seconds).
GitHub release bodies are immutable once published, so a long TTL (7 days) is safe. Set to
0to disable caching for single-release lookups.
- github_releases_ttl: int = 86400¶
Freshness TTL for cached all-releases responses (seconds).
New releases can appear at any time, so a shorter TTL (24 hours) balances freshness with API savings.
- max_age: int = 30¶
Auto-purge cached entries older than this many days.
Set to
0to disable auto-purge. TheREPOMATIC_CACHE_MAX_AGEenvironment variable takes precedence over this setting.
- class repomatic.config.DependencyGraphConfig(all_extras=True, all_groups=True, level=None, no_extras=<factory>, no_groups=<factory>, output='./docs/assets/dependencies.mmd')[source]¶
Bases:
objectNested schema for
[tool.repomatic.dependency-graph].- all_extras: bool = True¶
Whether to include all optional extras in the graph.
When
True, theupdate-dep-graphcommand behaves as if--all-extraswas passed.
- all_groups: bool = True¶
Whether to include all dependency groups in the graph.
When
True, theupdate-dep-graphcommand behaves as if--all-groupswas passed. Projects that want to exclude development dependency groups (docs, test, typing) from their published graph can set this tofalse.
- level: int | None = None¶
Maximum depth of the dependency graph.
Nonemeans unlimited.1= directly-declared deps only,2= adds their deps, etc. Equivalent to--level.
- no_extras: list[str]¶
Optional extras to exclude from the graph.
Equivalent to passing
--no-extrafor each entry. Takes precedence overdependency-graph.all-extras.
- class repomatic.config.DocsConfig(apidoc_exclude=<factory>, apidoc_extra_args=<factory>, update_script='./docs/docs_update.py')[source]¶
Bases:
objectNested schema for
[tool.repomatic.docs].- apidoc_exclude: list[str]¶
Glob patterns for modules to exclude from
sphinx-apidoc.Passed as positional exclude arguments after the source directory (e.g.,
["setup.py", "tests"]).
- class repomatic.config.AgentLayout(skills, subagents, settings)[source]¶
Bases:
objectWhere one AI coding agent expects its assets to live.
- repomatic.config.AGENT_LAYOUTS: Final[dict[str, AgentLayout]] = {'claude_code': AgentLayout(skills='./.claude/skills/', subagents='./.claude/agents/', settings='./.claude/settings.json')}¶
Asset layout per agent, keyed by
extra_platforms.ALL_AGENTStrait ID.Only agents repomatic can actually lay out appear here.
clineandcursorare valid trait IDs but have no Agent Skills layout to target, so selecting one is rejected rather than silently producing a Claude Code tree.
- repomatic.config.DEFAULT_AGENT: Final[str] = 'claude_code'¶
Agent assumed when
[tool.repomatic.flavor] agentis unset.
- repomatic.config.DEFAULT_CI: Final[str] = 'github_ci'¶
CI system assumed when
[tool.repomatic.flavor] ciis unset.
- repomatic.config.CLOUDFLARE_PLACEMENT_MODES: Final[frozenset[str]] = frozenset({'', 'off', 'smart'})¶
Values
site.cloudflare-placementaccepts, empty meaning unmanaged.The vocabulary of the Pages project’s
placement.modefield, which is whatrepomatic cloudflare-pageswrites the setting through. Anything else would be PATCHed to the live project verbatim and rejected there, far from thepyproject.tomlline that caused it.
- repomatic.config.SITE_DEPLOY_TARGETS: Final[frozenset[str]] = frozenset({'cloudflare-pages', 'github-pages'})¶
Hosts a repository’s built site can be published to.
One deploy job per target, each with the permissions its own host needs, so a value outside this set has no job at all behind it.
Config.__post_init__rejects one rather than letting the workflow run green and publish nothing.
- repomatic.config.deploys_to(site_deploy, target, *, is_sphinx)[source]¶
Whether a repository declaring site_deploy publishes its site to target.
The one routing rule behind both the
lint-repoaudit and the setup guide, which used to carry mirror copies kept in sync by prose. The GitHub Pages half stays gated on Sphinx, because the Docs workflow is the only publisher repomatic runs for that host and it only builds Sphinx trees. The Cloudflare half follows the declaration alone: a repository whose site is built by its own workflow still needs the project and the credentials the checks and the guide cover.- Return type:
- repomatic.config.location_path(location)[source]¶
Normalize a
*.locationconfig value into a bare repo-relative path.The location defaults carry a
./prefix (they read as paths in the reference table) and a directory location a trailing slash; neither belongs in a registry target or anoutput_dir / pathjoin. One normalizer keeps every consumer spelling the same value the same way.
- class repomatic.config.FlavorConfig(agent='claude_code', ci='github_ci')[source]¶
Bases:
objectNested schema for
[tool.repomatic.flavor].Declares which ecosystem repomatic is targeting, so a future decision has one place to branch on instead of a new flag per feature.
Note
Values are trait IDs from extra-platforms, which already models both AI agents and CI systems. Borrowing its vocabulary brings its detection helpers (
current_agent(),is_github_ci()) and its naming along for free, instead of repomatic maintaining a parallel enum.Caution
Defaults are static, never detected. Deriving them from
current_agent()would make a repository’s effective configuration depend on which tool happened to invokerepomaticlast, sorepomatic metadatawould stop being reproducible.- agent: str = 'claude_code'¶
AI coding agent whose asset layout the bundled skills and agents target.
Accepts a
extra_platforms.ALL_AGENTStrait ID present inAGENT_LAYOUTS. Hyphens are normalized, soclaude-codeworks too.
- ci: str = 'github_ci'¶
CI system the bundled workflows target.
Accepts a
extra_platforms.ALL_CItrait ID. Onlygithub_ciis implemented: every bundled workflow is a GitHub Actions workflow, so any other value is rejected rather than quietly emitting the wrong thing.
- property layout: AgentLayout¶
Asset layout for the selected agent.
- class repomatic.config.GitignoreConfig(extra_categories=<factory>, extra_content=<factory>, location='./.gitignore', sync=True)[source]¶
Bases:
objectNested schema for
[tool.repomatic.gitignore].- extra_categories: list[str]¶
Additional gitignore template categories to fetch from gitignore.io.
List of template names (e.g.,
["Python", "Node", "Terraform"]) to combine with the generated.gitignorecontent.
- extra_content: str¶
Content appended at the end of the generated
.gitignorefile.“Appended” describes where the string lands, after the gitignore.io block, not how a downstream value combines with the default above: setting this key replaces that default wholesale, so the entries shown there are lost unless the override repeats them.
repomatic.gitignore.orphaned_rules()catches that for any rule an earlier sync already wrote to disk, but not for one this repository never materialized, so copy the default and extend it rather than writing only the new lines. Reach forextra_categoriesinstead when adding whole gitignore.io templates: that one is additive.The
.cc-writesentry is the one carrying a**/prefix, because it is the one Claude Code does not place at the repository root: the directory is staged beside whichever working directory the session tracks, so a singlecdinto a subtree leaves one there instead. Anchoring it would miss every copy but the root’s.
- class repomatic.config.LabelsConfig(content_rules=<factory>, extra=<factory>, extra_files=<factory>, file_rules=<factory>, sync=True)[source]¶
Bases:
objectNested schema for
[tool.repomatic.labels].- content_rules: dict[str, list[str]]¶
Per-label patterns matched against an issue or pull request’s text.
The
[tool.repomatic.labels.content-rules]table maps each label to the patterns that apply it, evaluated byapply-labelsagainst the title and body. Any one pattern matching applies the label:[tool.repomatic.labels.content-rules] "🥭 mango" = ["mango", "papaya"] "🐛 bug" = []
A bare pattern is a literal keyword, matched case-insensitively on word boundaries; the
/regex/flagsform passes a regex through instead, withi,mandshonored. An entry for a label the bundled defaults also carry replaces the default entry, and an empty list disables it (seerepomatic.labels.DEFAULT_CONTENT_RULES).
- extra: list[dict[str, str | bool | list[str]]]¶
Inline label definitions applied at sync time under the
defaultprofile.Each entry is a mapping carrying
labelmaker’s per-label specification:name(required),color(single color or multi-color list),description,create,update,enforce-case,rename-fromandon-rename-clash. Arename-fromlist renames an existing label in place, preserving its issue and PR associations. Entries are serialized into a temporary TOML file as[[profiles.default.labels]]blocks and applied bylabelmaker apply, so noextra-labels/*.tomlfile needs committing.For label sets that need multiple profiles, commit a hand-written file under
extra-labels/or download one viaextra-filesinstead.
- extra_files: list[str]¶
URLs of additional label definition files (JSON, JSON5, TOML, or YAML).
Each URL is downloaded into
extra-labels/and applied separately bylabelmaker. For inline definitions that need no external file, useextrainstead.
- file_rules: dict[str, list[str]]¶
Per-label globs matched against the paths a pull request changes.
The
[tool.repomatic.labels.file-rules]table maps each label to the globs that apply it, evaluated byapply-labelsagainst the changed files. The label applies when any changed file matches the glob set:[tool.repomatic.labels.file-rules] "🥭 mango" = ["orchard/**", "!orchard/generated/**"]
Globs follow the
minimatchdialect (**crosses directories,{a,b}expands, a leading dot needs no special casing), and a!-prefixed entry subtracts from the label’s other globs the way a.gitignoreline would. An entry for a label the bundled defaults also carry replaces the default entry, and an empty list disables it (seerepomatic.labels.DEFAULT_FILE_RULES).
- class repomatic.config.LintDepsConfig(allow=<factory>, comment_word_threshold=40)[source]¶
Bases:
objectNested schema for
[tool.repomatic.lint-deps].- allow: dict[str, str]¶
Packages that may ship from somewhere other than PyPI, and why.
lint-depsblocks a release whose dependencies do not all resolve from the index its users will install from. A handful of arrangements are legitimate exceptions: a member of the same monorepo published under its own name, a private mirror an internal project genuinely targets. Name each one here, mapped to the reason it is safe:[tool.repomatic] lint-deps.allow = { papaya = "monorepo workspace member, published separately" }
A mapping rather than a list, deliberately: the reason is the point. An exemption without one is indistinguishable from a forgotten development shortcut six months later, which is the exact thing this gate exists to catch. The reason renders in the report and in the release PR banner, so an accepted exception stays visible instead of disappearing.
Per-package only, with no global off switch, following
exclude-newer-package: an exemption narrow enough to name is one somebody weighed. Listing a package does not silence its transitive dependencies, which stay gated on their own.
- comment_word_threshold: int = 40¶
Word count above which
lint-depswarns about a floor comment.A floor comment justifies the version in force: what breaks below it, and where the project would notice. It is not a running log of every earlier floor, which is what it turns into when each bump appends a paragraph and deletes nothing.
lint-depsemits a non-fatal warning for every comment longer than this many words. Set to0to disable the check.It starts at the same 40 words as
changelog.bullet-word-threshold, and stays an independent knob: both cap a paragraph written for a reader who came looking for one fact, but a project that wants its floors terser than its release notes says so here alone.
- class repomatic.config.MetricsConfig(charts=<factory>, colors=<factory>, forges=<factory>, predecessors=<factory>, skip=<factory>, store='./docs/assets/metrics.csv', subjects=<factory>, sync=False)[source]¶
Bases:
objectNested schema for
[tool.repomatic.metrics].- charts: list[dict[str, str | list[str]]]¶
Charts to draw from the accumulated history, one array-of-tables entry each.
Each entry carries an
outputpath, an optionalmetric(starsby default, and only a metric the store accrues can be charted), an optionalmode(absolute, the default, orrelative) measuring the horizontal axis, an optionalscale(linear, the default, orlogarithmic) measuring the vertical one, an optionalonlylist naming the subjects to plot in draw order, and an optionaltitleused as the chart’s accessible name:[[tool.repomatic.metrics.charts]] output = "./docs/assets/star-history.svg" [[tool.repomatic.metrics.charts]] mode = "relative" output = "./docs/assets/star-history-by-age.svg" [[tool.repomatic.metrics.charts]] only = [ "apricot" ] output = "./docs/assets/star-history-apricot.svg" [[tool.repomatic.metrics.charts]] scale = "logarithmic" output = "./docs/assets/star-history-compared.svg"
The two axes are independent, and a chart comparing projects of different sizes usually wants both:
mode = "relative"slides every curve onto a common origin, andscale = "logarithmic"keeps the smallest of them off the axis.An entry omitting
onlyplots every declared subject. Declaring none of these leaves the history accruing with nothing drawn from it, which is a valid way to collect first and decide later.
- colors: dict[str, list[str]]¶
Per-subject
[light, dark]hex pairs overriding the positional palette.Hues are assigned from
repomatic.metric_chart.SERIES_PALETTEin draw order, so a subject keeps its colour as long as the order holds. Pin one here when it must survive a reordering, or when a chart plots more curves than the palette holds:[tool.repomatic.metrics.colors] apricot = [ "#2a78d6", "#3987e5" ]
- forges: dict[str, str]¶
Self-hosted forge instances, mapping each host to the software it runs.
Merged over
repomatic.forge.FORGE_APIS, which only knows the three public hosts. A self-hosted instance is never guessed from its name, so an undeclared host raises rather than sampling nothing:[tool.repomatic.metrics.forges] "gitlab.example.org" = "gitlab" "codeberg.example.org" = "forgejo"
Values are
forgejo,githuborgitlab; Gitea instances read asforgejo, whose API they share.
- predecessors: dict[str, str]¶
Retired forerunners, mapping the subject they precede to their own repository.
A project that reopened under a new repository carries an audience it inherited rather than one it gathered, which a by-age chart would otherwise misreport as the fastest start in the field:
[tool.repomatic.metrics.predecessors] papaya = "old-owner/papaya"
Drawn in the successor’s own hue to tie the two together, but dashed and never joined to it: the counts are independent tallies on separate repositories, so a continuous line would claim a running total no repository ever showed. The forerunner’s line stops where its successor’s begins.
- skip: dict[str, str]¶
Subjects deliberately left unmeasured, mapped to the reason why.
A mapping rather than a list, following
lint-deps.allow: the reason is the point. A project absent from both tables is an oversight a conformance test can report, while one listed here is a decision:[tool.repomatic.metrics.skip] papaya = "Ships in a distribution package with no public repository."
Nothing is sampled for them, and whatever renders the readings leaves their cells empty.
- store: str = './docs/assets/metrics.csv'¶
Where the readings accumulate, one row per subject, metric and date.
- subjects: dict[str, str]¶
Repositories to track, mapping each subject name to its repository.
The name labels the curve and keys its colour, so it is what a reader sees. A bare
owner/nameis GitHub; anything else is a full URL on whichever forge hosts it:[tool.repomatic.metrics.subjects] apricot = "apricot-org/apricot" papaya = "https://gitlab.com/papaya/papaya"
Every subject is read for every metric its forge answers. The two deep collectors are GitHub-only and skip the rest with a note: an exact star reconstruction reads per-star timestamps, and the archive backfill mines
github.compages.
- class repomatic.config.SyncRunnerImagesConfig(ignore=<factory>)[source]¶
Bases:
objectNested schema for
[tool.repomatic.sync-runner-images].- ignore: list[str]¶
Runner labels never to propose, whatever GitHub announces about them.
A
sync-*job regenerates on every push, so a proposal declined by closing its pull request comes back on the next one. Without somewhere to record the decision, the only way to stop a proposal already considered and rejected is to disable the whole operation. Naming the label here is the one-line commit that makes a “no” stick:[tool.repomatic.sync-runner-images] # 26.04 stays out until its capacity settles: queue time matters more here # than the compute it wins. ignore = [ "ubuntu-26.04", "ubuntu-26.04-arm" ]
Applies to both shapes: an ignored label is neither probed when it arrives nor proposed as a successor when something retires onto it.
- class repomatic.config.TestMatrixConfig(exclude=<factory>, full_include=<factory>, include=<factory>, remove=<factory>, replace=<factory>, unstable=<factory>, variations=<factory>)[source]¶
Bases:
objectNested schema for
[tool.repomatic.test-matrix].Keys inside
replaceandvariationsare GitHub Actions matrix identifiers (e.g.,os,python-version) and must not be normalized to snake_case. Click Extra’sclick_extra.normalize_keys = Falsemetadata on the parent field prevents this.- exclude: list[dict[str, str]]¶
Extra exclude rules applied to both full and PR test matrices.
Each entry is a dict of GitHub Actions matrix keys (like
{"os": "windows-11-arm"}) that removes matching combinations. Additive to the upstream default excludes.
- full_include: list[dict[str, str]]¶
Full-matrix-only job rows, added as standalone matrix combinations.
Each entry is a dict of GitHub Actions matrix keys fully describing one job (like {“os”: “ubuntu-26.04-arm”, “python-version”: “3.10”, “click-version”: “8.3.1”}`). Unlike``include`, these are appended as independent rows of the full matrix, never merged into the base cross-product, so a cell can’t overwrite a shipped-config job that shares its
osandpython-version. Keys left out inherit the matrix defaults (the single-keyincludeentries, plusstate: stable), so a cell lists only what differs from the shipped configuration.Use this for heterogeneous coverage, like pinning each release of a dependency to its own runner and Python, where carving the same shape from the base cross-product with
excludewould take many rules. Likevariationsandunstable, it touches the full matrix only; the PR matrix stays a curated reduced set. Adding any entry makes the full matrix emit as a flat job list ({"include": [...]}), which GitHub runs verbatim with no cross-product expansion.
- include: list[dict[str, str]]¶
Extra include directives applied to both full and PR test matrices.
Each entry is a dict of GitHub Actions matrix keys that adds or augments matrix combinations. Additive to the upstream default includes.
Because includes apply to both matrices, a directive whose keys are not PR base axes is risky. In the PR matrix only
osandpython-versionare base axes, so a key likeclick-version(injected by another include) has nothing to match and GitHub’s expansion adds the directive to every PR job, overwriting it. To flag a value continue-on-error, preferunstableover anincludecarryingstate: unstable.
- remove: dict[str, list[str]]¶
Per-axis value removals applied to both full and PR test matrices.
Outer key is the variation/axis ID (e.g.,
os,python-version). Inner list contains values to drop from that axis. Applied after replacements but before excludes, includes, and variations.
- replace: dict[str, dict[str, str]]¶
Per-axis value replacements applied to both full and PR test matrices.
Outer key is the variation/axis ID (e.g.,
os,python-version). Inner dict maps old values to new values. Applied before removals, excludes, includes, and variations.
- unstable: list[dict[str, str]]¶
Full-matrix-only combinations to flag continue-on-error in CI.
Each entry is a dict of GitHub Actions matrix keys (like
{"click-version": "main"}). Every full-matrix combination matching an entry gets astate: unstablevalue, whichtests.yamlreads to setcontinue-on-error. Likevariations, this applies to the full matrix only; the PR matrix stays a curated stable set.Prefer this over an
includeentry carryingstate: unstable.includeapplies to both matrices, and in the PR matrix a key likeclick-versionis not a base axis (anotherincludeinjects it), so GitHub’s expansion would add the directive to every PR job and overwrite it.unstableonly touches the full matrix, sidestepping that hijack.
- variations: dict[str, list[str]]¶
Extra matrix dimension values added to the full test matrix only.
Each key is a dimension ID (e.g.,
os,click-version) and its value is a list of additional entries. For existing dimensions, values are merged with the upstream defaults. For new dimension IDs, a new axis is created. Only affects the full matrix; the PR matrix stays a curated reduced set.
- class repomatic.config.VulnerableDepsConfig(sources=<factory>, sync=True)[source]¶
Bases:
objectNested schema for
[tool.repomatic.vulnerable-deps].- sources: list[str]¶
Advisory databases to consult for known vulnerabilities.
Recognized values:
"uv-audit": PyPA Advisory Database viauv audit(works locally and in CI without a GitHub token)."github-advisories": GitHub Advisory Database via the repository’s Dependabot alerts (CI-only, requires a token withDependabot alerts: Read-only).
Sources are unioned and deduplicated per package by advisory identity: entries sharing an
advisory_idor a cross-referenced CVE/GHSA/PYSEC alias are merged. Repositories that distrust GHSA, or have no Dependabot alerts enabled, can opt out withsources = ["uv-audit"].
- class repomatic.config.WorkflowConfig(source_paths=None, extra_paths=<factory>, ignore_paths=<factory>, paths=<factory>, sync=True)[source]¶
Bases:
objectNested schema for
[tool.repomatic.workflow].- source_paths: list[str] | None = None¶
Source code directory names for workflow trigger
paths:filters.When set, thin-caller and header-only workflows include
paths:filters using these directory names (asname/**globs) alongside universal paths likepyproject.tomlanduv.lock.When
None(default), source paths are auto-derived from[project.name]inpyproject.tomlby replacing hyphens with underscores, the universal Python convention. For example,name = "extra-platforms"automatically uses["extra_platforms"].
- extra_paths: list[str]¶
Literal entries to append to every workflow’s
paths:filter.Applies to thin-caller and header-only sync. Useful for repo-specific files that should re-trigger CI but are not detected by the canonical
paths:filter (e.g.,install.sh,dotfiles/**).Per-workflow overrides in
pathsignore this list: when an entry exists for a given filename, that entry is treated as the complete list.
- ignore_paths: list[str]¶
Literal entries to strip from every workflow’s
paths:filter.Useful for canonical entries that don’t exist downstream (e.g.,
tests/**,uv.lockin repos with no Python tests or lockfile). Match is by exact string equality. Applies beforeextra_paths.Per-workflow overrides in
pathsignore this list.
- paths: dict[str, list[str]]¶
Per-workflow override of the
paths:filter, keyed by filename.When a workflow filename appears here, its
paths:blocks (inpush,pull_request, etc.) are replaced wholesale with the listed entries.source_paths,extra_paths, andignore_pathsdo not apply when a per-workflow override is set: the list is treated as authoritative.Override only takes effect on triggers that already have a
paths:filter in the canonical workflow. Workflows withoutpaths:upstream keep their unrestricted trigger semantics.Example:
[tool.repomatic.workflow.paths] "tests.yaml" = ["install.sh", "packages.toml", ".github/workflows/tests.yaml"]
- class repomatic.config.Config(abandoned_versions=<factory>, action_pins_sync=True, awesome_template_sync=True, binaries_sync=True, bumpversion_sync=True, cache=<factory>, changelog_archive_location='', changelog_bullet_word_threshold=40, changelog_location='./changelog.md', debug_sync=False, dep_sources_sync=True, dependency_graph=<factory>, dev_release_sync=True, docs=<factory>, exclude=<factory>, flavor=<factory>, gitignore=<factory>, include=<factory>, labels=<factory>, lint_deps=<factory>, mailmap_sync=True, manpages_asset_name='', manpages_script='', metrics=<factory>, minimum_release_age='1 week', notification_unsubscribe=False, nuitka_dev_targets=<factory>, nuitka_enabled=True, nuitka_entry_points=<factory>, nuitka_extras=<factory>, nuitka_nofollow_imports=<factory>, nuitka_unstable_targets=<factory>, pypi_package_history=<factory>, release_assets=<factory>, settings_location='./.claude/settings.json', setup_guide=True, site_cloudflare_compatibility_date='', site_cloudflare_placement='', site_cloudflare_project='', site_deploy='github-pages', skills_location='./.claude/skills/', sphinx_builder='html', subagents_location='./.claude/agents/', sync_runner_images=<factory>, test_matrix=<factory>, tool_versions_sync=True, uv_lock_sync=True, vulnerable_deps=<factory>, workflow=<factory>, workflow_pins_sync=True)[source]¶
Bases:
objectConfiguration schema for
[tool.repomatic]inpyproject.toml.This dataclass defines the structure and default values for repomatic configuration. Each field has a docstring explaining its purpose.
- abandoned_versions: list[str]¶
Versions documented in the changelog but never published.
A version reached only its
[changelog] Release vX.Y.Zfreeze and was then skipped perCLAUDE.md§ Skip and move forward (botched build, broken artifact, bad metadata) without rewriting history. List those versions here solint-changelogreports them as skipped (an info log line) instead of flagging them every run as⚠ X.Y.Z: not found on PyPI. Applies to both PyPI lookups and the git-tag fallback.
- action_pins_sync: bool = True¶
Whether the
sync-action-pinsjob is enabled for this project.Bumps SHA-pinned GitHub Actions (
uses: owner/repo@<sha> # vX.Y.Z) to the latest release passing theminimum-release-agecooldown. Projects that pin actions by hand can set this tofalse.
- awesome_template_sync: bool = True¶
Whether awesome-template sync is enabled for this project.
Repositories whose name starts with
awesome-get their boilerplate synced from files bundled inrepomatic. Set tofalseto opt out.
- binaries_sync: bool = True¶
Whether the release pipeline records released binaries into the repository.
When enabled, the
scan-virustotalrelease job regenerates the binaries catalog (docs/binaries.mdanddocs/assets/binaries.csv) and publishes it, along with the scan history (docs/assets/virustotal-scans.csv), through one long-lived pull request that each release appends to: the contract documented in docs/operation-contracts.md. Set tofalseto keep the repository untouched: binaries are still scanned on VirusTotal (seeding AV vendor databases), but no catalog page, CSV, or scan record is published.
- bumpversion_sync: bool = True¶
Whether bumpversion config sync is enabled for this project.
Projects that manage their own
[tool.bumpversion]section and do not want the autofix job to overwrite it can set this tofalse.
- cache: CacheConfig¶
Binary cache configuration.
- changelog_archive_location: str = ''¶
File path of the changelog archive, relative to the root of the repository.
The archive holds older release sections split out of the live changelog to keep it small. Empty (the default) disables archive handling.
When set,
lint-changelogtreats versions documented in the archive as present, so they are neither reported nor re-inserted as orphans (versions found on PyPI, GitHub, or git tags but missing from the changelog). The archive is frozen: its released entries are immutable and are not re-validated against their canonical release dates.
- changelog_bullet_word_threshold: int = 40¶
Word count above which
lint-changelogwarns about a changelog bullet.A changelog entry is a release note, not a commit message: ideally one short sentence stating what changed (see
CLAUDE.md§ Changelog entry length).lint-changelogemits a non-fatal warning for every bullet in the unreleased section longer than this many words, nudging verbose, implementation-heavy entries back toward a user-facing summary. Released sections are immutable and never flagged. Set to0to disable the check.
- changelog_location: str = './changelog.md'¶
File path of the changelog, relative to the root of the repository.
- debug_sync: bool = False¶
Whether the
debug.yamlworkflow is deployed to this project.Opt-in: the workflow dumps the GitHub contexts and the runner’s host probes across every build target, which answers a question a maintainer asks while chasing a runner difference and nothing else reads. Left on by default it spends a monthly matrix of runners, the scarce macOS and Windows ones included, producing logs nobody opens.
- dep_sources_sync: bool = True¶
Whether the
sync-dep-sourcesupdater is enabled for this project.Swaps a dependency tracked from a git branch back to its released version once the release named by its
.devversion floor ships on PyPI (seerepomatic.deps.dep_sourcesfor the managed idiom). Projects that manage[tool.uv.sources]overrides by hand can set this tofalse.
- dependency_graph: DependencyGraphConfig¶
Dependency graph generation configuration.
- dev_release_sync: bool = True¶
Whether dev pre-release sync is enabled for this project.
Projects that do not want a rolling draft pre-release maintained on GitHub can set this to
false.
- docs: DocsConfig¶
Sphinx documentation generation configuration.
- exclude: list[str]¶
Additional components and files to exclude from repomatic operations.
Additive to the default exclusions (
agents,labels,skills). Bare names exclude an entire component (e.g.,"workflows"). Qualifiedcomponent/identifierentries exclude a specific file within a component (e.g.,"workflows/autolock.yaml","skills/repomatic-audit","labels/labels.toml").Affects
repomatic init,workflow sync, andworkflow create. Explicit CLI positional arguments override this list.
- flavor: FlavorConfig¶
Which agent and CI ecosystem this repository targets.
- gitignore: GitignoreConfig¶
.gitignoresync configuration.
- include: list[str]¶
Components and files to force-include, overriding default exclusions.
Use this to opt into components that are excluded by default (
agents,labels,skills). Each entry is subtracted from the effective exclude set (defaults + userexclude) and bypassesRepoScopefiltering, so scope-restricted components (like awesome-only skills or Python-onlypublish-pypi-action) are included regardless of repository type. Qualified entries (component/file) implicitly select the parent component. Same syntax asexclude.
- labels: LabelsConfig¶
Repository label sync configuration.
- lint_deps: LintDepsConfig¶
Dependency shippability gate configuration.
- mailmap_sync: bool = True¶
Whether
.mailmapsync is enabled for this project.Projects that manage their own
.mailmapand do not want the autofix job to overwrite it can set this tofalse.
- manpages_asset_name: str = ''¶
Filename stem (without the
.tar.gzextension) for the man-page tarball uploaded to the GitHub release.Defaults to
<package-name>-manpageswhen left empty andmanpages.scriptis set. Has no effect whenmanpages.scriptis empty.
- manpages_script: str = ''¶
Click command target whose tree gets rendered as roff
.1files and attached as a tarball asset on every GitHub release.Same shape the
click-extra wrap --manCLI accepts: amodule:functionpath (preferred for projects whose console-script entry point dispatches through a wrapper), an entry-point name, a.pyfile path, or a plain importable module name. Leave empty to disable release-attached man pages.
- metrics: MetricsConfig¶
What forges say about the repositories this project tracks, over time.
- minimum_release_age: str = '1 week'¶
Stabilization window before a new upstream release is adopted.
Shared cooldown for the
sync-tool-versions,sync-action-pins, andsync-workflow-pinsjobs: a release is only proposed once it has been public for at least this long, giving upstream time to yank a bad cut. It also gatesrepomatic run’s ad-hoc installs at run time, so their transitive trees honor the same window:uvxtools via uv’s--exclude-newer, npm tools via npm’smin-release-age.repomatic inithonors it too: the derived upstream workflow pin steps back to the newest release past the window (override with--no-cooldown). The GitHub/PyPI/npm counterpart to uv’sexclude-newer(which guardssync-uv-lock). Accepts the same friendly durations (8 days,2 weeks,36 hours). Set to0 daysto adopt releases immediately.
- notification_unsubscribe: bool = False¶
Whether the unsubscribe-threads workflow is enabled.
Notifications are per-user across all repos. Enable on the single repo where you want scheduled cleanup of closed notification threads. Requires a classic PAT with
notificationsscope stored asREPOMATIC_NOTIFICATIONS_PAT.
- nuitka_dev_targets: list[str]¶
Nuitka build targets compiled on ordinary pushes, as a canary.
An ordinary push to the default branch rebuilds binaries only for these targets: enough to catch a compilation break early, while freeing runner slots the full fleet would occupy on every code push just to refresh the rolling dev pre-release (a draft). The full target roster still builds on release commits, on the weekly
scheduletrigger, and onworkflow_dispatch. Defaults to["linux-arm64"], the fastest and cheapest builder. Set to[]to skip dev builds entirely.
- nuitka_enabled: bool = True¶
Whether Nuitka binary compilation is enabled for this project.
Projects with
[project.scripts]entries that are not intended to produce standalone binaries (e.g., libraries with convenience CLI wrappers) can set this tofalseto opt out of Nuitka compilation.
- nuitka_entry_points: list[str]¶
Which
[project.scripts]entry points produce Nuitka binaries.List of CLI IDs (e.g.,
["mpm"]) to compile. When empty (the default), deduplicates by callable target: keeps the first entry point for each uniquemodule:callablepair. This avoids building duplicate binaries when a project declares alias entry points (like bothmpmandmeta-package-managerpointing to the same function).
- nuitka_extras: list[str]¶
[project.optional-dependencies]extras to install before the Nuitka build.List of extra names (like
["sbom"]) to sync into the build venv before invoking Nuitka. By default the binary build only sees the project’s base dependencies, which matches a barepip install <package>and excludes optional features. Listing an extra here calls uv sync –frozen –extra <name> before the Nuitka build so the binary can bundle the optional feature’s third-party packages (paired with--include-packagein[tool.nuitka]for imports guarded behindtry/except).
- nuitka_nofollow_imports: list[str]¶
Module names Nuitka must not follow into the compiled binary.
Each name is forwarded as a
--nofollow-import-toflag by repomatic run nuitka`. Defaults to``[“tkinter”]``:boltons.ecoutils` (in the dependency tree of every click-extra CLI) probes tkinter inside a guarded ``try/exceptimport, which otherwise drags the whole Tcl/Tk stack into every binary. Excluded modules raiseImportErrorwhen imported at run time, which guarded imports absorb. GUI projects that really ship tkinter can set this to[].
- nuitka_unstable_targets: list[str]¶
Nuitka build targets allowed to fail without blocking the release.
List of target names (e.g.,
["linux-arm64", "windows-x64"]) that are marked as unstable. Jobs for these targets will be allowed to fail without preventing the release workflow from succeeding.
- pypi_package_history: list[str]¶
Former PyPI package names for projects that were renamed.
When a project changes its PyPI name, older versions remain published under the previous name. List former names here so
lint-changelogcan fetch release metadata from all names and generate correct PyPI URLs.
- release_assets: list[str]¶
Extra asset filenames attached to every GitHub release.
Each listed file must be produced by a job the consumer defines in its own release workflow (alongside the
buildlane the engine call already gates on) and uploaded as a run artifact namedrelease-asset-<filename>. The engine’sextra-assetsjob downloads the artifacts, attests them with the same provenance chain as the compiled binaries, and attaches them to the release draft before publication locks it (GitHub immutable releases).The build code stays in the downstream repository as regular workflow code, reviewed and linted there: the engine never executes consumer-supplied commands. Filenames must be space-free, as they travel through a space-separated job environment variable. Leave empty to disable, which keeps the job silent.
- settings_location: str = './.claude/settings.json'¶
Path to the agent’s project settings file, relative to the repository root.
Left unset, it follows
[tool.repomatic.flavor] agent; setting it explicitly overrides that.Only the
plugincomponent writes here, merging the marketplace and enablement keys it owns into whatever the file already holds.
- setup_guide: bool = True¶
Whether the setup guide issue is enabled for this project.
Projects that do not need
REPOMATIC_PATor manage their own PAT setup can set this tofalseto suppress the setup guide issue.
- site_cloudflare_compatibility_date: str = ''¶
Workers runtime date the Cloudflare Pages project is pinned to.
A
YYYY-MM-DDdate, compared and enforced byrepomatic cloudflare-pagesagainst the live project’sdeployment_configs, on both the production and preview environments. Inert while the project has no Pages Functions, which is exactly how it drifts unnoticed: the value only starts mattering the moment a Function is added, long after anyone last chose it. Empty (the default) leaves the live value unmanaged.This is server-side state, not the
wrangler.tomlkey of the same name: Cloudflare honours the project’s own configuration, and the file only matters to a build that a Direct Upload project never runs.lint-repowarns when a committedwrangler.tomldisagrees, so the repository states one value rather than two.
- site_cloudflare_placement: str = ''¶
Smart Placement mode declared for the Cloudflare Pages project.
smartoroff, compared and enforced byrepomatic cloudflare-pageson both environments. For a static site it changes nothing measurable and costs nothing; declaring it means the dashboard toggle stops looking like an accident. Empty (the default) leaves the live value unmanaged.
- site_cloudflare_project: str = ''¶
Name of the Cloudflare Pages project the site deploys into.
Empty (the default) names the project after the repository, which is what the deploy job falls back to. Set it when the project predates repomatic or otherwise cannot carry the repository’s name: renaming a live Pages project would move the
<project>.pages.devhostname every custom domain CNAMEs through.
- site_deploy: str = 'github-pages'¶
Where this repository’s built site is published.
github-pages, the default, has the Docs workflow upload the Sphinx tree as a Pages artifact and deploy it with the repository’s own OIDC identity: no stored credential, and nothing to configure beyond enabling Pages.cloudflare-pagesuploads it to a Cloudflare Pages project instead, named persite.cloudflare-project, throughwrangler pages deploy. That path needs one repository secret,CLOUDFLARE_API_TOKEN, and it trades the OIDC deploy for a long-lived token: the Docs workflow’s monthly run is what surfaces its expiry, since Cloudflare warns about neither an approaching lapse nor a passed one.A property of the site rather than of Sphinx. A repository whose site is built by its own workflow (a Pelican blog, a hand-rolled static tree) declares the target here too: that is what turns on the credential checks, the setup-guide step and the Cloudflare drift job for it, even though the Docs workflow’s own Sphinx build never runs.
Choose Cloudflare for what the edge can do rather than for speed. A custom domain on Cloudflare Pages carries its own certificate, so the zone’s apex can be proxied, which is what a
_redirectsfile, a real404.htmland any edge rule on the apex all depend on.
- skills_location: str = './.claude/skills/'¶
Directory prefix for skill folders, relative to the repository root.
Left unset, it follows
[tool.repomatic.flavor] agent; setting it explicitly overrides that.Skill files are written as
{skills_location}/{skill-id}/SKILL.md. Useful for repositories where.claude/is not at the root (like dotfiles repos that store configs under a subdirectory).
- sphinx_builder: str = 'html'¶
Sphinx builder producing the deployed documentation site.
The default
htmlwritespage.html, so the site serves/page.html. Setting it todirhtmlwritespage/index.htmlinstead, so the same page serves at/page/and the published URLs carry no extension, which is the shape search engines and most static hosts expect.The one Sphinx setting a project cannot make in its own
conf.py, hence a config key: the builder is chosen on the command line, anddocs.yamlis what runs it. Switching an already-published site republishes every URL it has: the old paths stop existing, so the repository’s own absolute self-links (readme, packaging specs) move in the same commit, and whatever fronts the site redirects the old ones.
- subagents_location: str = './.claude/agents/'¶
Directory prefix for subagent definitions, relative to the repository root.
Left unset, it follows
[tool.repomatic.flavor] agent; setting it explicitly overrides that.Subagent files are written as
{subagents_location}/{agent-id}.md. Useful for repositories where.claude/is not at the root (like dotfiles repos that store configs under a subdirectory).
- sync_runner_images: SyncRunnerImagesConfig¶
Runner image pull request configuration.
- test_matrix: TestMatrixConfig¶
Per-project customizations for the GitHub Actions CI test matrix.
Keys inside this section are GitHub Actions matrix identifiers (e.g.,
os,python-version) and must not be normalized to snake_case.
- tool_versions_sync: bool = True¶
Whether the
sync-tool-versionsjob is enabled for this project.Bumps every tool in the
repomatic runregistry to the latest release passing theminimum-release-agecooldown (GitHub releases for binary tools, PyPI for the rest), recomputing binary checksums in the same pass. Projects that pin tool versions by hand can set this tofalse.
- uv_lock_sync: bool = True¶
Whether
uv.locksync is enabled for this project.Projects that manage their own lock file strategy and do not want the
sync-uv-lockjob to runuv lock --upgradecan set this tofalse.
- vulnerable_deps: VulnerableDepsConfig¶
Vulnerable dependency detection and remediation configuration.
- workflow: WorkflowConfig¶
Workflow sync configuration.
- workflow_pins_sync: bool = True¶
Whether the
sync-workflow-pinsjob is enabled for this project.Bumps version literals embedded in workflow YAML (npm
pkg@xinstalls anduvx '<pkg>==x'PyPI pins) to the latest release passing theminimum-release-agecooldown. Projects that pin these by hand can set this tofalse.
- repomatic.config.SUBCOMMAND_CONFIG_FIELDS: Final[frozenset[str]] = frozenset({'abandoned_versions', 'action_pins_sync', 'awesome_template_sync', 'bumpversion_sync', 'cache', 'changelog_archive_location', 'changelog_bullet_word_threshold', 'changelog_location', 'debug_sync', 'dep_sources_sync', 'dependency_graph', 'dev_release_sync', 'docs', 'exclude', 'flavor', 'gitignore', 'include', 'labels', 'lint_deps', 'mailmap_sync', 'metrics', 'minimum_release_age', 'notification_unsubscribe', 'nuitka_enabled', 'nuitka_nofollow_imports', 'pypi_package_history', 'settings_location', 'setup_guide', 'site_cloudflare_compatibility_date', 'site_cloudflare_placement', 'skills_location', 'subagents_location', 'sync_runner_images', 'test_matrix', 'tool_versions_sync', 'uv_lock_sync', 'vulnerable_deps', 'workflow', 'workflow_pins_sync'})¶
Config fields consumed directly by subcommands, not needed as metadata outputs.
These fields are read directly from
[tool.repomatic]inpyproject.tomlby their respective subcommands (e.g.dep-graph), so they no longer need to be passed through workflow metadata outputs.
- repomatic.config.escape_type_for_gfm_table(ftype)[source]¶
Escape outer brackets of nested generics for raw GFM table cells.
Nested generics like
list[dict[str, str]]would otherwise be interpreted by mdformat as a markdown link reference and re-escaped on every reformat. Escaping the outermost brackets up front keeps the cell stable under mdformat. Simple generics likelist[str]have no nested brackets and stay unescaped.Apply this only when the value lands directly in a raw GFM table cell (e.g. CLI
show-configoutput). Do not apply when wrapping the value in inline code backticks: inside a code span, backslashes are literal characters in CommonMark and would render visibly as\[.- Return type:
- repomatic.config.CONFIG_REFERENCE_HEADER_DEFS: tuple[tuple[str, str], ...] = (('Option', 'option'), ('Type', 'type'), ('Default', 'default'), ('Description', 'description'))¶
Column definitions for the
[tool.repomatic]configuration reference table.
- repomatic.config.config_reference()[source]¶
Build the
[tool.repomatic]configuration reference as table rows.Introspection comes from click-extra’s
schema_field_infos()(dotted kebab-case keys, type annotations, defaults, attribute-docstring summaries); this wrapper only applies the Markdown presentation of theshow-configtable. Returns a list of(option, type, default, description)tuples suitable forclick_extra.table.print_table.
- repomatic.config.load_repomatic_config(pyproject_data=None)[source]¶
Load
[tool.repomatic]config merged withConfigdefaults.Delegates to click-extra’s schema-aware dataclass instantiation, which handles normalization, flattening, nested dataclasses, and opaque field extraction automatically based on field metadata and type hints.
Loads are memoized per parsed document (see
_CONFIG_CACHE), so treat the returned instance as read-only.