-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcommitlint.config.js
More file actions
93 lines (87 loc) · 4.2 KB
/
Copy pathcommitlint.config.js
File metadata and controls
93 lines (87 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Commitlint configuration for conventional commits with required scopes
// https://commitlint.js.org/
module.exports = {
extends: ['@commitlint/config-conventional'],
// GitHub's auto-generated revert format ("Revert ...") has no scope — skip linting for those
ignores: [
(message) => message.startsWith('Revert '),
// Dependabot/Renovate bump messages have unpredictable length (package
// names, dirs, groups). Types follow .github/dependabot.yml and renovate.json.
// Granted only when the title check says a bot opened the PR: a bump-shaped
// title is text anyone can type, and this skips the length and full-stop
// rules on the only commit message that reaches main.
...(process.env.PR_AUTHOR_IS_BOT === 'true'
? [(message) => /^(build|chore|ci)\(deps\): (bump|update) .+ to .+/.test(message)]
: []),
],
plugins: [
{
rules: {
// Scope is required everywhere except `ci`. CI work never reaches the
// changelog, and most of it (path filters, sharding, the gate) has no
// surface to name: 83 of 103 CI PRs audited in 2026-09 reached for a
// `ci` scope, which is only the type said twice.
'scope-required-unless-ci': ({ type, scope }) => [
type === 'ci' || Boolean(scope),
'scope may not be empty (only ci may omit it)',
],
},
},
],
rules: {
'scope-empty': [0],
'scope-required-unless-ci': [2, 'always'],
// Allowed scopes. No scope repeats a type name: `fix(ci)` and `docs(docs)`
// were how CI and docs work ended up under the wrong type
// (tests/unit/scripts/test_commit_type_config.py).
'scope-enum': [2, 'always', [
// Technical areas
'frontend', // React components, hooks, pages, styles
'api', // FastAPI endpoints, Python backend logic
'sync', // Go sync services, CampMinder integration
'pb', // PocketBase schema, migrations, Go extensions
'solver', // OR-Tools constraint solver
'docker', // Dockerfiles, compose, container configs
'auth', // Authentication, OAuth/OIDC (permissions → rbac)
'google', // Google Sheets/Drive API integration
'security', // Security hardening, CVE fixes, vulnerability remediation
'logging', // Logging configuration
'release', // Release scripts, versioning
'config', // Configuration files (not code)
'deps', // Dependency updates
'tests', // Test infrastructure (not test: type)
'scripts', // Development and utility scripts
'harness', // Agent tooling: .claude/, CLAUDE.md, skills, lefthook
// Domain features (cross-cutting)
'metrics', // Analytics, dashboards, statistics
'graph', // Social network graph features
'data', // Data models, schema changes
'rbac', // Role-based access control
]],
// Allowed types. cliff.toml must give each one a parser of its own, and
// tests/unit/scripts/test_commit_type_config.py checks that it does.
'type-enum': [2, 'always', [
// Which one: docs/reference/commit-conventions.md.
'feat', // Staff can do something new → Features (minor)
'improve', // Something existing works or reads better → Improvements
'fix', // The deployed app was wrong → Bug Fixes
'perf', // Deployed app faster, same output → Performance
'build', // Shipped deps, Dockerfiles, toolchain → Dependencies & Build
'refactor', // Nothing anyone can reach changes → Internal
'revert', // Undoes a merged PR → Reverts
'docs', // Documentation → skipped
'test', // Tests only → skipped
'chore', // Agent/dev tooling, dev-only deps → skipped
'ci', // CI and release machinery → skipped
]],
// Enforce lowercase for type and scope
'type-case': [2, 'always', 'lower-case'],
'scope-case': [2, 'always', 'lower-case'],
// Subject case not enforced - allow proper nouns (Python, Docker, etc.)
'subject-case': [0],
'subject-full-stop': [2, 'never', '.'],
// Reasonable length limits
'header-max-length': [2, 'always', 120],
'body-max-line-length': [1, 'always', 200],
},
};