refactor(ci): drop c-* label prefix, delete pr-analyzer, simplify auto-label (#8963)

This commit is contained in:
Gustavo Valverde
2026-04-05 11:22:24 +01:00
committed by GitHub
parent 126e92ab24
commit 982b505099
5 changed files with 59 additions and 294 deletions

30
.github/labeler.yml vendored
View File

@@ -1,8 +1,8 @@
# Auto-label config for actions/labeler
# Maps changed file paths to c-* domain labels
# All matching labels are applied; cross-cutting PRs refined in workflow
# Maps changed file paths to domain labels
# All matching labels are applied
c-core:
core:
- changed-files:
- any-glob-to-any-file:
- 'packages/better-auth/src/auth/**'
@@ -20,7 +20,7 @@ c-core:
- 'packages/core/**'
- 'packages/redis-storage/**'
c-database:
database:
- changed-files:
- any-glob-to-any-file:
- 'packages/better-auth/src/db/**'
@@ -31,7 +31,7 @@ c-database:
- 'packages/kysely-adapter/**'
- 'packages/memory-adapter/**'
c-oauth:
oauth:
- changed-files:
- any-glob-to-any-file:
- 'packages/better-auth/src/plugins/generic-oauth/**'
@@ -41,7 +41,7 @@ c-oauth:
- 'packages/core/src/social-providers/**'
- 'packages/core/src/oauth2/**'
c-credentials:
credentials:
- changed-files:
- any-glob-to-any-file:
- 'packages/better-auth/src/plugins/magic-link/**'
@@ -52,7 +52,7 @@ c-credentials:
- 'packages/better-auth/src/plugins/siwe/**'
- 'packages/passkey/**'
c-identity:
identity:
- changed-files:
- any-glob-to-any-file:
- 'packages/oauth-provider/**'
@@ -60,14 +60,14 @@ c-identity:
- 'packages/better-auth/src/plugins/mcp/**'
- 'packages/better-auth/src/plugins/device-authorization/**'
c-organization:
organization:
- changed-files:
- any-glob-to-any-file:
- 'packages/better-auth/src/plugins/organization/**'
- 'packages/better-auth/src/plugins/admin/**'
- 'packages/better-auth/src/plugins/access/**'
c-security:
security:
- changed-files:
- any-glob-to-any-file:
- 'packages/better-auth/src/plugins/two-factor/**'
@@ -75,26 +75,26 @@ c-security:
- 'packages/better-auth/src/plugins/captcha/**'
- 'packages/better-auth/src/plugins/haveibeenpwned/**'
c-enterprise:
enterprise:
- changed-files:
- any-glob-to-any-file:
- 'packages/sso/**'
- 'packages/scim/**'
c-payments:
payments:
- changed-files:
- any-glob-to-any-file:
- 'packages/stripe/**'
- 'packages/api-key/**'
c-platform:
platform:
- changed-files:
- any-glob-to-any-file:
- 'packages/expo/**'
- 'packages/electron/**'
- 'packages/better-auth/src/integrations/**'
c-devtools:
devtools:
- changed-files:
- any-glob-to-any-file:
- 'packages/cli/**'
@@ -103,13 +103,13 @@ c-devtools:
- 'packages/i18n/**'
- 'packages/test-utils/**'
c-docs:
docs:
- changed-files:
- any-glob-to-any-file:
- 'docs/**'
- 'demo/**'
c-devops:
devops:
- changed-files:
- any-glob-to-any-file:
- '.github/**'

View File

@@ -11,11 +11,6 @@
import { execFileSync } from "node:child_process";
import { randomBytes } from "node:crypto";
import { appendFileSync } from "node:fs";
import {
mapTypeToBump,
parseConventionalCommit,
resolveDomain,
} from "./pr-analyzer.js";
// ── Types ──────────────────────────────────────────────────────────────
@@ -30,6 +25,13 @@ interface PRData {
changedFiles: string[];
}
interface ConventionalCommit {
type: string;
scope: string;
subject: string;
breaking: boolean;
}
// ── Constants ──────────────────────────────────────────────────────────
const REPO = process.env.GITHUB_REPOSITORY ?? "better-auth/better-auth";
@@ -37,6 +39,42 @@ const REPO = process.env.GITHUB_REPOSITORY ?? "better-auth/better-auth";
const CUBIC_OPEN = "<!-- This is an auto-generated description by cubic. -->";
const CUBIC_CLOSE = "<!-- End of auto-generated description by cubic. -->";
// ── Conventional commit helpers ────────────────────────────────────────
function parseConventionalCommit(title: string): ConventionalCommit {
const typeMatch = title.match(/^([a-z]+)/);
const type = typeMatch?.[1] ?? "";
const scopeMatch = title.match(/^[a-z]+\(([^)]+)\)/);
const scope = scopeMatch?.[1] ?? "";
const breaking = /^[a-z]+(\([^)]+\))?!:/.test(title);
const subject = title.replace(/^[a-z]+(\([^)]+\))?!?:\s*/, "");
return { type, scope, subject, breaking };
}
function mapTypeToBump(
type: string,
breaking: boolean,
): "patch" | "minor" | "major" | "skip" {
if (breaking) return "major";
switch (type) {
case "fix":
case "perf":
case "refactor":
return "patch";
case "feat":
return "minor";
case "chore":
case "docs":
case "ci":
case "test":
case "style":
case "build":
return "skip";
default:
return "patch";
}
}
// ── GitHub CLI helpers ─────────────────────────────────────────────────
function gh(args: string[]): string {
@@ -207,7 +245,6 @@ function main() {
const patchOnly = pr.baseRef === "main" || pr.baseRef.startsWith("release/");
if (patchOnly && resolvedBump !== "patch") {
if (force) {
// In /changeset mode, cap to patch so the recommendation is always usable
console.log(
`Capping ${resolvedBump} to patch on ${pr.baseRef} (patch-only branch)`,
);
@@ -220,7 +257,6 @@ function main() {
}
const cubicSummary = extractCubicSummary(pr.body);
const domain = resolveDomain(commit.scope, pr.changedFiles);
const fallback = cubicSummary || commit.subject || pr.title;
// All packages are in one changesets fixed group — listing any one
@@ -231,7 +267,6 @@ function main() {
setOutput("skip", "false");
setOutput("bump", resolvedBump);
setOutput("frontmatter", frontmatter);
setOutput("domain", domain);
setOutput("pr_title", pr.title);
setOutput("cubic_summary", cubicSummary);
setOutput("fallback_description", fallback);

View File

@@ -1,245 +0,0 @@
/**
* PR Analyzer — classification module for mapping conventional commit
* scopes and file paths to c-* domain labels.
*
* Pure functions, no side effects, no network calls.
*/
const SCOPE_TO_DOMAIN: Record<string, string> = {
// c-core
core: "c-core",
api: "c-core",
client: "c-core",
cookies: "c-core",
crypto: "c-core",
account: "c-core",
session: "c-core",
instrumentation: "c-core",
"last-login-method": "c-core",
"redis-storage": "c-core",
// c-database
db: "c-database",
adapters: "c-database",
"drizzle-adapter": "c-database",
"prisma-adapter": "c-database",
"kysely-adapter": "c-database",
"mongo-adapter": "c-database",
"memory-adapter": "c-database",
// c-oauth
"oauth-proxy": "c-oauth",
"one-tap": "c-oauth",
"generic-oauth": "c-oauth",
"social-provider": "c-oauth",
// c-credentials
"magic-link": "c-credentials",
"email-otp": "c-credentials",
"phone-number": "c-credentials",
phone: "c-credentials",
username: "c-credentials",
anonymous: "c-credentials",
siwe: "c-credentials",
passkey: "c-credentials",
// c-identity
"oauth-provider": "c-identity",
"oidc-provider": "c-identity",
mcp: "c-identity",
"device-authorization": "c-identity",
// c-organization
organization: "c-organization",
admin: "c-organization",
access: "c-organization",
// c-security
"two-factor": "c-security",
"2fa": "c-security",
captcha: "c-security",
haveibeenpwned: "c-security",
"rate-limiter": "c-security",
// c-enterprise
sso: "c-enterprise",
scim: "c-enterprise",
// c-payments
stripe: "c-payments",
"api-key": "c-payments",
// c-platform
expo: "c-platform",
electron: "c-platform",
// c-devtools
cli: "c-devtools",
telemetry: "c-devtools",
i18n: "c-devtools",
"test-utils": "c-devtools",
"open-api": "c-devtools",
// c-devops
build: "c-devops",
ci: "c-devops",
deps: "c-devops",
"deps-dev": "c-devops",
knip: "c-devops",
// c-docs
docs: "c-docs",
blog: "c-docs",
landing: "c-docs",
};
const PATH_TO_DOMAIN: [string, string][] = [
["packages/oauth-provider/", "c-identity"],
["packages/better-auth/src/plugins/oidc-provider/", "c-identity"],
["packages/better-auth/src/plugins/mcp/", "c-identity"],
["packages/better-auth/src/plugins/device-authorization/", "c-identity"],
["packages/better-auth/src/plugins/magic-link/", "c-credentials"],
["packages/better-auth/src/plugins/email-otp/", "c-credentials"],
["packages/better-auth/src/plugins/phone-number/", "c-credentials"],
["packages/better-auth/src/plugins/username/", "c-credentials"],
["packages/better-auth/src/plugins/anonymous/", "c-credentials"],
["packages/better-auth/src/plugins/siwe/", "c-credentials"],
["packages/passkey/", "c-credentials"],
["packages/better-auth/src/plugins/two-factor/", "c-security"],
["packages/better-auth/src/api/rate-limiter/", "c-security"],
["packages/better-auth/src/plugins/captcha/", "c-security"],
["packages/better-auth/src/plugins/haveibeenpwned/", "c-security"],
["packages/better-auth/src/plugins/organization/", "c-organization"],
["packages/better-auth/src/plugins/admin/", "c-organization"],
["packages/better-auth/src/plugins/access/", "c-organization"],
["packages/better-auth/src/plugins/generic-oauth/", "c-oauth"],
["packages/better-auth/src/plugins/oauth-proxy/", "c-oauth"],
["packages/better-auth/src/plugins/one-tap/", "c-oauth"],
["packages/better-auth/src/oauth2/", "c-oauth"],
["packages/core/src/social-providers/", "c-oauth"],
["packages/core/src/oauth2/", "c-oauth"],
["packages/sso/", "c-enterprise"],
["packages/scim/", "c-enterprise"],
["packages/stripe/", "c-payments"],
["packages/api-key/", "c-payments"],
["packages/better-auth/src/db/", "c-database"],
["packages/better-auth/src/adapters/", "c-database"],
["packages/drizzle-adapter/", "c-database"],
["packages/prisma-adapter/", "c-database"],
["packages/mongo-adapter/", "c-database"],
["packages/kysely-adapter/", "c-database"],
["packages/memory-adapter/", "c-database"],
["packages/expo/", "c-platform"],
["packages/electron/", "c-platform"],
["packages/better-auth/src/integrations/", "c-platform"],
["packages/cli/", "c-devtools"],
["packages/better-auth/src/plugins/open-api/", "c-devtools"],
["packages/telemetry/", "c-devtools"],
["packages/i18n/", "c-devtools"],
["packages/test-utils/", "c-devtools"],
["packages/better-auth/src/plugins/jwt/", "c-core"],
["packages/better-auth/src/plugins/bearer/", "c-core"],
["packages/better-auth/src/plugins/multi-session/", "c-core"],
["packages/better-auth/src/plugins/custom-session/", "c-core"],
["packages/redis-storage/", "c-core"],
["packages/better-auth/", "c-core"],
["packages/core/", "c-core"],
["docs/", "c-docs"],
["demo/", "c-docs"],
[".github/", "c-devops"],
["e2e/", "c-devops"],
];
export interface ConventionalCommit {
type: string;
scope: string;
subject: string;
breaking: boolean;
}
export function parseConventionalCommit(title: string): ConventionalCommit {
const typeMatch = title.match(/^([a-z]+)/);
const type = typeMatch?.[1] ?? "";
const scopeMatch = title.match(/^[a-z]+\(([^)]+)\)/);
const scope = scopeMatch?.[1] ?? "";
const breaking = /^[a-z]+(\([^)]+\))?!:/.test(title);
const subject = title.replace(/^[a-z]+(\([^)]+\))?!?:\s*/, "");
return { type, scope, subject, breaking };
}
export function scopeToDomain(scope: string): string | undefined {
return SCOPE_TO_DOMAIN[scope];
}
export function classifyDomain(filePath: string): string | undefined {
for (const [prefix, domain] of PATH_TO_DOMAIN) {
if (filePath.startsWith(prefix)) return domain;
}
return undefined;
}
export function resolveDomain(
scope: string | undefined,
changedFiles: string[],
): string {
if (scope) {
const domain = scopeToDomain(scope);
if (domain) return domain;
}
const counts: Record<string, number> = {};
for (const file of changedFiles) {
const domain = classifyDomain(file);
if (domain) {
counts[domain] = (counts[domain] ?? 0) + 1;
}
}
const domains = Object.keys(counts);
if (domains.length === 0) return "c-devops";
if (domains.length >= 3) return "c-core";
return domains.sort((a, b) => (counts[b] ?? 0) - (counts[a] ?? 0))[0]!;
}
export function mapTypeToBump(
type: string,
breaking: boolean,
): "patch" | "minor" | "major" | "skip" {
if (breaking) return "major";
switch (type) {
case "fix":
case "perf":
case "refactor":
return "patch";
case "feat":
return "minor";
case "chore":
case "docs":
case "ci":
case "test":
case "style":
case "build":
return "skip";
default:
return "patch";
}
}
export const DOMAIN_LABELS = [
"c-core",
"c-database",
"c-oauth",
"c-credentials",
"c-identity",
"c-organization",
"c-security",
"c-enterprise",
"c-payments",
"c-platform",
"c-devtools",
"c-docs",
"c-devops",
] as const;
export type DomainLabel = (typeof DOMAIN_LABELS)[number];

View File

@@ -59,14 +59,9 @@ jobs:
persist-credentials: false
token: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: '.nvmrc'
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Analyze PR
id: analyze
@@ -74,7 +69,7 @@ jobs:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.issue.number }}
FORCE: 'true'
run: npx tsx .github/scripts/auto-changeset.ts
run: node .github/scripts/auto-changeset.ts
- name: Fetch PR diff
id: diff
@@ -107,7 +102,6 @@ jobs:
PR title: ${{ steps.analyze.outputs.pr_title }}
Bump type: ${{ steps.analyze.outputs.bump }}
Domain: ${{ steps.analyze.outputs.domain }}
Changed files:
${{ steps.analyze.outputs.changed_files }}

View File

@@ -18,22 +18,3 @@ jobs:
with:
sync-labels: false
dot: true
- name: Refine cross-cutting labels
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
# Count primary domain labels only (exclude supporting labels like c-docs, c-devops)
ALL_LABELS=$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json labels --jq '[.labels[].name | select(startswith("c-"))]')
PRIMARY=$(echo "$ALL_LABELS" | jq '[.[] | select(. != "c-docs" and . != "c-devops")]')
COUNT=$(echo "$PRIMARY" | jq 'length')
if [ "$COUNT" -ge 3 ]; then
echo "Cross-cutting PR: $COUNT primary domain labels. Keeping only c-core."
# Remove all c-* labels except c-core, c-docs, c-devops
for label in $(echo "$PRIMARY" | jq -r '.[] | select(. != "c-core")'); do
gh pr edit "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --remove-label "$label" || true
done
gh pr edit "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --add-label "c-core" || true
fi