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.

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: