feat(ci): add reusable stale and PR labeler workflows#25
Conversation
- reusable-stale.yml: four-lane stale management - Unassigned issues/PRs: 14d stale → 7d close - Assigned issues: 30d stale → 10d close - Assigned PRs: 27d stale → 7d close (deadline-based, ignores updates) - Exempt labels: maintainer, pinned, security, no-stale - reusable-pr-labeler.yml: PR size auto-labeling + dependency-change detection - Size tiers XS/S/M/L/XL based on net diff lines (lockfiles excluded) - Detects go.mod/package.json/lockfile changes, adds dependencies-changed label - Posts/updates maintainer checklist comment; removes label on sync if resolved - Actions pinned by commit SHA
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #25 (.github)
Summary
Two new reusable workflows for the org-wide .github repository:
reusable-stale.yml— four-lane stale management (unassigned issues+PRs, assigned issues, assigned PRs with deadline-based close)reusable-pr-labeler.yml— size labels and dependency-change detection viaactions/github-script
Both third-party actions are pinned by commit SHA. I verified the pins resolve to the upstream v9 / v7 tag commits:
actions/stale@5bef64f19d7facfb25b37b414482c7164d639639→refs/tags/v9✅actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b→refs/tags/v7✅
Permission scoping is correct: workflow-level permissions: {}, job-level issues: write + pull-requests: write only where needed. The four-lane stale design cleanly partitions assigned vs. unassigned via exempt-all-assignees: true and include-only-assigned: true — no double-processing.
Overall the structure is solid. Findings below are non-blocking hardening and polish suggestions.
Findings
P2 — Defense-in-depth: avoid ${{ inputs.* }} interpolation inside github-script
reusable-pr-labeler.yml lines 39–41:
const owner = '${{ inputs.repo_owner }}';
const repo = '${{ inputs.repo_name }}';
const prNumber = Number('${{ inputs.pr_number }}');Direct expression interpolation into a script: body is the script-injection pattern that GitHub explicitly warns against (security hardening guide). In this deployment the callers pass github.repository_owner and github.event.repository.name, which are GitHub-controlled and safe — so there is no live exploit. But because this workflow is workflow_call-exposed to every repo in Mininglamp-OSS, a future caller (or a misuse like with: repo_owner: ${{ github.event.pull_request.title }}) could turn this into a code-execution sink in one keystroke.
The defense-in-depth pattern costs nothing:
- name: Apply size label and detect dependency changes
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
REPO_OWNER: ${{ inputs.repo_owner }}
REPO_NAME: ${{ inputs.repo_name }}
PR_NUMBER: ${{ inputs.pr_number }}
with:
script: |
const owner = process.env.REPO_OWNER;
const repo = process.env.REPO_NAME;
const prNumber = Number(process.env.PR_NUMBER);
...Recommended given the security_sensitive classification.
P2 — "net diff lines" is a misnomer
reusable-pr-labeler.yml line 64:
netLines += (f.additions || 0) + (f.deletions || 0);This is total churn (additions + deletions), not net lines. The PR body and the inline comment ("Calculate net lines excluding lockfiles") describe it as net; the size thresholds (<10 XS … ≥500 XL) were almost certainly tuned for churn. The math is fine — just rename the variable and update the description so future maintainers don't "fix" it into actual additions - deletions.
P2 — Orphaned dep-change comment when label is removed
reusable-pr-labeler.yml lines 132–141: when a sync push removes all dep files, the dependencies-changed label is removed, but the existing maintainer-checklist comment (matched by <!-- octo-dep-change-bot -->) is left in place. Result: the PR has no label but still shows a "Dependency Changes Detected" checklist. Minor UX inconsistency — either also delete/strikethrough the comment in this branch, or update its body to "no longer present".
P3 — Duplicate listLabelsOnIssue call
Lines 107 and 133 both call listLabelsOnIssue for the same PR within the same script run. Fold the second lookup into the first snapshot to save one API call per run.
P3 — operations-per-run: 500 is well above the default
All three stale jobs set operations-per-run: 500 (default is 30). Each "operation" consumes ~1 API call against the 1,000/hr GITHUB_TOKEN budget. At current Mininglamp-OSS scale this is fine, but on a very active repo a daily cron could spike the GraphQL/REST budget and starve other workflows running in the same hour. Worth keeping in mind if you ever consolidate orgs.
Nit — Caller permissions: block in PR description
The example caller snippets put a permissions: block on the calling job that uses the reusable workflow. This is correct — for a uses:-style job, job-level permissions: set the GITHUB_TOKEN scope for the called workflow, and the called workflow can only narrow further. Just confirming this is intentional and matches docs; nothing to change.
Security review (security_sensitive)
Things I'd want a human maintainer to double-check before merge — none are blockers, all are defense posture:
- Script-injection surface — see P2 finding above. Today's callers are safe; future callers may not be.
- Permission grants are minimal and correct. No
contents: write, noid-token: write, noactions: write. The labelercreateLabelcall works againstissues: write. - No secrets used.
secrets: inheritin the example caller is harmless but unnecessary — the reusable workflow only uses the default GITHUB_TOKEN. - Trigger choice is safe. The labeler example uses
pull_request(notpull_request_target), so PRs from forks get the read-only GITHUB_TOKEN and the label step will simply no-op rather than running attacker-controlled code with write access. (Trade-off: fork PRs won't get auto-labeled. Acceptable for internal-first repos.) - Action pins verified against upstream tag refs (see Summary).
Verdict
No correctness, security, or data-loss blockers. The script-injection hardening (P2) is strongly recommended for a shared reusable workflow and should be addressed in a follow-up if not in this PR. Approving so the downstream rollout PRs are unblocked.
Summary
Adds two reusable workflows to the shared
.githubrepository, available to allMininglamp-OSSrepos.reusable-stale.ymlFour-lane stale management (modeled after openclaw best practice):
maintainer,pinned,security,no-staleclose-issue-reason: not_plannedactions/stalepinned by commit SHAreusable-pr-labeler.ymlsize/XS/size/S/size/M/size/L/size/XL(net diff lines, lockfiles excluded)go.mod,package.json,pnpm-lock.yaml,yarn.lock,go.sumare touched:dependencies-changedlabelactions/github-scriptpinned by commit SHAHow to use in repos
Part of P0 automation rollout
Merge this first — business repos'
stale.ymlandlabeler.ymldepend on these reusable workflows being onmain.