Skip to main content

check-paired-file-change.yml

Fail a pull request that changes a file matching trigger-paths without also changing a file matching required-paths, unless the PR carries the skip label.

The first use is a macro library and the page that documents its notation: a change to macros.qmd must come with a change to notation-choices.qmd (Morrison-Lab/macros#248). Nothing in the check knows that pair; the paths are the caller’s.

What it compares

The files the pull request changes. The check diffs HEAD^1...HEAD on a pull_request run, where the checkout is GitHub’s merge ref and HEAD^1 is the base tip it was built on. On any other event there is no base, and the check skips with a notice. A renamed file counts at both its old and its new path.

Matching is git’s own. Both path inputs are git pathspecs, comma- or newline-separated, passed to git diff --name-only. A bare * also crosses /; use :(glob) magic for **.

The skip label is read live. The label is read from the API at the moment of the check, because the event payload’s labels are frozen at the triggering event and miss any label applied later. The read happens only when the check would otherwise fail, and an API error fails the check rather than reading as “no labels”. An empty skip-label never matches.

Inputs

Input Type Default Description
trigger-paths string required Comma- or newline-separated git pathspecs. A changed file matching any of them requires a change matching required-paths.
required-paths string required Comma- or newline-separated git pathspecs. At least one changed file must match one of them whenever a changed file matches trigger-paths.
skip-label string 'no-notation-change' PR label that waives the pairing, matched case-insensitively. Set to an empty string to disable the escape hatch.
message string see the workflow Text shown in the failure annotation, saying what to update.
runs-on string empty (CI_RUNNER variable, else ubuntu-latest) Runner label for the job. Use a self-hosted label only for a private repository; never for a public one, whose pull requests can run arbitrary code on it (#1167).

An empty trigger-paths or required-paths is an error, not a pass.

Permissions

Read-only: contents: read, plus issues: read and pull-requests: read for the live label read. A caller that pins the calling job’s permissions must include all three.

Example

name: Check paired file change
on:
  pull_request:
    branches: [main]
jobs:
  check:
    uses: Morrison-Lab/gha/.github/workflows/check-paired-file-change.yml@v3
    with:
      trigger-paths: 'macros.qmd'
      required-paths: 'notation-choices.qmd'
      # skip-label: 'no-notation-change'
      # message: 'Update notation-choices.qmd to match the macro change.'
      # runs-on: private-runner  # self-hosted runner label (private repositories only)

See check-paired-file-change.yml in examples/ for the full caller stub.