mandrel-platform 0.17.2 → 0.18.0
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/README.md +220 -14
- package/config/commitlint.base.mjs +36 -0
- package/config/repo-settings.schema.json +78 -0
- package/package.json +2 -1
- package/scripts/apply-uptime-monitors.mjs +378 -0
- package/scripts/apply-uptime-monitors.test.mjs +372 -0
- package/scripts/check-repo-settings.mjs +363 -0
- package/scripts/check-repo-settings.test.mjs +320 -0
- package/scripts/check-required-contexts.mjs +247 -129
- package/scripts/check-required-contexts.test.mjs +137 -0
- package/scripts/check-ruleset.mjs +435 -0
- package/scripts/check-ruleset.test.mjs +439 -0
- package/scripts/check-wrangler-baseline.mjs +514 -0
- package/scripts/check-wrangler-baseline.test.mjs +454 -0
- package/scripts/platform-sync.mjs +533 -5
- package/scripts/platform-sync.test.mjs +477 -0
- package/templates/workflows/deploy-staging.yml +86 -0
- package/templates/workflows/uptime-apply.yml +54 -0
package/README.md
CHANGED
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
# mandrel-platform
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The **single source of truth (SSOT)** for CI/CD, security, and configuration
|
|
4
|
+
across the Mandrel fleet. A consumer repo adopts this platform once and inherits
|
|
5
|
+
a converged, hardened baseline — reusable GitHub Actions workflows, a composite
|
|
6
|
+
toolchain action, importable config bases, guardrail scripts, edge-security
|
|
7
|
+
middleware, a Renovate preset, and operator runbook templates — instead of
|
|
8
|
+
hand-maintaining its own copies and drifting apart over time.
|
|
9
|
+
|
|
10
|
+
## What's in scope
|
|
11
|
+
|
|
12
|
+
| Pillar | What it provides |
|
|
13
|
+
| ------ | ---------------- |
|
|
14
|
+
| **Reusable workflows** | `workflow_call` CI, deploy, secret-scan, release, and CodeQL pipelines, consumed by tag/SHA pin. |
|
|
15
|
+
| **Composite action** | `setup-toolchain` — pnpm + Node + frozen install in one step. |
|
|
16
|
+
| **Config bases (npm)** | `extends`-able baselines: TypeScript, Biome, Knip, Stryker, commitlint, dependency-cruiser, size-limit, Lighthouse. |
|
|
17
|
+
| **Edge-security middleware (npm)** | Per-env closed-allowlist CORS, security headers, and app-layer rate limiting for Astro + Hono. |
|
|
18
|
+
| **Guardrail scripts (npm)** | Dependency-free policy checks: CVE gate, action-pin ratchet, coverage floor, destructive-migration guard, workflow-portability, required-contexts, docs-staleness. |
|
|
19
|
+
| **Renovate preset** | Shared dependency-update policy, including auto-bumping this repo's own `uses:` pins. |
|
|
20
|
+
| **Supply-chain config** | pnpm-native hardening fragment (`blockExoticSubdeps`, `trustPolicy`, 7-day `minimumReleaseAge`). |
|
|
21
|
+
| **Adoption & drift control** | `platform-sync` (adopt/repair) plus a scheduled cross-consumer pin-drift dashboard and auto-repair-PR loop. |
|
|
22
|
+
| **Runbook templates** | Copyable thin-stub operator runbooks that link back to the canonical process docs. |
|
|
23
|
+
|
|
24
|
+
**Out of scope.** mandrel-platform is not an application and ships no runtime
|
|
25
|
+
service — it deploys nothing of its own beyond its release train. Consumers keep
|
|
26
|
+
every project-specific knob (entrypoints, budgets, score floors, deploy targets)
|
|
27
|
+
local: the platform sets the floor, the consumer sets the ceiling. The `.agents/`
|
|
28
|
+
tree in this repo is the Mandrel agent framework used to *develop* the platform
|
|
29
|
+
(sourced from the separate `mandrel` CLI) — it is dev-time only and is **not**
|
|
30
|
+
part of the published npm package, whose `files` allowlist ships only `config/`,
|
|
31
|
+
`default.json`, `scripts/`, and `templates/`.
|
|
5
32
|
|
|
6
33
|
**Docs:** [reusable-workflows.md](docs/reusable-workflows.md) (the `workflow_call`
|
|
7
34
|
contract) · [decisions.md](docs/decisions.md) (decision log). Status, the
|
|
@@ -11,15 +38,24 @@ consumer convergence matrix, and the forward roadmap are tracked privately.
|
|
|
11
38
|
|
|
12
39
|
## Reusable workflows
|
|
13
40
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
Five workflows expose a stable `workflow_call` contract and are consumed by
|
|
42
|
+
tag/SHA pin. Configure your callers from
|
|
43
|
+
**[docs/reusable-workflows.md](docs/reusable-workflows.md)** — the authoritative
|
|
44
|
+
reference for input types, defaults, when-to-override, the frozen
|
|
45
|
+
`{CLOUDFLARE_*, TURSO_*}` deploy-secret allowlist, the single `ci-required`
|
|
46
|
+
aggregator context, and the pin-by-tag/SHA versioning model.
|
|
47
|
+
|
|
48
|
+
| Workflow | Purpose |
|
|
49
|
+
| -------- | ------- |
|
|
50
|
+
| [`pr-quality.yml`](docs/reusable-workflows.md#pr-qualityyml) | Tiered PR gate — lint/format → typecheck → unit → contract → e2e/smoke → migration-guard → security → osv-scan, each tier independently toggled, behind one `ci-required` aggregator. |
|
|
51
|
+
| [`deploy-cloudflare.yml`](docs/reusable-workflows.md#deploy-cloudflareyml) | Defence-in-depth Cloudflare deploy with a frozen deploy-secret allowlist. |
|
|
52
|
+
| [`secret-scan-push.yml`](docs/reusable-workflows.md#secret-scan-pushyml) | Full-history gitleaks secret scan on push to the default branch. |
|
|
53
|
+
| [`release-automation.yml`](docs/reusable-workflows.md#release-automationyml) | Conventional-commit release lifecycle (version bump + `CHANGELOG.md` + tag) via release-please. |
|
|
54
|
+
| [`codeql.yml`](docs/reusable-workflows.md#codeqlyml) | CodeQL SAST analysis — dual-mode: runs on this repo's push/PR/schedule **and** is `workflow_call`-consumable. |
|
|
55
|
+
|
|
56
|
+
> `smoke-dispatch.yml` is a **platform-internal** cross-repo smoke trigger
|
|
57
|
+
> (`push` / `workflow_dispatch`, not `workflow_call`). It appears in the
|
|
58
|
+
> reference for completeness but is not part of the consumer caller surface.
|
|
23
59
|
|
|
24
60
|
`release-automation.yml` extends the platform from CI/deploy into the **full
|
|
25
61
|
release lifecycle**: a thin caller gets conventional-commit-driven version
|
|
@@ -147,6 +183,30 @@ mutate set (`stryker.config.json`):
|
|
|
147
183
|
}
|
|
148
184
|
```
|
|
149
185
|
|
|
186
|
+
#### `commitlint.base.mjs`
|
|
187
|
+
|
|
188
|
+
Single-sources the conventional-commit **type-enum** — the eleven types
|
|
189
|
+
(`feat`, `fix`, `perf`, `refactor`, `revert`, `docs`, `style`, `chore`,
|
|
190
|
+
`test`, `build`, `ci`) documented in
|
|
191
|
+
[`.agents/rules/git-conventions.md`](.agents/rules/git-conventions.md) — so
|
|
192
|
+
consumers stop hand-copying the list into their own
|
|
193
|
+
`commitlint.config.js`. Extends `@commitlint/config-conventional` for
|
|
194
|
+
everything else (header casing/length, body/footer blank-line rules) and
|
|
195
|
+
narrows `type-enum` to the fleet list. commitlint supports a native
|
|
196
|
+
`extends`, so a consumer's local config reduces to the extend plus any
|
|
197
|
+
repo-specific scope enforcement (`commitlint.config.js`):
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
export default {
|
|
201
|
+
extends: ["mandrel-platform/commitlint.base.mjs"],
|
|
202
|
+
// repo-specific scope-enum, etc. — optional
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Keep this base's `type-enum`, the git-conventions.md prose list, and
|
|
207
|
+
`release-please-config.json`'s `changelog-sections` in sync when adding a
|
|
208
|
+
type — all three must agree.
|
|
209
|
+
|
|
150
210
|
#### `dependency-cruiser.base.json`
|
|
151
211
|
|
|
152
212
|
Shared dependency-cruiser rule set (no-circular, no-orphans,
|
|
@@ -362,6 +422,78 @@ is detected by Renovate).
|
|
|
362
422
|
|
|
363
423
|
---
|
|
364
424
|
|
|
425
|
+
### Guardrail & policy-check scripts
|
|
426
|
+
|
|
427
|
+
Beyond the CVE gate, the package ships a set of **dependency-free guardrail
|
|
428
|
+
lints** (`node`-only, nothing to install) that enforce cross-repo CI and
|
|
429
|
+
security invariants. Several are already wired into the reusable workflows, so a
|
|
430
|
+
consumer that adopts those inherits the check for free; each is also runnable
|
|
431
|
+
standalone (`node node_modules/mandrel-platform/scripts/<name>.mjs`) or copyable
|
|
432
|
+
into a repo's own `scripts/`.
|
|
433
|
+
|
|
434
|
+
| Script | Enforces | Wired into |
|
|
435
|
+
| ------ | -------- | ---------- |
|
|
436
|
+
| `check-coverage-threshold.mjs` | Coverage floor (lines/statements/functions/branches) read from `coverage-summary.json`; threshold `0` disables the gate. | `pr-quality.yml` (unit tier) |
|
|
437
|
+
| `check-destructive-migration.mjs` | Blocks `DROP` / `TRUNCATE` / `ALTER … DROP` (and Drizzle `.dropTable()`) in migration files unless a reviewer applies the override label. | `pr-quality.yml` (migration-guard tier, opt-in) |
|
|
438
|
+
| `check-workflow-portability.mjs` | Catches cross-repo footguns in reusable workflows / composite actions: relative `uses:` paths, `${{ }}` in `workflow_call` input metadata, and lagging first-party pins. | `pr-quality.yml` + `ci.yml` |
|
|
439
|
+
| `check-action-pins.mjs` | Ratchet requiring every third-party Action to be pinned to a full 40-char commit SHA (tag-swap defence); local and first-party refs are exempt. | `ci.yml` |
|
|
440
|
+
| `check-required-contexts.mjs` | Validates that every branch-protection required check in `main-protection.json` maps to a real CI job — no phantom required checks. Also **warns** (never blocks) when the caller file / display `name:` / caller job id diverge from the canonical `ci.yml` / `CI` / `ci` triplet ([details](docs/reusable-workflows.md#canonical-caller-naming-the-ciyml--ci--ci-triplet)). | `ci.yml` |
|
|
441
|
+
| `check-docs-staleness.mjs` | Lints markdown/JSON docs for known staleness patterns (stale URLs, expired dates, dead runbook paths); suppressible per-rule. | standalone |
|
|
442
|
+
| `check-repo-settings.mjs` | Cross-consumer dashboard for the GitHub-side repo-settings baseline (merge methods, squash source, auto-merge, Actions default token permissions, PR-approval-by-Actions) — see below. | standalone / `platform-sync --check-settings` |
|
|
443
|
+
|
|
444
|
+
> **`config/main-protection.schema.json`** is the JSON Schema for the
|
|
445
|
+
> branch-protection contract (`docs/runbooks/main-protection.json`) — required
|
|
446
|
+
> status checks, the aggregator job, upstream jobs, and enforcement flags.
|
|
447
|
+
> `check-required-contexts.mjs` validates the contract against the actual
|
|
448
|
+
> workflow job graph; see the
|
|
449
|
+
> [branch-protection runbook](docs/runbooks/branch-protection-setup.md).
|
|
450
|
+
|
|
451
|
+
> **`config/repo-settings.schema.json`** is the JSON Schema for the
|
|
452
|
+
> repo-settings baseline contract (`docs/runbooks/repo-settings.json`),
|
|
453
|
+
> sibling to `main-protection.schema.json` — merge methods, squash-commit
|
|
454
|
+
> source, auto-merge/delete-branch-on-merge, Actions default workflow token
|
|
455
|
+
> permissions, and whether Actions can approve pull requests. Fleet baseline
|
|
456
|
+
> decided 2026-07-01 (see `docs/decisions.md`): squash-only merges, squash
|
|
457
|
+
> source `PR_TITLE`/`PR_BODY`, auto-merge + delete-branch-on-merge on, Actions
|
|
458
|
+
> default token permissions `read`, `can_approve_pull_request_reviews` off.
|
|
459
|
+
>
|
|
460
|
+
> **Because `squashMergeCommitMessage` is `PR_BODY`, PR templates must stay
|
|
461
|
+
> commit-body-safe.** The PR description becomes the literal squash-commit
|
|
462
|
+
> body on `main`, which `release-please` and `commitlint` then parse — a
|
|
463
|
+
> template that injects checklist boilerplate, HTML comments, or
|
|
464
|
+
> non-conventional-commit prose into that body will land in commit history and
|
|
465
|
+
> can break changelog generation or commitlint's body rules. Keep PR templates
|
|
466
|
+
> to short, commit-message-safe prose, or put checklists in sections authors
|
|
467
|
+
> delete before merge.
|
|
468
|
+
>
|
|
469
|
+
> **Check + apply.** `scripts/check-repo-settings.mjs` is the GitHub-side
|
|
470
|
+
> drift dashboard (mirrors `check-pin-drift.mjs`'s shape: data-driven consumer
|
|
471
|
+
> registry — reuses `scripts/pin-drift-consumers.json` — injectable `gh`
|
|
472
|
+
> runner, `--json`/`--strict`). `scripts/platform-sync.mjs` gained a
|
|
473
|
+
> settings mode for the per-consumer check/apply flow:
|
|
474
|
+
>
|
|
475
|
+
> ```bash
|
|
476
|
+
> # Report drift for one consumer against the baseline — never mutates, never
|
|
477
|
+
> # fails the exit code unless the read itself errors (standing decision #10).
|
|
478
|
+
> node scripts/platform-sync.mjs --check-settings --consumer-repo dsj1984/domio
|
|
479
|
+
>
|
|
480
|
+
> # Same read, then PATCH the drifted fields to match the baseline.
|
|
481
|
+
> node scripts/platform-sync.mjs --apply-settings --consumer-repo dsj1984/domio
|
|
482
|
+
>
|
|
483
|
+
> # Preview what --apply-settings would PATCH without mutating anything.
|
|
484
|
+
> node scripts/platform-sync.mjs --apply-settings --dry-run --consumer-repo dsj1984/domio
|
|
485
|
+
> ```
|
|
486
|
+
>
|
|
487
|
+
> Both commands accept `--baseline <path>` (default:
|
|
488
|
+
> `docs/runbooks/repo-settings.json`) and `--json` for a machine-readable
|
|
489
|
+
> envelope. **Non-blocking by design** (standing decision #10, same posture as
|
|
490
|
+
> the pin-drift dashboard and `check-ruleset.mjs`): drift is reported, not a
|
|
491
|
+
> hard gate — it never fails CI on a consumer's `main`. Branch-protection
|
|
492
|
+
> ruleset drift is out of scope here (see the companion `check-ruleset.mjs`
|
|
493
|
+
> story); this contract covers repo-settings only.
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
365
497
|
## Renovate preset
|
|
366
498
|
|
|
367
499
|
The shared Renovate preset (`default.json`, also exposed at
|
|
@@ -464,12 +596,27 @@ reports `already in sync`):
|
|
|
464
596
|
actions (`actions/checkout`, …) are left untouched. The trailing
|
|
465
597
|
`# <ref>` comment is refreshed so the pin stays human-auditable and the
|
|
466
598
|
Renovate auto-bump rule above can track it.
|
|
467
|
-
2. **
|
|
599
|
+
2. **Checks CI-caller naming** (advisory only, Story #173). Reports whether
|
|
600
|
+
`.github/workflows/ci.yml` matches the canonical caller triplet — file
|
|
601
|
+
`ci.yml`, display name `CI`, caller job id `ci` (required context
|
|
602
|
+
`ci / ci-required`; see
|
|
603
|
+
[reusable-workflows.md § "Canonical caller naming"](docs/reusable-workflows.md#canonical-caller-naming-the-ciyml--ci--ci-triplet)).
|
|
604
|
+
Never renames or rewrites anything — a caller rename must land atomically
|
|
605
|
+
with its own branch-protection ruleset context update, which is a
|
|
606
|
+
deliberate per-consumer Story, not an automatic sync side-effect.
|
|
607
|
+
3. **Materializes runbook reference stubs** (§2.2 *link, don't copy*). Copies
|
|
468
608
|
the thin stubs from `templates/runbooks/` into the consumer's
|
|
469
609
|
`docs/runbooks/` **only when absent** — an already-adopted stub is skipped,
|
|
470
610
|
and a full local copy (no stub marker) is surfaced as a warning to
|
|
471
611
|
reconcile by hand, never silently overwritten.
|
|
472
|
-
|
|
612
|
+
4. **Materializes workflow caller templates.** Copies canonical callers from
|
|
613
|
+
`templates/workflows/` (e.g. `deploy-staging.yml`, the one-paved-road
|
|
614
|
+
`workflow_run` caller for `deploy-cloudflare.yml`'s CI-green guard — see
|
|
615
|
+
[`docs/reusable-workflows.md`](docs/reusable-workflows.md#deploy-cloudflareyml))
|
|
616
|
+
into the consumer's `.github/workflows/` — same link-don't-copy semantics:
|
|
617
|
+
only when absent, and a hand-authored file without the template marker is
|
|
618
|
+
surfaced as a warning rather than overwritten.
|
|
619
|
+
5. **Reconciles `extends`.** Prepends `github>dsj1984/mandrel-platform` to the
|
|
473
620
|
consumer's Renovate `extends` and `mandrel-platform/tsconfig.base.json` to
|
|
474
621
|
its `tsconfig.json` `extends`. The SSOT goes first so the consumer's own
|
|
475
622
|
later entries continue to override it.
|
|
@@ -481,16 +628,74 @@ result envelope on stdout).
|
|
|
481
628
|
|
|
482
629
|
---
|
|
483
630
|
|
|
631
|
+
## Drift control & auto-repair
|
|
632
|
+
|
|
633
|
+
The platform actively keeps consumers converged rather than trusting them to
|
|
634
|
+
stay in sync by hand. Two scheduled workflows run this loop against the consumer
|
|
635
|
+
registry in
|
|
636
|
+
[`scripts/pin-drift-consumers.json`](scripts/pin-drift-consumers.json):
|
|
637
|
+
|
|
638
|
+
- **Detect** — [`pin-drift.yml`](.github/workflows/pin-drift.yml) (weekly +
|
|
639
|
+
`workflow_dispatch`) runs `check-pin-drift.mjs`, a cross-consumer dashboard
|
|
640
|
+
that flags split pins (multiple mandrel-platform SHAs in one repo), release
|
|
641
|
+
lag, npm lag, and npm-vs-workflow surface skew. Advisory by default; `--strict`
|
|
642
|
+
turns drift into a failure. See the
|
|
643
|
+
[pin-drift dashboard runbook](docs/runbooks/pin-drift-dashboard.md).
|
|
644
|
+
- **Repair** — [`platform-sync-repair.yml`](.github/workflows/platform-sync-repair.yml)
|
|
645
|
+
runs `platform-repair.mjs`, which clones each drifting consumer, runs
|
|
646
|
+
`platform-sync`, and opens (or updates) an **idempotent** repair PR on a stable
|
|
647
|
+
head branch. Requires a fine-grained `PIN_REPAIR_TOKEN` scoped to the consumer
|
|
648
|
+
repos' contents + pull-requests.
|
|
649
|
+
|
|
650
|
+
`update-semgrep-rules.mjs` is a related maintenance script that vendors Semgrep's
|
|
651
|
+
`p/default` ruleset — filtered to the languages actually in-repo — into
|
|
652
|
+
`.semgrep/rules.json` against a pinned Semgrep version, so the SAST step in
|
|
653
|
+
`pr-quality.yml` scans deterministically. Run it deliberately when bumping the
|
|
654
|
+
ruleset, not on every CI run.
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
## Runbook templates
|
|
659
|
+
|
|
660
|
+
`templates/runbooks/` ships **copyable thin-stub** operator runbooks — one per
|
|
661
|
+
canonical runbook in [`docs/runbooks/`](docs/runbooks). The adoption model is
|
|
662
|
+
*link, don't copy*: each stub links to the canonical process doc (the source of
|
|
663
|
+
truth) and carries only `<PLACEHOLDER>` slots for project-specific values, so an
|
|
664
|
+
upstream process change is picked up by re-reading the link rather than
|
|
665
|
+
re-authoring the stub. `platform-sync` materializes them for you — link-only, and
|
|
666
|
+
never clobbering a stub you have already filled in.
|
|
667
|
+
|
|
668
|
+
| Stub | Canonical runbook |
|
|
669
|
+
| ---- | ----------------- |
|
|
670
|
+
| `deploy-promotion.md` | staging → production promotion |
|
|
671
|
+
| `incident-response.md` | severity, escalation, postmortem |
|
|
672
|
+
| `database-backup-restore.md` | backup, PITR, restore/rollback |
|
|
673
|
+
| `observability.md` | logs, Sentry, uptime, metrics |
|
|
674
|
+
| `post-deploy-smoke.md` | boot-smoke gate + diagnosis |
|
|
675
|
+
| `environments-provisioning.md` | env model + provisioning steps |
|
|
676
|
+
| `dependency-update.md` | Renovate, CVE gate, catalog |
|
|
677
|
+
| `branch-protection-setup.md` | aggregator required-check model |
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
484
681
|
## Development
|
|
485
682
|
|
|
486
683
|
```bash
|
|
487
|
-
# Install dependencies
|
|
684
|
+
# Install dependencies (packageManager: pnpm@11.5.2)
|
|
488
685
|
pnpm install
|
|
489
686
|
|
|
490
687
|
# Bootstrap agent scaffolding
|
|
491
688
|
pnpm run bootstrap
|
|
689
|
+
|
|
690
|
+
# Run the guardrail-script test suite (node:test)
|
|
691
|
+
pnpm test
|
|
492
692
|
```
|
|
493
693
|
|
|
694
|
+
Every script under `scripts/` (the guardrail lints, `platform-sync`,
|
|
695
|
+
`platform-repair`, `update-semgrep-rules`) carries a colocated `*.test.mjs`
|
|
696
|
+
suite run by `pnpm test`. The `.agents/` tree is the Mandrel agent framework this
|
|
697
|
+
repo is developed with — dev-time only, and not shipped in the npm package.
|
|
698
|
+
|
|
494
699
|
---
|
|
495
700
|
|
|
496
701
|
## Package exports
|
|
@@ -501,6 +706,7 @@ pnpm run bootstrap
|
|
|
501
706
|
| `mandrel-platform/biome.base.json` | `config/biome.base.json` |
|
|
502
707
|
| `mandrel-platform/knip.base.json` | `config/knip.base.json` |
|
|
503
708
|
| `mandrel-platform/stryker.base.json` | `config/stryker.base.json` |
|
|
709
|
+
| `mandrel-platform/commitlint.base.mjs` | `config/commitlint.base.mjs` |
|
|
504
710
|
| `mandrel-platform/dependency-cruiser.base.json` | `config/dependency-cruiser.base.json` |
|
|
505
711
|
| `mandrel-platform/size-limit.base.json` | `config/size-limit.base.json` |
|
|
506
712
|
| `mandrel-platform/lighthouse.base.json` | `config/lighthouse.base.json` |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commitlint.base.mjs — shared commitlint config for mandrel-platform
|
|
3
|
+
* consumers.
|
|
4
|
+
*
|
|
5
|
+
* Single-sources the conventional-commit type-enum from
|
|
6
|
+
* `.agents/rules/git-conventions.md` (§ Conventional Commits) so the eleven
|
|
7
|
+
* allowed types live in exactly one place instead of being hand-copied into
|
|
8
|
+
* each consumer's own `commitlint.config.js`. Keep this list, the
|
|
9
|
+
* git-conventions.md prose list, and `release-please-config.json`'s
|
|
10
|
+
* `changelog-sections` in sync when adding a type — all three must agree.
|
|
11
|
+
*
|
|
12
|
+
* Extends `@commitlint/config-conventional` for everything else (header
|
|
13
|
+
* casing/length, body/footer leading-blank-line, etc.) and narrows
|
|
14
|
+
* `type-enum` to the fleet's eleven types.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const TYPE_ENUM = [
|
|
18
|
+
"feat",
|
|
19
|
+
"fix",
|
|
20
|
+
"perf",
|
|
21
|
+
"refactor",
|
|
22
|
+
"revert",
|
|
23
|
+
"docs",
|
|
24
|
+
"style",
|
|
25
|
+
"chore",
|
|
26
|
+
"test",
|
|
27
|
+
"build",
|
|
28
|
+
"ci",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
extends: ["@commitlint/config-conventional"],
|
|
33
|
+
rules: {
|
|
34
|
+
"type-enum": [2, "always", TYPE_ENUM],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/dsj1984/mandrel-platform/blob/main/config/repo-settings.schema.json",
|
|
4
|
+
"title": "Repository Settings Baseline Contract",
|
|
5
|
+
"description": "Defines the GitHub-side repository settings baseline (merge methods, squash-commit source, auto-merge, Actions default token permissions, PR-approval-by-Actions) the fleet must converge on. Validated by scripts/check-repo-settings.mjs against each consumer's live settings (gh api repos/{owner}/{repo}); applied where safe by scripts/platform-sync.mjs --apply-settings. Non-blocking by design (standing decision #10) — drift is reported, never a hard gate on a consumer's main.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"allowSquashMerge",
|
|
9
|
+
"allowMergeCommit",
|
|
10
|
+
"allowRebaseMerge",
|
|
11
|
+
"squashMergeCommitTitle",
|
|
12
|
+
"squashMergeCommitMessage",
|
|
13
|
+
"deleteBranchOnMerge",
|
|
14
|
+
"allowAutoMerge",
|
|
15
|
+
"actionsDefaultWorkflowPermissions",
|
|
16
|
+
"actionsCanApprovePullRequestReviews"
|
|
17
|
+
],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"$schema": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "JSON Schema reference."
|
|
23
|
+
},
|
|
24
|
+
"allowSquashMerge": {
|
|
25
|
+
"type": "boolean",
|
|
26
|
+
"description": "Whether squash merging is allowed. The fleet baseline requires this true (squash-only).",
|
|
27
|
+
"default": true
|
|
28
|
+
},
|
|
29
|
+
"allowMergeCommit": {
|
|
30
|
+
"type": "boolean",
|
|
31
|
+
"description": "Whether merge commits are allowed. The fleet baseline requires this false (squash-only).",
|
|
32
|
+
"default": false
|
|
33
|
+
},
|
|
34
|
+
"allowRebaseMerge": {
|
|
35
|
+
"type": "boolean",
|
|
36
|
+
"description": "Whether rebase merging is allowed. The fleet baseline requires this false (squash-only).",
|
|
37
|
+
"default": false
|
|
38
|
+
},
|
|
39
|
+
"squashMergeCommitTitle": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Source of the squash-commit title.",
|
|
42
|
+
"enum": ["PR_TITLE", "COMMIT_OR_PR_TITLE"],
|
|
43
|
+
"default": "PR_TITLE"
|
|
44
|
+
},
|
|
45
|
+
"squashMergeCommitMessage": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Source of the squash-commit message body. The fleet baseline requires PR_BODY — commits landing on main feed release-please + commitlint, so the PR body becomes the literal commit body and must stay commit-body-safe (see the _note below).",
|
|
48
|
+
"enum": ["PR_BODY", "COMMIT_MESSAGES", "BLANK"],
|
|
49
|
+
"default": "PR_BODY"
|
|
50
|
+
},
|
|
51
|
+
"deleteBranchOnMerge": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"description": "Whether head branches are auto-deleted after a merge.",
|
|
54
|
+
"default": true
|
|
55
|
+
},
|
|
56
|
+
"allowAutoMerge": {
|
|
57
|
+
"type": "boolean",
|
|
58
|
+
"description": "Whether GitHub's native auto-merge feature is enabled for the repo.",
|
|
59
|
+
"default": true
|
|
60
|
+
},
|
|
61
|
+
"actionsDefaultWorkflowPermissions": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "Default GITHUB_TOKEN permissions granted to Actions workflows. The fleet baseline requires 'read' (least-privilege) — a workflow that needs write scopes requests it explicitly via a `permissions:` block.",
|
|
64
|
+
"enum": ["read", "write"],
|
|
65
|
+
"default": "read"
|
|
66
|
+
},
|
|
67
|
+
"actionsCanApprovePullRequestReviews": {
|
|
68
|
+
"type": "boolean",
|
|
69
|
+
"description": "Whether GitHub Actions workflows are permitted to approve pull requests. The fleet baseline requires this false — an automated actor approving its own (or any) PR is a bypass of human review, not a feature.",
|
|
70
|
+
"default": false
|
|
71
|
+
},
|
|
72
|
+
"_note": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "Human-readable note for maintainers."
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"_note": "PR templates must stay commit-body-safe: with squashMergeCommitMessage=PR_BODY, the full PR description becomes the literal squash-commit body on main, which release-please and commitlint then parse. A PR template that injects checklist boilerplate, HTML comments, or non-conventional-commit prose into that body will land in the commit history and can break release-please's changelog generation or commitlint's body-line rules. Keep PR templates short, commit-message-safe prose, or move checklists into template sections that authors delete before merge."
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mandrel-platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Shared CI/deploy workflows, composite toolchain action, npm config package, Renovate preset, and operator runbook templates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"./biome.base.json": "./config/biome.base.json",
|
|
22
22
|
"./knip.base.json": "./config/knip.base.json",
|
|
23
23
|
"./stryker.base.json": "./config/stryker.base.json",
|
|
24
|
+
"./commitlint.base.mjs": "./config/commitlint.base.mjs",
|
|
24
25
|
"./dependency-cruiser.base.json": "./config/dependency-cruiser.base.json",
|
|
25
26
|
"./size-limit.base.json": "./config/size-limit.base.json",
|
|
26
27
|
"./lighthouse.base.json": "./config/lighthouse.base.json",
|