mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-05 11:37:31 +09:00
Add two GitHub-native workflows ahead of disabling the Dosu app and
migrate size labels to canonical k8s namespacing.
- .github/workflows/stale.yaml uses actions/stale@v10 to manage the
lifecycle/stale and lifecycle/rotten lifecycle. Daily cron, 60d
before stale, 14d before close. Issue/PR exempt lists differ on
purpose: an accepted issue (triage/accepted) is a known long-tail
task, not stale; a held PR (do-not-merge/hold) is paused
intentionally. Priority/security/regression labels protect issues
someone explicitly decided to keep visible.
- .github/workflows/pr-size.yaml uses actions/github-script@v7 to
apply size/* labels based on diff line count. Thresholds match the
descriptions in .github/labels.yml. Inline ignore patterns skip
vendored deps, kubebuilder/openapi inline generated files
(zz_generated.{deepcopy,conversion,defaults,openapi}.go), whole
code-generated trees (any path under generated/ or *.pb.go),
vendored Helm charts, and lockfiles. Concurrency group keyed on PR
number with cancel-in-progress: true. removeLabel is wrapped in
try/catch tolerating 404 for races between event payload and
execution.
- Migrate the six size labels from legacy 'size:X' (colon) to
canonical 'size/X' (slash) via aliases in .github/labels.yml. The
colon form was an artefact of the previous sizing bot's output;
with the bot replaced and namespacing already used everywhere else
(kind/, area/, priority/, triage/, lifecycle/, do-not-merge/,
security/), the slash form aligns with the rest of the scheme. The
alias migration preserves IDs and keeps every existing labelled PR
pointing at the renamed entry.
pr-size.yaml emits the new 'size/' form from day 1 and accepts
either prefix when looking up the previous size label, to handle
the brief window between PR merge and the labels-sync run.
Verification of zero hard dependencies on Dosu in workflows: only
backport.yaml (consumes backport*, applied by humans only) and
pull-requests*.yaml + tags.yaml (consume release, applied by tags
workflow itself) — none of those labels come from Dosu.
The lgtm label (33/33 in last 100 PRs from Dosu, none from humans)
is intentionally not replaced in this PR — observe reviewer
behaviour for 7 days after Dosu is disabled and decide whether a
pull_request_review-on-approve workflow is needed.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
94 lines
3.6 KiB
YAML
94 lines
3.6 KiB
YAML
name: PR size label
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, synchronize, reopened]
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
|
|
concurrency:
|
|
group: pr-size-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
size:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const pr = context.payload.pull_request;
|
|
|
|
// Skip files that should not count as "real diff" — they are produced by tools,
|
|
// not authored by humans, and including them lies to reviewers about review effort:
|
|
// - vendored Go deps,
|
|
// - kubebuilder/openapi inline generated files (zz_generated.{deepcopy,conversion,defaults,openapi}.go),
|
|
// - whole code-generated trees from client-gen / lister-gen / applyconfiguration-gen
|
|
// and protobuf (any path under a `generated/` directory or ending in `.pb.go`),
|
|
// - vendored Helm charts,
|
|
// - lockfiles.
|
|
const isIgnored = (path) =>
|
|
path.startsWith('vendor/') ||
|
|
/\bzz_generated\.[^/]*\.go$/.test(path) ||
|
|
/(^|\/)generated\//.test(path) ||
|
|
path.endsWith('.pb.go') ||
|
|
/^packages\/system\/[^/]+\/charts\//.test(path) ||
|
|
path === 'go.sum' || path.endsWith('/go.sum') ||
|
|
path.endsWith('.lock') || path.endsWith('.lockb');
|
|
|
|
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: pr.number,
|
|
});
|
|
const lines = files
|
|
.filter((f) => !isIgnored(f.filename))
|
|
.reduce((acc, f) => acc + (f.additions || 0) + (f.deletions || 0), 0);
|
|
|
|
// Thresholds match the descriptions of size/* labels in .github/labels.yml.
|
|
const bucket =
|
|
lines <= 9 ? 'XS' :
|
|
lines <= 29 ? 'S' :
|
|
lines <= 99 ? 'M' :
|
|
lines <= 499 ? 'L' :
|
|
lines <= 999 ? 'XL' : 'XXL';
|
|
const target = `size/${bucket}`;
|
|
|
|
// Match both legacy "size:" and canonical "size/" during the migration window.
|
|
const existing = (pr.labels || []).map((l) => l.name);
|
|
const oldSizes = existing.filter((n) => n.startsWith('size/') || n.startsWith('size:'));
|
|
const alreadyTarget = oldSizes.includes(target);
|
|
|
|
// Remove every size/* label that is not the target. Tolerate 404 — concurrent
|
|
// runs or manual edits between event payload and execution can race here.
|
|
for (const name of oldSizes) {
|
|
if (name === target) continue;
|
|
try {
|
|
await github.rest.issues.removeLabel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: pr.number,
|
|
name,
|
|
});
|
|
} catch (e) {
|
|
if (e.status !== 404) throw e;
|
|
core.info(`label ${name} already gone (404)`);
|
|
}
|
|
}
|
|
|
|
if (alreadyTarget && oldSizes.length === 1) {
|
|
core.info(`PR #${pr.number}: ${lines} lines, label already ${target}`);
|
|
return;
|
|
}
|
|
if (!alreadyTarget) {
|
|
await github.rest.issues.addLabels({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: pr.number,
|
|
labels: [target],
|
|
});
|
|
}
|
|
core.info(`PR #${pr.number}: ${lines} lines -> ${target}`);
|