Skip to content

Regex

Regex directives came after anchors and flipped the relationship: instead of the provider owning what a region says, the project file is the source of truth. A regex directive is a contract — "the provider promises to respect whatever text in your file matches this pattern." You edit the final document directly; no detour through repolish.yaml.

The classic cases are values repolish can't know: a description = "..." line only the project can write, a version that other tools bump between applies.

The honest tradeoff: writing the pattern is the painful part. That's why repolish preview exists — an interactive debugging session over a single template, no project setup required. Every example on this page was validated with it.

Syntax

## repolish:regex[version] ^__version__\s*=\s*"(.+?)"$
__version__ = "0.0.0"
  • The directive line sits directly above the line it manages and is always stripped from the output.
  • If the pattern has a capture group, the first group is the preserved value; with no group, the entire match is used. Prefer explicit groups.
  • On apply, the pattern runs against your current file. If it matches, the captured text replaces the match in the template; if not, the template default ships unchanged.
  • Supports the |after-render phase — necessary when the line the directive manages is produced by Jinja (loops, conditionals).

Worked example

Preserving version and description in a provider-managed pyproject.toml:

repolish/pyproject.toml.jinja:

[project]
name = "mylib"
## repolish:regex[version] ^version\s*=\s*"(.+?)"$
version = "0.0.0"
## repolish:regex[description] ^description\s*=\s*"(.*?)"$
description = ""

pyproject.toml — values you (or your tooling) already set:

[project]
name = "mylib"
version = "1.4.2"
description = "A library for things"

Save this as scratch.yaml:

template: |
  [project]
  name = "mylib"
  ## repolish:regex[version] ^version\s*=\s*"(.+?)"$
  version = "0.0.0"
  ## repolish:regex[description] ^description\s*=\s*"(.*?)"$
  description = ""

target: |
  [project]
  name = "mylib"
  version = "1.4.2"
  description = "A library for things"

and run:

repolish preview scratch.yaml

The provider can restructure the rest of the file freely; these two lines stay yours, on every apply:

[project]
name = "mylib"
version = "1.4.2"
description = "A library for things"

The contract is two-sided

The pattern must match both files: your current file (to capture from) and the template itself (to find the region to replace). A pattern that doesn't match the template's own default silently falls back to that default.

The subtle instance: "(.+?)" requires at least one character between the quotes, so it cannot match the template default description = "" — the directive above therefore uses "(.*?)". repolish preview surfaces this instantly (a regex_matched_in_target event with unchanged output is the tell); a full apply would leave you guessing.

The trim safeguard

As a conservative guard, repolish trims a captured block to the contiguous same-indentation region — this stops a greedy multiline pattern from pulling the next section of the file into the capture. When in doubt, tighten the pattern instead of relying on the guard.

The guard has one real surprise, with append-zone directives. For a pattern like:

## repolish:regex[project-ignores] ^## project-specific patterns[^\n]*\n([\s\S]*)$
## project-specific patterns - add your own below

the [^\n]*\n before the capture group matters: it makes the capture start after the sentinel line's newline, so its first line is your appended content at column 0 and the trim guard keeps it. If the capture starts mid-sentinel (^## project-specific patterns([\s\S]*)$), the sentinel's trailing text anchors the indentation and column-0 entries get trimmed away — the directive appears to do nothing. (Indented content, like extra YAML keys under a sentinel, survives either way.)

Iterating with preview

Write a small YAML debug file and run it until the pattern does what you mean:

scratch.yaml
template: |
  ## repolish:regex[version] ^version\s*=\s*"(.+?)"$
  version = "0.0.0"

target: |
  version = "1.4.2"
repolish preview scratch.yaml
repolish preview scratch.yaml --show-patterns --show-steps -vv

--show-steps -vv shows each family's pass and which regexes matched, with the captured values — the fastest feedback loop for directive work. Full format in repolish preview.

Limits

Regex adoption is single-occurrence: with loop-generated duplicates only the first rendered line is reconciled against your file. When you find yourself preserving a region rather than a value, keep blocks are the better tool — no pattern to write, and occurrence pairing is handled for you. See the uncovered regions map for the planned follow-ups.