@rheged-studio/commitlint-config 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rob Easthope
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # @acme-skunkworks/commitlint-config
2
+
3
+ Shared [commitlint](https://commitlint.js.org) configuration for Rheged Studio packages.
4
+
5
+ It **single-sources** the estate's allowed [Conventional Commit](https://www.conventionalcommits.org) types into one published package, consumed by both layers of the commit gate so the ruleset can never drift between them:
6
+
7
+ - the reusable CI workflow (`reusable-validate-commits.yml`) that validates a PR's `base..head` commit range, and
8
+ - the local husky `pre-push` hook that runs the same range check before a push ever reaches CI.
9
+
10
+ It extends [`@commitlint/config-conventional`](https://www.npmjs.com/package/@commitlint/config-conventional) and retains all of its defaults — only the list of allowed commit types is pinned to the estate's set.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ pnpm add -D @acme-skunkworks/commitlint-config @commitlint/cli
16
+ ```
17
+
18
+ `@commitlint/config-conventional` is a runtime dependency of this package, so installing this config pulls it in automatically — you do not need to add it yourself.
19
+
20
+ ## Usage
21
+
22
+ Point your commitlint config at this package:
23
+
24
+ ```js
25
+ // commitlint.config.mjs
26
+ export default {
27
+ extends: ["@acme-skunkworks/commitlint-config"],
28
+ };
29
+ ```
30
+
31
+ The example uses `.mjs` so the `export default` works regardless of the consuming project's module type. In an ESM project (`"type": "module"`) a plain `commitlint.config.js` works too; in a CommonJS project use `.mjs` (as above) or `module.exports` in a `.js`/`.cjs` file.
32
+
33
+ Then validate a commit-message range — exactly what the CI workflow and the pre-push hook do:
34
+
35
+ ```sh
36
+ # lint every commit on the branch that isn't on the base
37
+ pnpm exec commitlint --from "origin/main" --to "HEAD"
38
+ ```
39
+
40
+ ## Allowed commit types
41
+
42
+ The `type-enum` rule is set explicitly and is aligned to the estate's release-please bump rules:
43
+
44
+ | Type | Release bump | Notes |
45
+ | ---------- | ------------ | -------------------------------------- |
46
+ | `feat` | minor | A new feature |
47
+ | `fix` | patch | A bug fix |
48
+ | `perf` | patch | A performance improvement |
49
+ | `revert` | patch | Reverts a previous commit |
50
+ | `chore` | none | Tooling / housekeeping |
51
+ | `docs` | none | Documentation only |
52
+ | `ci` | none | CI configuration and pipelines |
53
+ | `build` | none | Build system or external dependencies |
54
+ | `refactor` | none | Neither fixes a bug nor adds a feature |
55
+ | `test` | none | Adding or correcting tests |
56
+ | `style` | none | Formatting; no code-behaviour change |
57
+
58
+ A `!` marker or a `BREAKING CHANGE:` footer promotes any type to a **major** bump.
59
+
60
+ Everything else is inherited from `@commitlint/config-conventional` unchanged, including:
61
+
62
+ - a non-empty commit type and subject,
63
+ - the header max-length limit, and
64
+ - `defaultIgnores` — merge (`Merge …`), revert (`Revert …`), `fixup!` and `squash!` messages are skipped automatically, so this config never needs to blanket-ignore them by author identity.
65
+
66
+ ## Bot-authored commits
67
+
68
+ **There is no identity denylist.** Bots are validated exactly like everyone else — the config has no `ignores` entry, and none should be added. Every automated commit producer in the estate already emits a Conventional subject, verified against this ruleset:
69
+
70
+ | Producer | Subject |
71
+ | ------------------------------- | ------------------------------------------------- |
72
+ | Dependabot (`github-actions`) | `ci(deps): bump the actions group with 3 updates` |
73
+ | Dependabot (npm) | `chore(deps): …` / `chore(deps-dev): …` |
74
+ | release-please | `chore: release main` |
75
+ | `reusable-changelog-enrich.yml` | `chore(changelog): enrich post-merge metadata` |
76
+
77
+ Dependabot's subjects come from a `commit-message` template in each repo's `.github/dependabot.yml`, which sets `prefix` (plus `prefix-development` for the npm ecosystem) and `include: scope`. Without that template Dependabot falls back to inferring a prefix from recent commit history — which may yield a Conventional subject, but is not guaranteed to, and can leave a bare `Bump …` that would fail the gate. The explicit template is what makes the output deterministic, which is why it is a prerequisite for making the gate a required check rather than an optional nicety ([A-980](https://linear.app/rheged-studio/issue/A-980)).
78
+
79
+ Two inherited rules are worth knowing when reading a bot commit:
80
+
81
+ - **`header-max-length` (100) is the one real ceiling.** This is why Dependabot bumps are grouped — a grouped subject stays short (`bump the actions group with 3 updates`), whereas an ungrouped multi-package subject can run past 100 characters and fail.
82
+ - **`body-max-line-length` (100) ignores unbreakable lines.** Dependabot bodies contain compare URLs well over 100 characters; those pass, because the rule only trips on over-long lines that could have been wrapped. A long prose line still fails.
83
+
84
+ Should a bot ever need an exception, prefer fixing its commit template. A per-identity `ignores` entry is a documented last resort, not a first move.
85
+
86
+ ## Versioning: float on latest
87
+
88
+ The gate is designed to track this config's **latest published version** rather than pinning it, mirroring how the estate floats on its shared GitHub workflows (`@v1`). The reusable CI workflow installs the config fresh on each run, so a caret range with no committed lockfile resolves to the latest published version — a ruleset change reaches the gate on its next run, with no per-repo bump PR.
89
+
90
+ Where a consumer instead commits a lockfile (for example the local pre-push hook running in a repo that pins its dev-dependencies), the resolved version stays put until it is updated: the package is Dependabot-versioned like the estate's other shared packages, so those consumers get update PRs. The trade-off is deliberate — because a ruleset change reaches the floating consumers immediately, edits must be made carefully.
91
+
92
+ ## Development
93
+
94
+ ```sh
95
+ pnpm install
96
+ pnpm build # tsc → dist/ (the published artifact)
97
+ pnpm test # vitest — behavioural tests over the resolved ruleset
98
+ pnpm tsc # type-check
99
+ pnpm lint # eslint
100
+ ```
101
+
102
+ The ruleset itself is `src/index.ts`; `src/index.test.ts` exercises the _effective_ config by resolving `extends` through `@commitlint/load` and linting sample messages with `@commitlint/lint`.
103
+
104
+ ## Licence
105
+
106
+ MIT
@@ -0,0 +1,23 @@
1
+ import type { UserConfig } from "@commitlint/types";
2
+ /**
3
+ * `@acme-skunkworks/commitlint-config` — the estate's shared commitlint ruleset.
4
+ *
5
+ * It single-sources the allowed Conventional-Commit types into one published
6
+ * package so that both consumers of the commit gate — the reusable CI workflow
7
+ * (`reusable-validate-commits.yml`) and the local husky `pre-push` range check —
8
+ * float on the *same* list with zero drift (A-823 / A-979).
9
+ *
10
+ * The config extends `@commitlint/config-conventional` and deliberately retains
11
+ * all of its defaults (header max length, non-empty type/subject, and the
12
+ * `defaultIgnores` for `Merge …` / `Revert …` / `fixup!` / `squash!` messages).
13
+ * Only `type-enum` is overridden.
14
+ *
15
+ * `type-enum` is set *explicitly* — even though the list currently mirrors
16
+ * config-conventional's own default — so that an upstream change to
17
+ * config-conventional's default type set can never silently move the estate's
18
+ * gate. The list is aligned to the release-please bump rules: `feat` → minor;
19
+ * `fix`/`perf`/`revert` → patch; the remaining types cut no release.
20
+ */
21
+ declare const config: UserConfig;
22
+ export default config;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,MAAM,EAAE,UAqBb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `@acme-skunkworks/commitlint-config` — the estate's shared commitlint ruleset.
3
+ *
4
+ * It single-sources the allowed Conventional-Commit types into one published
5
+ * package so that both consumers of the commit gate — the reusable CI workflow
6
+ * (`reusable-validate-commits.yml`) and the local husky `pre-push` range check —
7
+ * float on the *same* list with zero drift (A-823 / A-979).
8
+ *
9
+ * The config extends `@commitlint/config-conventional` and deliberately retains
10
+ * all of its defaults (header max length, non-empty type/subject, and the
11
+ * `defaultIgnores` for `Merge …` / `Revert …` / `fixup!` / `squash!` messages).
12
+ * Only `type-enum` is overridden.
13
+ *
14
+ * `type-enum` is set *explicitly* — even though the list currently mirrors
15
+ * config-conventional's own default — so that an upstream change to
16
+ * config-conventional's default type set can never silently move the estate's
17
+ * gate. The list is aligned to the release-please bump rules: `feat` → minor;
18
+ * `fix`/`perf`/`revert` → patch; the remaining types cut no release.
19
+ */
20
+ const config = {
21
+ extends: ["@commitlint/config-conventional"],
22
+ rules: {
23
+ "type-enum": [
24
+ 2, // error
25
+ "always",
26
+ [
27
+ "feat",
28
+ "fix",
29
+ "perf",
30
+ "revert",
31
+ "chore",
32
+ "docs",
33
+ "ci",
34
+ "build",
35
+ "refactor",
36
+ "test",
37
+ "style",
38
+ ],
39
+ ],
40
+ },
41
+ };
42
+ export default config;
package/package.json ADDED
@@ -0,0 +1,121 @@
1
+ {
2
+ "name": "@rheged-studio/commitlint-config",
3
+ "version": "1.0.1",
4
+ "description": "Shared commitlint configuration for ACME Skunkworks packages.",
5
+ "keywords": [
6
+ "commitlint",
7
+ "conventional-commits",
8
+ "config",
9
+ "rheged-studio"
10
+ ],
11
+ "homepage": "https://github.com/rheged-studio/commitlint-config#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/rheged-studio/commitlint-config/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/rheged-studio/commitlint-config.git"
18
+ },
19
+ "license": "MIT",
20
+ "author": {
21
+ "name": "Rob Easthope",
22
+ "url": "https://github.com/RobEasthope"
23
+ },
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "default": "./dist/index.js"
30
+ }
31
+ },
32
+ "main": "./dist/index.js",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "scripts": {
39
+ "act:ci": "act pull_request -W .github/workflows/ci.yml --eventpath .github/act-events/pull_request.json",
40
+ "act:list": "act --list",
41
+ "act:release:dry": "act push -W .github/workflows/pkg-release.yml",
42
+ "build": "tsc",
43
+ "ci:list": "gh run list --limit 10",
44
+ "ci:view": "gh run view",
45
+ "ci:watch": "gh run watch $(gh run list -L 1 --json databaseId -q '.[0].databaseId // empty')",
46
+ "clean": "rimraf node_modules dist",
47
+ "format": "prettier --write .",
48
+ "lint": "eslint 'src/**/*.ts' --cache --cache-location ./.eslintcache",
49
+ "lint:fix": "eslint 'src/**/*.ts' --fix --cache --cache-location ./.eslintcache",
50
+ "lint:md": "markdownlint-cli2 '**/*.{md,mdx}' '!**/node_modules/**' '!**/dist/**' '!**/.turbo/**' '!**/.astro/**' '!**/.claude/skills/**' '!**/.agents/skills/**' '!AGENTS.md'",
51
+ "lint:md:fix": "markdownlint-cli2 --fix '**/*.{md,mdx}' '!**/node_modules/**' '!**/dist/**' '!**/.turbo/**' '!**/.astro/**' '!**/.claude/skills/**' '!**/.agents/skills/**' '!AGENTS.md'",
52
+ "lint:sh": "bash -c 'if command -v shellcheck >/dev/null 2>&1; then shellcheck infrastructure/scripts/*.sh .husky/pre-commit .husky/pre-push .husky/commit-msg; elif [ \"$(uname -s)\" = \"Darwin\" ]; then echo \"⚠️ shellcheck not installed — skipping. Install: brew install shellcheck\"; else echo \"⚠️ shellcheck not installed — skipping. Install: apt-get install shellcheck\"; fi'",
53
+ "lint:workflows": "actionlint",
54
+ "lint:yaml": "yamllint .",
55
+ "prepare": "husky",
56
+ "prepublishOnly": "pnpm run build",
57
+ "release:manual": "pnpm run build && npm publish --access public --provenance=false",
58
+ "release:manual:dry": "pnpm run build && npm publish --access public --provenance=false --dry-run",
59
+ "sort-pkg-json": "sort-package-json",
60
+ "test": "vitest run",
61
+ "test:sh": "bash -c 'if command -v bats >/dev/null 2>&1; then bats infrastructure/tests/*.bats; elif [ \"$(uname -s)\" = \"Darwin\" ]; then echo \"⚠️ bats not installed — skipping. Install: brew install bats-core\"; else echo \"⚠️ bats not installed — skipping. Install: apt-get install bats\"; fi'",
62
+ "test:watch": "vitest",
63
+ "tsc": "tsc --noEmit && tsc -p tsconfig.tools.json",
64
+ "validate:changelog": "pnpm exec changelog-core validate"
65
+ },
66
+ "lint-staged": {
67
+ "**/*": [
68
+ "prettier --write --ignore-unknown"
69
+ ],
70
+ "**/*.{ts,tsx,js,mjs,cjs}": [
71
+ "bash -c 'pnpm exec eslint --fix --cache --cache-location ./.eslintcache \"$@\" || true' --"
72
+ ],
73
+ "**/package.json": [
74
+ "bash -c 'sort-package-json \"$@\" || true' --",
75
+ "bash -c 'pnpm exec eslint --fix --cache --cache-location ./.eslintcache \"$@\" || true' --"
76
+ ],
77
+ "**/*.{md,mdx}": [
78
+ "bash -c 'markdownlint-cli2 --fix --config ./.markdownlint-cli2.jsonc \"$@\" || true' --"
79
+ ],
80
+ "**/*.{yml,yaml}": [
81
+ "bash -c 'if command -v yamllint >/dev/null 2>&1; then yamllint \"$@\"; elif [ \"$(uname -s)\" = \"Darwin\" ]; then echo \"⚠️ yamllint not installed — skipping. Install with: brew install yamllint\"; else echo \"⚠️ yamllint not installed — skipping. Install with: pip install --user yamllint==1.37.1\"; fi' --"
82
+ ],
83
+ ".github/workflows/*.{yml,yaml}": [
84
+ "bash -c 'if command -v actionlint >/dev/null 2>&1; then actionlint \"$@\"; elif [ \"$(uname -s)\" = \"Darwin\" ]; then echo \"⚠️ actionlint not installed — skipping. Install with: brew install actionlint\"; else echo \"⚠️ actionlint not installed — skipping. Install via: bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/v1.7.5/scripts/download-actionlint.bash)\"; fi' --"
85
+ ]
86
+ },
87
+ "dependencies": {
88
+ "@commitlint/config-conventional": "^21.2.0",
89
+ "@commitlint/types": "^21.2.0"
90
+ },
91
+ "devDependencies": {
92
+ "@acme-skunkworks/changelog-core": "^1.1.1",
93
+ "@acme-skunkworks/eslint-config": "^1.1.3",
94
+ "@acme-skunkworks/markdownlint-config": "^3.0.0",
95
+ "@commitlint/cli": "^21.2.1",
96
+ "@commitlint/lint": "^21.2.0",
97
+ "@commitlint/load": "^21.2.0",
98
+ "@types/node": "^25.6.0",
99
+ "bats": "1.13.0",
100
+ "eslint": "^9.0.0",
101
+ "husky": "^9.1.7",
102
+ "jiti": "^2.7.0",
103
+ "lint-staged": "^16.3.2",
104
+ "markdownlint-cli2": "^0.18.1",
105
+ "prettier": "^3.0.0",
106
+ "prettier-plugin-tailwindcss": "^0.8.0",
107
+ "rimraf": "^6.0.1",
108
+ "sort-package-json": "^3.6.1",
109
+ "tsx": "^4.21.1",
110
+ "typescript": "^6.0.3",
111
+ "vitest": "^4.1.6"
112
+ },
113
+ "packageManager": "pnpm@10.33.0",
114
+ "engines": {
115
+ "node": ">=22"
116
+ },
117
+ "publishConfig": {
118
+ "access": "public",
119
+ "provenance": true
120
+ }
121
+ }