diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2d9088f36..322c62b57 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -28,4 +28,4 @@ - [ ] New user-facing strings are wrapped in `I18n.tr()` with translator context, reusing existing terms where possible - [ ] Go changes: ran `make fmt`, added/updated tests, `make test` passes, and `go mod tidy` is clean - [ ] QML changes: ran `make lint-qml` with no new warnings -- [ ] I have opened a corresponding pull request in dlx-docs to document any new behaviors: https://github.com/duxlabs-dev/dux-docs +- [ ] I have opened a corresponding pull request in dux-docs to document any new behaviors: https://github.com/duxlabs-dev/dux-docs diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 000000000..4d7c82a93 --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,64 @@ +name: PR Labels + +# pull_request_target: never check out PR code here, the token has write access + +on: + pull_request_target: + types: [opened, edited, reopened, synchronize] + +permissions: + contents: read + pull-requests: write + +concurrency: + group: pr-labels-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + label: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const body = pr.body || ""; + + const checked = [...body.matchAll(/^\s*-\s*\[[xX]\]\s*(.+)$/gm)].map(m => m[1].trim()); + const typeOfChange = { + "Bug fix": "fix", + "New feature": "feature", + "Breaking change": "breaking", + "Refactor / internal cleanup": "internal", + "Documentation": "docs", + }; + const labels = new Set( + Object.entries(typeOfChange) + .filter(([item]) => checked.some(line => line.startsWith(item))) + .map(([, label]) => label) + ); + + const files = await github.paginate(github.rest.pulls.listFiles, { + ...context.repo, + pull_number: pr.number, + per_page: 100, + }); + const paths = files.map(f => f.filename); + const pathRules = [ + ["packaging", p => p.startsWith("distro/") || p === "flake.nix" || p === "flake.lock"], + ["i18n", p => p.startsWith("quickshell/translations/")], + ]; + for (const [label, matches] of pathRules) { + if (paths.some(matches)) labels.add(label); + } + + const existing = new Set(pr.labels.map(l => l.name)); + const missing = [...labels].filter(l => !existing.has(l)); + if (missing.length === 0) return core.info("nothing to add"); + + await github.rest.issues.addLabels({ + ...context.repo, + issue_number: pr.number, + labels: missing, + }); + core.info(`added: ${missing.join(", ")}`);