Skip to content

design: generic-automerge.yml reusable + 62-consumer conversion arc #379

Description

@opoudjis

Proximal trigger: metanorma/ci#342 rolling drift-audit report (2026-07-29 run) — automerge.yml surfaced as the largest remaining drift class at 62 consumers, following the 2026-07-27 to 2026-07-30 rake.yml sweep that cleared its class (31 consumers merged + cimas.yml unmap landed at metanorma/ci@0b4623e). This ticket is the design-first response to the shape of the next-class conversion.

Related: metanorma/cimas#68 (rake.yml conversion arc precedent) + metanorma/ci#372 (v1.1.0 immutable + three-tier tagging discipline).

Problem

The cimas-managed automerge.yml template is the largest drift class in the current audit report: 62 consumers mapped to master/automerge.yml, spanning flavour gems (15), model repos (14), sample repos (~18), plus basicdoc-models, requirements-models, and various project repos (ogc, csa, hk, bipm-si-brochure, eccma).

Unlike the rake.yml conversion arc — where consumers just needed uses: metanorma/ci/.github/workflows/generic-rake.yml@v1 to point at an existing reusable — the current master/automerge.yml template is a full inline workflow (triggers + permissions + pascalgn/automerge-action@v0.16.4 steps), and there is no metanorma/ci/.github/workflows/generic-automerge.yml reusable to point consumers at.

Consequences of the current shape:

  • 62 places to update when pascalgn/automerge-action bumps a major.
  • 62 places to drift silently when consumers hand-edit or when the master template changes.
  • 62 places for the (e.2) audit warning to accumulate.

Concrete example — the current drift class (as of the 2026-07-29 audit)

Spot-checked master template + consumer files show they all agree on pascalgn/automerge-action@v0.16.4 — the action-version pin is NOT the drift source. The actual drift is the permissions: block that metanorma/ci#339 added to the master template on 2026-07-12 (least-privilege contents: write + pull-requests: write) that has not yet been synced out to the 62 consumers. All 62 automerge.yml files are on the pre-#339 shape, missing the block.

That's the shape of every template update in this class: master gets a change, consumers await a sync wave to catch up, and in the meantime the (e.2) audit accumulates the drift count. A reusable-workflow reference obviates the sync-wave requirement entirely for future template updates — consumers pinned at @v1 pick up permission changes, action-version bumps, and step-shape changes transparently on the next workflow run.

Proposal

Create metanorma/ci/.github/workflows/generic-automerge.yml as a workflow_call reusable. Consumers then adopt a small uses: stub referencing it at @v1 (per the three-tier discipline landed in ci#372 — moving major, moving minor, immutable patch).

Reusable shape

# metanorma/ci/.github/workflows/generic-automerge.yml
name: automerge (reusable)

on:
  workflow_call:
    secrets:
      # No secrets input needed — automerge uses GITHUB_TOKEN which is
      # available via secrets.GITHUB_TOKEN in the caller context.
      # If a consumer needs to pass a PAT with wider permissions,
      # add a `pat_token` optional secret later.

permissions:
  contents: write
  pull-requests: write

jobs:
  automerge:
    runs-on: ubuntu-latest
    steps:
      - id: automerge
        name: automerge
        uses: pascalgn/automerge-action@v0.16.4
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Consumer stub shape

# In each consumer repo: .github/workflows/automerge.yml
name: automerge

on:
  pull_request:
    types: [labeled, unlabeled, synchronize, opened, edited, ready_for_review, reopened, unlocked]
  pull_request_review:
    types: [submitted]
  check_suite:
    types: [completed]
  status: {}

permissions:
  contents: write
  pull-requests: write

jobs:
  automerge:
    uses: metanorma/ci/.github/workflows/generic-automerge.yml@v1
    secrets: inherit

Triggers stay at the caller level (that's a reusable-workflow structural requirement — the caller decides what fires it). Workflow-level permissions at the caller must declare what the reusable's job needs, since job-level permissions can't exceed the caller's ceiling. secrets: inherit passes GITHUB_TOKEN through.

Versioning

Same three-tier discipline as ci#372 established: immutable patch tag (v1.2.0), moving minor (v1.2), moving major (v1). Consumers pin @v1 for auto-track by default.

Bumping pascalgn/automerge-action

One reusable-side change instead of 62 consumer PRs. This is the whole point of the conversion.

Non-scope for this issue

  • The 62-consumer sweep is a follow-up conversion arc (would mirror the rake.yml cimas#68 pattern: batches by ownership, direct-to-main cimas.yml unmap after PRs merge). Not proposing that here — this issue is only for the reusable design.
  • Any change to what triggers automerge on consumer side — that stays out of scope; the reusable just executes the merge attempt on invocation.
  • Special-cases where a consumer needs a broader-permission PAT (rare in the automerge context) — can be added as an optional pat_token secret in a follow-up if a real consumer needs it.

Ask

@ronaldtse — happy to open the PR against metanorma/ci implementing the reusable shape above once you've had a chance to look at this design. If you have a different shape preference (different trigger scope on the reusable, different permissions posture, different action-version pin than the current v0.16.4), name it and I'll draft accordingly.

🤖

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status
🆕 New

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions