Skip to content

README: improve positioning + add competitor comparison for AI-assistant discoverability #310

README: improve positioning + add competitor comparison for AI-assistant discoverability

README: improve positioning + add competitor comparison for AI-assistant discoverability #310

Workflow file for this run

name: 📏 Auto Label PR Size
on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
size-labeler:
name: Label PR by size
runs-on: ubuntu-latest
timeout-minutes: 2
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Compute size and apply label
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const pr = context.payload.pull_request;
const prNumber = pr.number;
// Uniform charcoal chip for every size label so the size metadata reads as one
// recessive family and doesn't compete with semantic labels (bug, Frontend, …) on
// a busy PR list. The cool -> hot ramp lives entirely in the per-bucket emoji
// (🔵 smallest -> 🔴 largest), which keeps its crisp shape on a neutral background.
// maxSize is the inclusive upper bound of changed LOC (additions + deletions) for
// the bucket; the last entry (null) catches everything above it.
const CHIP_COLOR = "2d3139";
const BUCKETS = [
{ name: "🔵 size/XS", color: CHIP_COLOR, maxSize: 19 },
{ name: "🟢 size/S", color: CHIP_COLOR, maxSize: 100 },
{ name: "🟡 size/M", color: CHIP_COLOR, maxSize: 300 },
{ name: "🟠 size/L", color: CHIP_COLOR, maxSize: 600 },
{ name: "🔴 size/XL", color: CHIP_COLOR, maxSize: null },
];
const MANAGED_LABELS = BUCKETS.map((b) => b.name);
// Lockfiles and generated clients dwarf the human-reviewed diff, so exclude them
// from the count — otherwise a lockfile bump mislabels a tiny PR as XL.
const IGNORE_GLOBS = [
"**/package-lock.json",
"**/yarn.lock",
"**/pnpm-lock.yaml",
"**/poetry.lock",
"**/uv.lock",
"sdks/python/src/opik/rest_api/**",
"sdks/typescript/src/opik/rest_api/**",
"**/*.snap",
"**/*.svg",
"**/*.png",
];
const globToRegExp = (glob) => {
let re = "";
for (let i = 0; i < glob.length; i++) {
if (glob.startsWith("**/", i)) { re += "(?:[^/]+/)*"; i += 2; continue; }
if (glob.startsWith("**", i)) { re += ".*"; i += 1; continue; }
const c = glob[i];
if (c === "*") { re += "[^/]*"; continue; }
if (".+^${}()|[]\\".includes(c)) { re += "\\" + c; continue; }
re += c;
}
return new RegExp("^" + re + "$");
};
const ignoreRes = IGNORE_GLOBS.map(globToRegExp);
const isIgnored = (path) => ignoreRes.some((re) => re.test(path));
const files = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number: prNumber,
per_page: 100,
});
const total = files
.filter((f) => !isIgnored(f.filename))
.reduce((sum, f) => sum + f.additions + f.deletions, 0);
const bucket =
BUCKETS.find((b) => b.maxSize !== null && total <= b.maxSize) ||
BUCKETS[BUCKETS.length - 1];
core.info(`PR #${prNumber}: ${total} changed LOC (excluding ignored files) -> ${bucket.name}`);
// Keep the label's color/emoji authoritative regardless of how it was first created.
try {
const { data: existing } = await github.rest.issues.getLabel({
owner,
repo,
name: bucket.name,
});
if (existing.color !== bucket.color) {
await github.rest.issues.updateLabel({
owner,
repo,
name: bucket.name,
color: bucket.color,
});
}
} catch (err) {
if (err.status !== 404) throw err;
await github.rest.issues.createLabel({
owner,
repo,
name: bucket.name,
color: bucket.color,
});
}
const current = pr.labels.map((l) => l.name);
for (const stale of current.filter(
(name) => MANAGED_LABELS.includes(name) && name !== bucket.name
)) {
try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: prNumber,
name: stale,
});
} catch (err) {
if (err.status !== 404) throw err;
}
}
if (!current.includes(bucket.name)) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: [bucket.name],
});
}