From f1c1a80e0900a9393c1029da158d4e1266cc7ded Mon Sep 17 00:00:00 2001 From: Abdullah Date: Tue, 22 Sep 2026 23:57:57 +0530 Subject: [PATCH 1/2] ci: add automatic PR scope and size labels --- .github/labeler.yml | 58 +++++++++++++++++++ .github/workflows/pr-labels.yml | 99 +++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/pr-labels.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000..7415171f65 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,58 @@ +# Automatically managed pull-request scope labels. +# +# The workflow enables sync-labels, so labels declared here are removed when a +# later PR update no longer matches. Labels outside this file are never managed +# by actions/labeler. + +"area: core": + - changed-files: + - any-glob-to-any-file: + - "graphify/*.py" + - "graphify/**/*.py" + +"area: extractors": + - changed-files: + - any-glob-to-any-file: + - "graphify/extractors/**" + +"area: skills": + - changed-files: + - any-glob-to-any-file: + - "graphify/skill*.md" + - "graphify/command-kilo.md" + - "graphify/skills/**" + - "graphify/always_on/**" + - "tools/skillgen/**" + +"area: tools": + - changed-files: + - any-glob-to-any-file: + - "tools/**" + - "scripts/**" + +"area: tests": + - changed-files: + - any-glob-to-any-file: + - "tests/**" + +"area: docs": + - changed-files: + - any-glob-to-any-file: + - "docs/**" + - "*.md" + +"area: ci": + - changed-files: + - any-glob-to-any-file: + - ".github/**" + +"area: packaging": + - changed-files: + - any-glob-to-any-file: + - "pyproject.toml" + - "uv.lock" + - "Dockerfile" + - ".dockerignore" + - "LICENSE" + - "LICENSE-*" + - "NOTICE" diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 0000000000..3b6099a746 --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,99 @@ +name: PR Labels + +# This workflow needs a write-capable token for fork pull requests. It runs in +# the base repository's context and never checks out or executes PR code. +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +concurrency: + group: pr-labels-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + label: + name: Apply scope and size labels + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Apply scope labels + uses: actions/labeler@v7 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/labeler.yml + sync-labels: true + + - name: Apply size label + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const pull_number = context.issue.number; + + // github.paginate fetches every page; listFiles returns at most 100 + // files per request. + const files = await github.paginate(github.rest.pulls.listFiles, { + owner, + repo, + pull_number, + per_page: 100, + }); + const totalChanges = files.reduce( + (total, file) => total + file.additions + file.deletions, + 0, + ); + + let sizeLabel; + if (totalChanges < 10) { + sizeLabel = "size/XS"; + } else if (totalChanges < 50) { + sizeLabel = "size/S"; + } else if (totalChanges < 200) { + sizeLabel = "size/M"; + } else if (totalChanges < 500) { + sizeLabel = "size/L"; + } else { + sizeLabel = "size/XL"; + } + + const sizeLabels = new Set([ + "size/XS", + "size/S", + "size/M", + "size/L", + "size/XL", + ]); + const currentLabels = await github.paginate( + github.rest.issues.listLabelsOnIssue, + { owner, repo, issue_number: pull_number, per_page: 100 }, + ); + + for (const label of currentLabels) { + if (sizeLabels.has(label.name) && label.name !== sizeLabel) { + await github.rest.issues.removeLabel({ + owner, + repo, + issue_number: pull_number, + name: label.name, + }); + } + } + + if (!currentLabels.some((label) => label.name === sizeLabel)) { + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: pull_number, + labels: [sizeLabel], + }); + } + + core.info( + `PR changes ${totalChanges} lines across ${files.length} files: ${sizeLabel}`, + ); From 8677b27d8eb2a0a6ad37a878dbf15861fe6ca408 Mon Sep 17 00:00:00 2001 From: Abdullah Date: Wed, 23 Sep 2026 00:06:41 +0530 Subject: [PATCH 2/2] ci: use slash-prefixed area labels --- .github/labeler.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 7415171f65..1dbe4c324e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -4,18 +4,18 @@ # later PR update no longer matches. Labels outside this file are never managed # by actions/labeler. -"area: core": +"area/core": - changed-files: - any-glob-to-any-file: - "graphify/*.py" - "graphify/**/*.py" -"area: extractors": +"area/extractors": - changed-files: - any-glob-to-any-file: - "graphify/extractors/**" -"area: skills": +"area/skills": - changed-files: - any-glob-to-any-file: - "graphify/skill*.md" @@ -24,29 +24,29 @@ - "graphify/always_on/**" - "tools/skillgen/**" -"area: tools": +"area/tools": - changed-files: - any-glob-to-any-file: - "tools/**" - "scripts/**" -"area: tests": +"area/tests": - changed-files: - any-glob-to-any-file: - "tests/**" -"area: docs": +"area/docs": - changed-files: - any-glob-to-any-file: - "docs/**" - "*.md" -"area: ci": +"area/ci": - changed-files: - any-glob-to-any-file: - ".github/**" -"area: packaging": +"area/packaging": - changed-files: - any-glob-to-any-file: - "pyproject.toml"