Skip to content

Fast Lanes

A fast lane is a named slice of a provider's work with its own CLI subcommand. While repolish apply -p <alias> already scopes a run to one provider, it still builds the full session: every mapping the provider declares is staged and rendered. A lane run stops earlier. It loads exactly one provider and stages only the templates the lane declares. For a day-to-day edit loop over a handful of generated files, that is the difference between a quick gate and a short coffee break.

That shorter loop is only half the story. Fast lanes also let a provider ship its own focused project utilities without forcing the team to build and maintain another CLI. If a React provider already owns the templates and conventions for components, hooks, or route modules, the same provider can expose commands like react-provider-cli component or react-provider-cli route and generate those files directly. The team gets scaffolding, standards, and repo-health automation from one package instead of spreading the same logic across repolish templates, ad hoc scripts, and a second command-line tool.

Lanes are a quick-development tool. The full system remains the source of truth: run repolish apply (or the provider's all subcommand) before committing, and CI should keep checking the full tree.

This section is split into focused pages:

  • CLI covers provider_cli, wiring, config, and trying a lane.
  • Commands covers provider-owned command declarations.
  • Workflows covers how commands and lanes work together.
  • Runtime Semantics covers execution rules and advanced cases.

Why they matter

  • They give a provider an ergonomic developer-facing CLI "for free" once the provider already exists.
  • They keep scaffolding logic next to the templates and standards it depends on, instead of duplicating that logic in a second tool.
  • They let teams skip expensive full-provider runs when they only need a narrow update, especially when post_process work is the dominant cost.
  • They still preserve a path back to the full system: the same provider can offer quick lane commands for local work and a complete repolish apply pass for repo-wide consistency.

In practice that means a provider can serve two roles at once:

  • repo maintenance through full repolish runs, and
  • day-to-day developer workflows through small, provider-owned commands.

If you are already paying the cost to define provider templates and standards, lanes let you reuse that investment instead of rebuilding it in a separate scaffolding tool.

Declaring lanes

Implement create_fast_lanes on the provider. It returns {lane_name: FastLaneSpec} (or a factory returning one, see Lane factories), where each spec holds the same shapes the regular hooks return:

from repolish import FastLaneSpec, Provider, TemplateMapping


class ActionsProvider(Provider[Ctx, BaseInputs]):
    def create_file_mappings(self, context):
        # context-fed work that needs the full pipeline (peers may feed it)
        return {'.github/workflows/ci.yml': 'ci.yml.jinja'}

    def create_fast_lanes(self, repolish):
        # the repolish namespace only: repo info plus this provider's
        # name and version, identical in a lane run and a full apply
        header = f'generated by {repolish.provider.alias}'
        return {
            'actions': FastLaneSpec(
                file_mappings={
                    f'{f.stem}/action.yaml': TemplateMapping(
                        '_repolish.action.yaml.jinja',
                        extra_context={
                            'module': f'{f.stem}.main',
                            'header': header,
                        },
                    )
                    for f in ACTIONS_SRC.glob('*.py')
                },
            ),
            'docs': FastLaneSpec(
                file_insertions={
                    'README.md': {'usage': _usage_block},
                    'docs/usage.md': {'usage': _usage_block},
                },
            ),
        }

The hook receives the repolish namespace and nothing else on purpose. Those values (repo info, year, this provider's alias and version) are the same whether the lane runs alone or merged into a full apply, so a lane's file set is stable either way; this is the namespace every provider gets for free, the one a lane needs for things like generated file headers. The provider context is deliberately absent: it may carry peer-provider input that is only complete in a full run, and a lane declaration that depended on it would produce a different file set in each mode. A dest whose content genuinely needs data from another provider belongs in the regular hooks, not in a lane.

One rule follows from the same reasoning: lane templates must live off TemplateMapping.extra_context. Rendering still flattens the provider's finalized context into the template namespace, so a lane template that reads a peer-derived variable renders differently in a lane run than in a full apply. Keep lane templates self-contained and the parity guarantee holds: a lane run writes the same bytes a full apply would write for those files.