From 903ba5e8749785c5b31bb6a37d5a63944ebae9a5 Mon Sep 17 00:00:00 2001 From: Joseph Bylund Date: Mon, 9 Nov 2026 21:10:01 -0500 Subject: [PATCH 1/2] ci: label new pull requests from the files they change Add a workflow that runs actions/labeler v7.0.0 when a PR is opened, reopened or marked ready for review. The rules in .github/labeler.yml add: - a `backend: *` label for each backend whose directory the PR touches (including its `experimental/` counterpart); - `category:internal` when every changed file is CI config, build-support, release tooling, `3rdparty/` or a Cargo manifest or lockfile; - `category:documentation` when every changed file is under `docs/docs/`. Labels are only added, never removed, and a PR that would gain more than three labels gains none, so tree-wide refactors are left alone. `release-notes:not-required` is never applied: that stays a human decision. The workflow uses `pull_request_target` so that PRs from forks get a token that can add labels. Nothing from the PR is checked out or run: the labeler lists changed files through the API and reads its config from the base branch. Disclosure: investigated and implemented with Claude. --- .github/labeler.yml | 124 +++++++++++++++++++++++++++++++++ .github/workflows/labeler.yaml | 24 +++++++ 2 files changed, 148 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yaml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000000..c7bbd828755 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,124 @@ +# Configuration for .github/workflows/labeler.yaml (https://github.com/actions/labeler). +# +# Labels are only ever added, never removed, so a maintainer can always correct them by hand. +# The release-notes gate label `release-notes:not-required` is deliberately never applied here: that +# is a human decision. + +# If a PR would gain more than this many labels from the rules below, it gains none of them. This +# keeps tree-wide refactors from collecting a label for every backend they happen to touch. +changed-files-labels-limit: 3 + +"backend: Adhoc": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/adhoc/** + - src/python/pants/backend/experimental/adhoc/** + +"backend: CC": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/cc/** + - src/python/pants/backend/experimental/cc/** + +"backend: Docker": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/docker/** + - src/python/pants/backend/experimental/docker/** + +"backend: Environments": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/core/environments/** + +"backend: Go": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/go/** + - src/python/pants/backend/experimental/go/** + +"backend: Helm": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/helm/** + - src/python/pants/backend/experimental/helm/** + +"backend: JavaScript": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/javascript/** + - src/python/pants/backend/typescript/** + - src/python/pants/backend/jsx/** + - src/python/pants/backend/tsx/** + - src/python/pants/backend/experimental/javascript/** + - src/python/pants/backend/experimental/typescript/** + +"backend: JVM": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/jvm/** + - src/python/pants/backend/java/** + - src/python/pants/backend/kotlin/** + - src/python/pants/backend/scala/** + - src/python/pants/backend/experimental/java/** + - src/python/pants/backend/experimental/kotlin/** + - src/python/pants/backend/experimental/scala/** + +"backend: OpenAPI": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/openapi/** + - src/python/pants/backend/experimental/openapi/** + +"backend: Protobuf": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/codegen/protobuf/** + - src/python/pants/backend/experimental/codegen/protobuf/** + +"backend: Python": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/python/** + - src/python/pants/backend/experimental/python/** + +"backend: Rust": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/rust/** + - src/python/pants/backend/experimental/rust/** + +"backend: Shell": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/shell/** + +"backend: Swift": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/swift/** + - src/python/pants/backend/experimental/swift/** + +"backend: Terraform": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/terraform/** + - src/python/pants/backend/experimental/terraform/** + +"backend: Thrift": + - changed-files: + - any-glob-to-any-file: + - src/python/pants/backend/codegen/thrift/** + - src/python/pants/backend/experimental/codegen/thrift/** + +# Only when every changed file is CI, release tooling, or a dependency lockfile. +"category:internal": + - changed-files: + - all-globs-to-all-files: + - "{.github/**,build-support/**,src/python/pants_release/**,3rdparty/**,src/rust/**/Cargo.toml,src/rust/Cargo.lock}" + +# Only when every changed file is user-facing documentation (release notes do not count). +"category:documentation": + - changed-files: + - all-globs-to-all-files: + - "docs/docs/**" diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml new file mode 100644 index 00000000000..18f0246d3a7 --- /dev/null +++ b/.github/workflows/labeler.yaml @@ -0,0 +1,24 @@ +# https://github.com/actions/labeler +# +# Adds labels to new PRs based on the files they change. The rules are in .github/labeler.yml. + +name: Label pull requests +on: + # `pull_request_target` rather than `pull_request`, so that PRs from forks get a token that can + # add labels. This is safe because nothing from the PR is checked out or run: the labeler lists + # the changed files through the API and reads .github/labeler.yml from the base branch. + pull_request_target: + types: [opened, reopened, ready_for_review] + +permissions: {} + +jobs: + label: + name: Label from changed files + if: github.repository_owner == 'pantsbuild' + runs-on: ubuntu-latest + permissions: + contents: read # read .github/labeler.yml + pull-requests: write # add labels to the PR + steps: + - uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0 From ecda4e2b4197ca139ca65bcc4cbe21d2be12e123 Mon Sep 17 00:00:00 2001 From: Joseph Bylund Date: Mon, 9 Nov 2026 21:14:50 -0500 Subject: [PATCH 2/2] ci: relabel on push and stop applying category:internal Run the labeler on `synchronize` too. Each run looks at the PR's whole diff, so a later push that touches a new backend adds its label. The labeler keeps no state, so it also re-adds a matching label that someone removed by hand; that is acceptable for the `backend: *` labels, which nothing automated depends on. Drop the `category:internal` rule. The labeler never removes labels, so a PR that started out CI-only would keep the label after later pushes made it user-facing, and it would pass the release-notes check without anyone deciding that it needs no notes. Also write the trigger types as a block-style list. --- .github/labeler.yml | 13 ++++--------- .github/workflows/labeler.yaml | 10 ++++++++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index c7bbd828755..eb674ef1964 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,8 +1,9 @@ # Configuration for .github/workflows/labeler.yaml (https://github.com/actions/labeler). # -# Labels are only ever added, never removed, so a maintainer can always correct them by hand. -# The release-notes gate label `release-notes:not-required` is deliberately never applied here: that -# is a human decision. +# Labels are only ever added, never removed. The labels that satisfy the release-notes check +# (`release-notes:not-required` and `category:internal`) are deliberately never applied here: whether +# a change needs release notes is a human decision, and an added label would stay even if later +# pushes made the PR user-facing. # If a PR would gain more than this many labels from the rules below, it gains none of them. This # keeps tree-wide refactors from collecting a label for every backend they happen to touch. @@ -111,12 +112,6 @@ changed-files-labels-limit: 3 - src/python/pants/backend/codegen/thrift/** - src/python/pants/backend/experimental/codegen/thrift/** -# Only when every changed file is CI, release tooling, or a dependency lockfile. -"category:internal": - - changed-files: - - all-globs-to-all-files: - - "{.github/**,build-support/**,src/python/pants_release/**,3rdparty/**,src/rust/**/Cargo.toml,src/rust/Cargo.lock}" - # Only when every changed file is user-facing documentation (release notes do not count). "category:documentation": - changed-files: diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml index 18f0246d3a7..af44625e7d5 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/labeler.yaml @@ -1,6 +1,8 @@ # https://github.com/actions/labeler # -# Adds labels to new PRs based on the files they change. The rules are in .github/labeler.yml. +# Adds labels to PRs based on the files they change. The rules are in .github/labeler.yml. Each run +# looks at the PR's whole diff, so a push that touches a new area adds its label. The labeler keeps no +# state between runs, so it also re-adds a matching label that someone removed by hand. name: Label pull requests on: @@ -8,7 +10,11 @@ on: # add labels. This is safe because nothing from the PR is checked out or run: the labeler lists # the changed files through the API and reads .github/labeler.yml from the base branch. pull_request_target: - types: [opened, reopened, ready_for_review] + types: + - opened + - reopened + - synchronize + - ready_for_review permissions: {}