Skip to content

Quick Start

Repolish is nothing without a provider. This guide builds a minimal local provider from scratch - one that scans your .github/workflows/ directory and generates a WORKFLOWS.md summary table. The same pattern works for any file-discovery task.

What you will build

my-project/
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── local/
│   ├── repolish.py               ← provider logic
│   └── repolish/
│       └── WORKFLOWS.md.jinja    ← template
├── repolish.yaml
└── WORKFLOWS.md                  ← generated by repolish

1. Write the provider

Create local/repolish.py:

import yaml
from pathlib import Path
from pydantic import BaseModel
from repolish import BaseContext, Provider


class Workflow(BaseModel):
    name: str
    file: str
    triggers: list[str]


class Ctx(BaseContext):
    workflows: list[Workflow] = []


def _parse_triggers(on_val: object) -> list[str]:
    if isinstance(on_val, str):
        return [on_val]
    if isinstance(on_val, list):
        return [str(t) for t in on_val]
    if isinstance(on_val, dict):
        return list(on_val.keys())
    return []


def _scan_workflows() -> list[Workflow]:
    workflows = []
    for yml_path in sorted(Path('.github/workflows').glob('*.yml')):
        raw = yaml.safe_load(yml_path.read_text())
        name = raw.get('name', yml_path.stem)
        # PyYAML parses bare `on` as the boolean True
        on_val = raw.get(True, raw.get('on', {}))
        workflows.append(Workflow(name=name, file=yml_path.name, triggers=_parse_triggers(on_val)))
    return workflows


class WorkflowSummaryProvider(Provider[Ctx, BaseModel]):
    def create_context(self) -> Ctx:
        return Ctx(workflows=_scan_workflows())

BaseContext gives the provider access to the built-in repolish namespace (repo name, year, etc.). The Ctx model is the data contract between your Python logic and your Jinja2 templates.

2. Write the template

Create local/repolish/WORKFLOWS.md.jinja:

# Workflows

<!-- Auto-generated by repolish - do not edit directly -->

| Workflow | File | Triggers |
| -------- | ---- | -------- |
{% for wf in workflows -%}
| {{ wf.name }} | `{{ wf.file }}` | `{{ wf.triggers | join('`, `') }}` |
{% endfor %}

Repolish strips the .jinja extension automatically, so this template renders to WORKFLOWS.md.

3. Configure repolish

Create repolish.yaml at the project root:

providers:
  workflows:
    provider_root: ./local

provider_root points to the directory that contains repolish.py and the repolish/ template folder. No install step is needed for a local provider.

4. Run it

Check what would be generated without writing anything:

repolish apply --check
apply summary
└── Standalone
    └── workflows@  [1 file]
        └── ✓ WORKFLOWS.md

Apply and write the file:

repolish apply
apply summary
└── Standalone
    └── workflows@  1 written
        └── ✓ WORKFLOWS.md

WORKFLOWS.md is now in your project:

# Workflows

<!-- Auto-generated by repolish - do not edit directly -->

| Workflow | File          | Triggers               |
| -------- | ------------- | ---------------------- |
| CI       | `ci.yml`      | `push`, `pull_request` |
| Release  | `release.yml` | `push`                 |

Run repolish apply again whenever you add or rename a workflow file and the table updates automatically.

Every marker and annotation the summary trees print is documented in the Summary Trees reference.

What's next

This example only scratches the surface. Providers can:

  • supply multiple templates from the same context
  • preserve hand-edited sections in the output with preprocessor directives
  • accept inputs from other providers
  • delete obsolete files as part of migration

For a complete walkthrough with multiple providers, a monorepo, and published packages, follow the Tutorial.

Other useful references: