@keelcore/standards 0.9.7 → 0.10.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 +37 -26
- package/governance/ai/conduct.md +17 -26
- package/governance/api-management.md +91 -102
- package/governance/arb.md +55 -62
- package/governance/bash.md +117 -46
- package/governance/ci.md +165 -144
- package/governance/coding.md +89 -78
- package/governance/observability.md +77 -86
- package/governance/platform.md +81 -87
- package/governance/release-policy.md +126 -96
- package/governance/runtime.md +87 -95
- package/governance/security.md +77 -85
- package/governance/testing.md +66 -20
- package/package.json +1 -1
package/governance/ci.md
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
These rules govern all CI workflow and script work. They are non-negotiable.
|
|
4
4
|
|
|
5
|
-
**Maturity:** Required
|
|
6
|
-
**Version:** 1.0.0
|
|
7
|
-
**Last Reviewed:** 2026-03-09
|
|
5
|
+
**Maturity:** Required **Version:** 1.1.0 **Last Reviewed:** 2026-10-30
|
|
8
6
|
|
|
9
7
|
## Core Principle
|
|
10
8
|
|
|
11
|
-
CI workflow YAML is thin orchestration only. No build, test, lint, packaging, signing, or release logic lives
|
|
12
|
-
|
|
9
|
+
CI workflow YAML is thin orchestration only. No build, test, lint, packaging, signing, or release logic lives in YAML —
|
|
10
|
+
it lives in `scripts/` entrypoints.
|
|
13
11
|
|
|
14
12
|
## Workflow File Responsibilities
|
|
15
13
|
|
|
@@ -32,8 +30,8 @@ YAML may only contain:
|
|
|
32
30
|
|
|
33
31
|
## Script Entrypoints
|
|
34
32
|
|
|
35
|
-
One script per concern. Scripts are the canonical entry point for all CI logic. Workflow YAML invokes them;
|
|
36
|
-
|
|
33
|
+
One script per concern. Scripts are the canonical entry point for all CI logic. Workflow YAML invokes them; it does not
|
|
34
|
+
duplicate their logic.
|
|
37
35
|
|
|
38
36
|
## Makefile Entrypoints
|
|
39
37
|
|
|
@@ -43,59 +41,72 @@ it does not duplicate their logic.
|
|
|
43
41
|
- Multi-command steps must be aggregated into one target; YAML calls the target.
|
|
44
42
|
- Setup steps (tooling, fixtures) are also Makefile targets (e.g. `make setup-bats`).
|
|
45
43
|
- The Makefile is the single dev-facing interface: what CI calls, a developer can call locally.
|
|
46
|
-
- Every `scripts/**/*.sh` file (except sourced library files under `scripts/lib/`) MUST have a
|
|
47
|
-
|
|
44
|
+
- Every `scripts/**/*.sh` file (except sourced library files under `scripts/lib/`) MUST have a Makefile target that
|
|
45
|
+
invokes it — the script path must appear in a Makefile recipe.
|
|
46
|
+
- Every Makefile target MUST have at most **one active recipe line**, and that line MUST invoke a single
|
|
47
|
+
`scripts/**/*.sh` script. "Active" excludes blank lines, `#` comments, and `@echo` lines used for human-readable
|
|
48
|
+
progress reporting. Concretely:
|
|
49
|
+
- Allowed: any number of `@echo` lines, followed by exactly one `bash scripts/X/Y.sh ...` line.
|
|
50
|
+
- Allowed: prerequisite chains in the target header (`mytarget: prereq1 prereq2`) — the rule applies to the recipe
|
|
51
|
+
body, not to prerequisite expansion.
|
|
52
|
+
- Disallowed: two or more substantive commands in one recipe (e.g. `cargo clean` followed by `rm -f data/...`).
|
|
53
|
+
Aggregate them into a single shell script and call it.
|
|
54
|
+
- Disallowed: invoking `cargo`, `go`, `bats`, `ln`, `mkdir`, `tee`, or any tool directly from the Makefile recipe.
|
|
55
|
+
Wrap the call in a `scripts/` shell script and have the Makefile invoke the script.
|
|
56
|
+
- Rationale: the Makefile is a router, not an orchestrator. All multi-step orchestration, flag handling, env var
|
|
57
|
+
assembly, and tool invocation belongs in `scripts/`, which is runnable identically by humans, by `make`, and by CI
|
|
58
|
+
YAML. This eliminates Makefile quoting traps, makes recipes shellcheck-able, and means every project concern has
|
|
59
|
+
exactly one canonical implementation surface.
|
|
48
60
|
|
|
49
61
|
## Universal Canonical Makefile Targets
|
|
50
62
|
|
|
51
|
-
Every project MUST define these language-independent targets. They form a stable, tool-agnostic
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
replace the universal targets.
|
|
63
|
+
Every project MUST define these language-independent targets. They form a stable, tool-agnostic interface for humans and
|
|
64
|
+
CI alike. Names are fixed; projects may not rename or skip them. Language-specific subtargets (e.g. `make lint-go`,
|
|
65
|
+
`make test-unit`) are additive — they do not replace the universal targets.
|
|
55
66
|
|
|
56
67
|
### Always required
|
|
57
68
|
|
|
58
|
-
| Target
|
|
59
|
-
|
|
60
|
-
| Build default artifact | `make build`
|
|
61
|
-
| Run all linters
|
|
62
|
-
| Run full test suite
|
|
63
|
-
| Run unit tests
|
|
64
|
-
| Run integration tests
|
|
65
|
-
| Remove artifacts
|
|
66
|
-
| Standards audit
|
|
67
|
-
| Code coverage report
|
|
68
|
-
| PR coverage gate
|
|
69
|
-
| PR policy gate
|
|
70
|
-
| Secret scan
|
|
71
|
-
| DCO check
|
|
72
|
-
| Trailing newlines
|
|
73
|
-
| Legal drift check
|
|
69
|
+
| Target | Makefile target | Script | Responsibility |
|
|
70
|
+
| ---------------------- | ------------------------ | ---------------------------------- | ------------------------------------------------------------------ |
|
|
71
|
+
| Build default artifact | `make build` | project-specific | Produce the primary deliverable (binary, image, package). |
|
|
72
|
+
| Run all linters | `make lint` | project-specific | Format check + static analysis. Must invoke `make lint-newlines`. |
|
|
73
|
+
| Run full test suite | `make test` | project-specific | Unit + integration tests in sequence. |
|
|
74
|
+
| Run unit tests | `make unit-test` | `scripts/test/ci.sh` | Unit tests only; produces JUnit XML and coverage profile. |
|
|
75
|
+
| Run integration tests | `make integration-test` | project-specific | BATS / end-to-end tests against a built artifact. |
|
|
76
|
+
| Remove artifacts | `make clean` | — | Remove `dist/` and generated files. |
|
|
77
|
+
| Standards audit | `make audit` | `scripts/ci/audit-make-targets.sh` | CI/Makefile compliance auditor. |
|
|
78
|
+
| Code coverage report | `make coverage` | `scripts/test/coverage.sh` | Print total %, uncovered statements, total lines. |
|
|
79
|
+
| PR coverage gate | `make ci-coverage-delta` | `scripts/test/coverage-delta.sh` | Fail PR if coverage drops > threshold vs. base branch. |
|
|
80
|
+
| PR policy gate | `make ci-pr-policy` | `scripts/ci/pr-policy.sh` | Title, body, branch naming, linked issue enforcement. |
|
|
81
|
+
| Secret scan | `make ci-secret-scan` | `scripts/ci/secret-scan.sh` | gitleaks scan; required on every PR and default-branch push. |
|
|
82
|
+
| DCO check | `make ci-dco` | `scripts/ci/dco-check.sh` | Signed-off-by trailer on every commit in the PR. |
|
|
83
|
+
| Trailing newlines | `make lint-newlines` | `scripts/lint/newlines.sh` | Every `.md`, `.sh`, `.go` ends with `\n`. |
|
|
84
|
+
| Legal drift check | `make check-legal-drift` | `scripts/check-legal-drift.sh` | Copied legal files (LICENSE, TRADEMARK) match the source of truth. |
|
|
74
85
|
|
|
75
86
|
### Required for projects with releases
|
|
76
87
|
|
|
77
|
-
| Target
|
|
78
|
-
|
|
79
|
-
| Generate checksums | `make release-checksums`
|
|
80
|
-
| Verify checksums
|
|
81
|
-
| Generate SBOM
|
|
82
|
-
| Sign artifacts
|
|
83
|
-
| Upload to release
|
|
84
|
-
| Version tagging
|
|
88
|
+
| Target | Makefile target | Script | Responsibility |
|
|
89
|
+
| ------------------ | ------------------------------- | --------------------------------------- | --------------------------------------------------------------- |
|
|
90
|
+
| Generate checksums | `make release-checksums` | `scripts/release/checksums.sh` | Write `dist/SHA256SUMS` for all release artifacts. |
|
|
91
|
+
| Verify checksums | `make release-checksums-verify` | `scripts/release/checksums.sh --verify` | Confirm downloaded artifacts match `SHA256SUMS`. |
|
|
92
|
+
| Generate SBOM | `make release-sbom` | `scripts/release/sbom.sh` | SPDX JSON SBOM via syft; attached to every release. |
|
|
93
|
+
| Sign artifacts | `make release-sign` | `scripts/release/sign.sh` | cosign keyless signing; bundles attached alongside binaries. |
|
|
94
|
+
| Upload to release | `make release-upload` | `scripts/release/upload.sh` | Push artifacts + checksums + SBOM to GitHub Release. |
|
|
95
|
+
| Version tagging | `make create-release` | `scripts/release/create-release.sh` | Semver bump from project hook + signed tag; developer action. |
|
|
85
96
|
|
|
86
97
|
### Required for projects with container images
|
|
87
98
|
|
|
88
|
-
| Target
|
|
89
|
-
|
|
99
|
+
| Target | Makefile target | Script | Responsibility |
|
|
100
|
+
| -------------------- | --------------------- | --------------------------- | --------------------------------------------------------- |
|
|
90
101
|
| Build and push image | `make release-docker` | `scripts/release/docker.sh` | Build, tag, push, and sign container images from `dist/`. |
|
|
91
102
|
|
|
92
103
|
### Required for projects with Helm charts
|
|
93
104
|
|
|
94
|
-
| Target
|
|
95
|
-
|
|
96
|
-
| Helm lint
|
|
97
|
-
| Helm schema validation | `make lint-helm-validate` | `scripts/lint/helm-validate.sh` | kubeconform validation of rendered templates.
|
|
98
|
-
| Helm chart publish
|
|
105
|
+
| Target | Makefile target | Script | Responsibility |
|
|
106
|
+
| ---------------------- | ------------------------- | ------------------------------- | ------------------------------------------------------ |
|
|
107
|
+
| Helm lint | `make lint-helm` | `scripts/lint/helm.sh` | `helm lint` + template render; no cluster required. |
|
|
108
|
+
| Helm schema validation | `make lint-helm-validate` | `scripts/lint/helm-validate.sh` | kubeconform validation of rendered templates. |
|
|
109
|
+
| Helm chart publish | `make release-helm-push` | `scripts/release/helm-push.sh` | OCI push to GHCR + cosign signing of the chart digest. |
|
|
99
110
|
|
|
100
111
|
## CI Auditor
|
|
101
112
|
|
|
@@ -106,15 +117,14 @@ replace the universal targets.
|
|
|
106
117
|
3. All universal canonical targets exist in the Makefile.
|
|
107
118
|
- `make audit` invokes the auditor.
|
|
108
119
|
- The auditor MUST be a required CI step (add to the workflow as `run: make audit`).
|
|
109
|
-
- The auditor MUST be wired into the pre-commit hook, gated on changes to
|
|
110
|
-
|
|
120
|
+
- The auditor MUST be wired into the pre-commit hook, gated on changes to `.github/workflows/`, `scripts/`, or
|
|
121
|
+
`Makefile`.
|
|
111
122
|
|
|
112
123
|
### Canonical Script Source of Truth
|
|
113
124
|
|
|
114
|
-
The source-of-truth copies of all canonical scripts live in the `keelcore/standards` repository
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
propagated to all downstream repos.
|
|
125
|
+
The source-of-truth copies of all canonical scripts live in the `keelcore/standards` repository (`scripts/` directory).
|
|
126
|
+
Downstream repos MUST keep their copies byte-identical to the standards versions. Any change to a canonical script must
|
|
127
|
+
be made first in `keelcore/standards` and then propagated to all downstream repos.
|
|
118
128
|
|
|
119
129
|
To verify that a downstream repo's canonical scripts match the standards source of truth, run:
|
|
120
130
|
|
|
@@ -122,60 +132,67 @@ To verify that a downstream repo's canonical scripts match the standards source
|
|
|
122
132
|
make verify-canonical REPO=<path-to-downstream-repo-root>
|
|
123
133
|
```
|
|
124
134
|
|
|
125
|
-
from the `keelcore/standards` repository root. This compares `sha256sum` of each mandatory script
|
|
126
|
-
|
|
135
|
+
from the `keelcore/standards` repository root. This compares `sha256sum` of each mandatory script and reports PASS/FAIL
|
|
136
|
+
per file, exiting 1 if any mismatch is found.
|
|
137
|
+
|
|
138
|
+
Project-local hooks (`scripts/**/*.local.sh`) are owned by the repo that holds them and are never canonical, including
|
|
139
|
+
the ones in `keelcore/standards` itself. `verify-canonical` and `governance-refresh` neither compare nor sync them, so a
|
|
140
|
+
consumer's hook is never overwritten. A canonical script that calls a hook resolves it from the git toplevel of the repo
|
|
141
|
+
it runs in: inside a consumer it calls the consumer's hook, never the copy in `.standards`.
|
|
127
142
|
|
|
128
143
|
## Canonical CI Scripts
|
|
129
144
|
|
|
130
|
-
Scripts whose names and responsibilities are fixed across all projects. Projects must not rename
|
|
131
|
-
|
|
145
|
+
Scripts whose names and responsibilities are fixed across all projects. Projects must not rename these scripts or move
|
|
146
|
+
their canonical logic elsewhere.
|
|
132
147
|
|
|
133
|
-
The source of truth for all canonical scripts is the `keelcore/standards` repository. Downstream
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
The source of truth for all canonical scripts is the `keelcore/standards` repository. Downstream repo copies must be
|
|
149
|
+
byte-identical to the standards versions for the scripts marked **canonical**. Use `make verify-canonical REPO=<path>`
|
|
150
|
+
from the standards repo to verify compliance.
|
|
136
151
|
|
|
137
152
|
### Always required
|
|
138
153
|
|
|
139
|
-
| Script
|
|
140
|
-
|
|
141
|
-
| `scripts/ci/audit-make-targets.sh` | `make audit`
|
|
142
|
-
| `scripts/ci/pr-policy.sh`
|
|
143
|
-
| `scripts/ci/secret-scan.sh`
|
|
144
|
-
| `scripts/ci/dco-check.sh`
|
|
145
|
-
| `scripts/
|
|
146
|
-
| `scripts/
|
|
147
|
-
| `scripts/
|
|
148
|
-
| `scripts/
|
|
149
|
-
| `scripts/
|
|
154
|
+
| Script | Makefile target | Responsibility | Source of truth |
|
|
155
|
+
| ---------------------------------- | ------------------------ | ------------------------------------------------------------------ | ------------------ |
|
|
156
|
+
| `scripts/ci/audit-make-targets.sh` | `make audit` | CI/Makefile standards compliance auditor. | keelcore/standards |
|
|
157
|
+
| `scripts/ci/pr-policy.sh` | `make ci-pr-policy` | PR policy gate: title, body, branch naming, linked issue. | project-specific |
|
|
158
|
+
| `scripts/ci/secret-scan.sh` | `make ci-secret-scan` | Secret scanning on every PR and default-branch push. | keelcore/standards |
|
|
159
|
+
| `scripts/ci/dco-check.sh` | `make ci-dco` | DCO Signed-off-by trailer verification. | keelcore/standards |
|
|
160
|
+
| `scripts/ci/setup-bats.sh` | `make setup-bats` | Install bats-core (test harness) idempotently across darwin/linux. | keelcore/standards |
|
|
161
|
+
| `scripts/install-hooks.sh` | `make install-hooks` | Symlink `scripts/git_precommit.sh` into `.git/hooks/pre-commit`. | keelcore/standards |
|
|
162
|
+
| `scripts/lint/newlines.sh` | `make lint-newlines` | Trailing newline enforcement for `.md`, `.sh`, `.go` files. | keelcore/standards |
|
|
163
|
+
| `scripts/test/coverage.sh` | `make coverage` | Coverage report: total %, uncovered statements, total lines. | keelcore/standards |
|
|
164
|
+
| `scripts/test/coverage-delta.sh` | `make ci-coverage-delta` | PR coverage delta gate (fails if drop exceeds threshold). | keelcore/standards |
|
|
165
|
+
| `scripts/check-legal-drift.sh` | `make check-legal-drift` | Verify copied legal files match the source of truth. | project-specific |
|
|
166
|
+
| `scripts/lib/paths.sh` | _(sourced; no target)_ | Shared path-filter helpers; sourced by other scripts. | keelcore/standards |
|
|
150
167
|
|
|
151
168
|
### Required for projects with releases
|
|
152
169
|
|
|
153
|
-
| Script
|
|
154
|
-
|
|
155
|
-
| `scripts/release/checksums.sh`
|
|
156
|
-
| `scripts/release/sbom.sh`
|
|
157
|
-
| `scripts/release/sign.sh`
|
|
158
|
-
| `scripts/release/upload.sh`
|
|
159
|
-
| `scripts/release/create-release.sh` | `make create-release`
|
|
170
|
+
| Script | Makefile target | Responsibility |
|
|
171
|
+
| ----------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------- |
|
|
172
|
+
| `scripts/release/checksums.sh` | `make release-checksums` / `make release-checksums-verify` | Generate or verify `dist/SHA256SUMS`. |
|
|
173
|
+
| `scripts/release/sbom.sh` | `make release-sbom` | Generate SPDX JSON SBOM via syft. |
|
|
174
|
+
| `scripts/release/sign.sh` | `make release-sign` | cosign keyless signing of all release artifacts. |
|
|
175
|
+
| `scripts/release/upload.sh` | `make release-upload` | Upload artifacts to GitHub Release via gh CLI. |
|
|
176
|
+
| `scripts/release/create-release.sh` | `make create-release` | Semver bump from project hook + signed tag; developer action. |
|
|
160
177
|
|
|
161
178
|
### Required for projects with container images
|
|
162
179
|
|
|
163
|
-
| Script
|
|
164
|
-
|
|
180
|
+
| Script | Makefile target | Responsibility |
|
|
181
|
+
| --------------------------- | --------------------- | -------------------------------------------- |
|
|
165
182
|
| `scripts/release/docker.sh` | `make release-docker` | Build, tag, push, and sign container images. |
|
|
166
183
|
|
|
167
184
|
### Required for projects with Helm charts
|
|
168
185
|
|
|
169
|
-
| Script
|
|
170
|
-
|
|
171
|
-
| `scripts/lint/helm.sh`
|
|
186
|
+
| Script | Makefile target | Responsibility |
|
|
187
|
+
| ------------------------------- | ------------------------- | --------------------------------------------------------- |
|
|
188
|
+
| `scripts/lint/helm.sh` | `make lint-helm` | `helm lint` + template render; no cluster required. |
|
|
172
189
|
| `scripts/lint/helm-validate.sh` | `make lint-helm-validate` | kubeconform schema validation of rendered Helm templates. |
|
|
173
|
-
| `scripts/release/helm-push.sh`
|
|
190
|
+
| `scripts/release/helm-push.sh` | `make release-helm-push` | OCI Helm chart push to GHCR + cosign signing. |
|
|
174
191
|
|
|
175
192
|
## Source File Formatting Invariants
|
|
176
193
|
|
|
177
|
-
Every text source file MUST end with a single trailing newline (`\n`). Files without one fail the
|
|
178
|
-
|
|
194
|
+
Every text source file MUST end with a single trailing newline (`\n`). Files without one fail the pre-commit hook and CI
|
|
195
|
+
lint. Covered extensions:
|
|
179
196
|
|
|
180
197
|
- Source: `.go`, `.sh`
|
|
181
198
|
- Markup / documentation: `.md`
|
|
@@ -186,24 +203,24 @@ Rules:
|
|
|
186
203
|
|
|
187
204
|
- Enforce via `scripts/lint/newlines.sh`; auto-fix via `scripts/lint/newlines.sh --fix`.
|
|
188
205
|
- The pre-commit hook checks staged files; `make lint-newlines` checks the full tracked tree.
|
|
189
|
-
- Adding a new text-format type to the repo? Add its glob to `scripts/lint/newlines.sh` and the
|
|
190
|
-
|
|
206
|
+
- Adding a new text-format type to the repo? Add its glob to `scripts/lint/newlines.sh` and the pre-commit
|
|
207
|
+
`staged_text_files` filter in the same commit.
|
|
191
208
|
|
|
192
209
|
## Build Platform vs. Target Platform
|
|
193
210
|
|
|
194
211
|
- Prefer build-once, deploy/validate-everywhere.
|
|
195
212
|
- Use cross-compilation to produce artifacts from a single runner.
|
|
196
213
|
- Use runner matrices for testing/smoke validation on target platforms, not for rebuilding.
|
|
197
|
-
- Per-platform builds only when required by native signing, platform-bound linkers, OS-specific packaging,
|
|
198
|
-
|
|
214
|
+
- Per-platform builds only when required by native signing, platform-bound linkers, OS-specific packaging, or
|
|
215
|
+
target-specific verification unavailable elsewhere. State the reason explicitly when doing so.
|
|
199
216
|
- Artifact promotion over artifact regeneration: the CI-tested artifact is the release artifact.
|
|
200
217
|
|
|
201
218
|
## Platform-Specific Finishing Steps
|
|
202
219
|
|
|
203
220
|
- Signing, notarization, and native packaging are acceptable reasons for platform matrices.
|
|
204
221
|
- Build the artifact in a canonical build job; use a platform matrix only for the platform-bound finishing step.
|
|
205
|
-
- Keep signing, notarization, and packaging steps in repository scripts; workflow YAML provides only
|
|
206
|
-
|
|
222
|
+
- Keep signing, notarization, and packaging steps in repository scripts; workflow YAML provides only orchestration,
|
|
223
|
+
credentials, and artifact movement.
|
|
207
224
|
|
|
208
225
|
## Permissions
|
|
209
226
|
|
|
@@ -216,8 +233,8 @@ Rules:
|
|
|
216
233
|
## Action Version Pinning
|
|
217
234
|
|
|
218
235
|
- Always pin action versions to a full commit SHA, not a tag. Tags are mutable.
|
|
219
|
-
- Correct:
|
|
220
|
-
- Incorrect:
|
|
236
|
+
- Correct: `actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2`
|
|
237
|
+
- Incorrect: `actions/checkout@v4`
|
|
221
238
|
- Use Dependabot (`dependabot.yml`) with `package-ecosystem: github-actions` to keep SHA pins current.
|
|
222
239
|
|
|
223
240
|
## Toolchain Version
|
|
@@ -361,10 +378,10 @@ Rules:
|
|
|
361
378
|
|
|
362
379
|
### CI Workflow
|
|
363
380
|
|
|
364
|
-
- PR job: compute current coverage → download `coverage/baseline.json` from the base branch →
|
|
365
|
-
|
|
366
|
-
- Post-merge job (runs on default branch only): recompute coverage → commit updated
|
|
367
|
-
|
|
381
|
+
- PR job: compute current coverage → download `coverage/baseline.json` from the base branch → compare → fail if current
|
|
382
|
+
< baseline.
|
|
383
|
+
- Post-merge job (runs on default branch only): recompute coverage → commit updated `coverage/baseline.json` → push
|
|
384
|
+
directly (no PR required for baseline updates).
|
|
368
385
|
- The post-merge baseline update commit must be signed and attributed to the CI bot identity.
|
|
369
386
|
- Baseline update commits use a conventional commit message: `chore: update coverage baseline to XX.X%`.
|
|
370
387
|
|
|
@@ -406,75 +423,79 @@ Rules:
|
|
|
406
423
|
|
|
407
424
|
### Versioning Specification
|
|
408
425
|
|
|
409
|
-
All version numbers follow **Semantic Versioning 2.0.0** — [https://semver.org](https://semver.org).
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
extensions at this time.
|
|
426
|
+
All version numbers follow **Semantic Versioning 2.0.0** — [https://semver.org](https://semver.org). That document is
|
|
427
|
+
canonical and must not be contradicted by project-specific rules. Version tags take the form `vMAJOR.MINOR.PATCH` with
|
|
428
|
+
no pre-release or build-metadata extensions at this time.
|
|
413
429
|
|
|
414
430
|
Specific semver.org rules in force:
|
|
415
431
|
|
|
416
|
-
- **[Item 2](https://semver.org/#spec-item-2):** Version numbers take the form
|
|
417
|
-
|
|
418
|
-
- **[Item 4](https://semver.org/#spec-item-4):** MAJOR is incremented for incompatible
|
|
419
|
-
|
|
420
|
-
- **[Item 5](https://semver.org/#spec-item-5):** MINOR is incremented for new
|
|
421
|
-
|
|
422
|
-
- **[Item 6](https://semver.org/#spec-item-6):** PATCH is incremented for
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
public API. Subsequent increments follow the rules above without exception.
|
|
432
|
+
- **[Item 2](https://semver.org/#spec-item-2):** Version numbers take the form `MAJOR.MINOR.PATCH` — non-negative
|
|
433
|
+
integers, no leading zeroes.
|
|
434
|
+
- **[Item 4](https://semver.org/#spec-item-4):** MAJOR is incremented for incompatible API changes; MINOR and PATCH
|
|
435
|
+
reset to 0.
|
|
436
|
+
- **[Item 5](https://semver.org/#spec-item-5):** MINOR is incremented for new backward-compatible functionality; PATCH
|
|
437
|
+
resets to 0.
|
|
438
|
+
- **[Item 6](https://semver.org/#spec-item-6):** PATCH is incremented for backward-compatible bug fixes only.
|
|
439
|
+
- **[Item 9](https://semver.org/#spec-item-9):** Major version zero (`0.y.z`) is for initial development. The public API
|
|
440
|
+
is not yet stable and anything may change.
|
|
441
|
+
- **[Item 10](https://semver.org/#spec-item-10):** `1.0.0` defines the first stable public API. Subsequent increments
|
|
442
|
+
follow the rules above without exception.
|
|
428
443
|
|
|
429
444
|
### Tagging is a Developer Action — Never CI
|
|
430
445
|
|
|
431
|
-
- Release version tags (`vX.Y.Z`) are created exclusively by developers running
|
|
432
|
-
|
|
446
|
+
- Release version tags (`vX.Y.Z`) are created exclusively by developers running `scripts/release/create-release.sh` from
|
|
447
|
+
a local checkout.
|
|
433
448
|
- CI must never create, push, or move version tags under any circumstance.
|
|
434
|
-
- This is a hard policy: no workflow file may contain `git tag`, `git push --tags`,
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
commit represents. That decision requires human judgment and cannot be delegated
|
|
438
|
-
to automation.
|
|
449
|
+
- This is a hard policy: no workflow file may contain `git tag`, `git push --tags`, or equivalent tag-creation steps.
|
|
450
|
+
- Rationale: a tag is a public commitment about what version of the software a commit represents. That decision requires
|
|
451
|
+
human judgment and cannot be delegated to automation.
|
|
439
452
|
|
|
440
453
|
### Automatic Tag Triggering Is Permitted
|
|
441
454
|
|
|
442
|
-
- CI workflows _triggered by_ a pushed tag (e.g. `on: push: tags: ['v*']`) are
|
|
443
|
-
|
|
444
|
-
pushed a tag.
|
|
455
|
+
- CI workflows _triggered by_ a pushed tag (e.g. `on: push: tags: ['v*']`) are permitted and expected — they run the
|
|
456
|
+
release pipeline once a developer has pushed a tag.
|
|
445
457
|
- The distinction is: CI reacts to tags; CI does not create tags.
|
|
446
458
|
|
|
447
459
|
### Version Computation
|
|
448
460
|
|
|
449
|
-
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
-
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
- Git tags are the only source of truth for versions. Projects keep no version file and make no version-bump commit;
|
|
462
|
+
every publisher derives the version from the tag at publish time.
|
|
463
|
+
- `scripts/release/create-release.sh` is canonical (source of truth: `keelcore/standards`). It checks preconditions (on
|
|
464
|
+
`main`, upstream set, `HEAD` equals the upstream after a fetch, clean tree), finds the previous tag, computes the next
|
|
465
|
+
version, presents the findings for approval, then creates a signed annotated tag and pushes that one tag.
|
|
466
|
+
- The bump level comes from the project's own hook, `scripts/release/create-release.local.sh`, resolved from the git
|
|
467
|
+
toplevel of the repo being released. The driver calls it as `create-release.local.sh <previous-tag>`; the hook prints
|
|
468
|
+
two lines to stdout: the bump level (`major`, `minor` or `patch`) and a one-line tag subject. Its stderr is shown to
|
|
469
|
+
the developer and recorded in the tag body; a non-zero exit aborts the release. With no hook, every release is a
|
|
470
|
+
`patch`.
|
|
471
|
+
- Each hook maps its project's public surface to semver:
|
|
472
|
+
- Breaking change → major bump ([semver.org item 4](https://semver.org/#spec-item-4)).
|
|
473
|
+
- New backward-compatible functionality → minor bump ([semver.org item 5](https://semver.org/#spec-item-5)).
|
|
474
|
+
- Anything else → patch bump ([semver.org item 6](https://semver.org/#spec-item-6)).
|
|
475
|
+
- While the major version is 0 ([semver.org item 9](https://semver.org/#spec-item-9)) the driver demotes the bump one
|
|
476
|
+
step: major → minor, minor → patch. A patch stays a patch when there are commits since the previous tag; with none,
|
|
477
|
+
there is nothing to release.
|
|
478
|
+
- `make create-release DRY_RUN=1` (`--dry-run`) computes and prints the version and tag message without prompting,
|
|
479
|
+
tagging or pushing; failed preconditions are reported as warnings.
|
|
480
|
+
- Worked example (keel): the hook diffs the field set of `pkg/config/schema.yaml` at the previous tag against HEAD.
|
|
481
|
+
Removed fields → major; added fields (no removals) → minor; no field surface change → patch.
|
|
482
|
+
`scripts/release/gen-schema.sh` regenerates the schema from `cmd/config-schema/main.go` after any change to the config
|
|
483
|
+
struct, and the consistency suite enforces that `schema.yaml` is never stale.
|
|
484
|
+
- `keelcore/standards`: the hook reads the `## [Unreleased]` section of `CHANGELOG.md`. Any `### Removed` entry → major;
|
|
485
|
+
else any `### Added` or `### Changed` entry → minor; else patch. An empty `[Unreleased]` fails: nothing to release.
|
|
463
486
|
|
|
464
487
|
### --force Override Rules
|
|
465
488
|
|
|
466
|
-
Developers may override the computed version with `--force vX.Y.Z`. Only bare
|
|
467
|
-
|
|
468
|
-
and build metadata (`+sha`) are not supported by the tooling at this time.
|
|
489
|
+
Developers may override the computed version with `--force vX.Y.Z`. Only bare `MAJOR.MINOR.PATCH` versions are accepted;
|
|
490
|
+
pre-release identifiers (`-alpha.1`) and build metadata (`+sha`) are not supported by the tooling at this time.
|
|
469
491
|
|
|
470
|
-
- **Pre-1.0 (major = 0):** Only versions in the `0.x.y` range or exactly `v1.0.0`
|
|
471
|
-
|
|
492
|
+
- **Pre-1.0 (major = 0):** Only versions in the `0.x.y` range or exactly `v1.0.0` are accepted. Forcing `v1.1.0` or
|
|
493
|
+
`v2.0.0` from a `0.x` base is rejected.
|
|
472
494
|
- **Post-1.0 (major ≥ 1):** `--force` is restricted to exact single-step increments:
|
|
473
495
|
- Patch: `cur_maj.cur_min.(cur_pat+1)` — always allowed.
|
|
474
496
|
- Minor: `cur_maj.(cur_min+1).0` — always allowed (e.g. internal improvements).
|
|
475
|
-
- Major: only accepted when breaking
|
|
476
|
-
|
|
477
|
-
or redirect a breaking-change bump, and cannot skip versions.
|
|
497
|
+
- Major: only accepted when the hook reports a breaking change (major) **and** the forced version exactly matches the
|
|
498
|
+
auto-computed version. `--force` cannot suppress or redirect a breaking-change bump, and cannot skip versions.
|
|
478
499
|
|
|
479
500
|
## Do Not
|
|
480
501
|
|
|
@@ -493,4 +514,4 @@ and build metadata (`+sha`) are not supported by the tooling at this time.
|
|
|
493
514
|
- Allow release jobs to run without environment protection and required human reviewers.
|
|
494
515
|
- Ship release artifacts without provenance attestation, signatures, or checksums.
|
|
495
516
|
- Create, push, or move version tags from CI. Tags are a developer action only.
|
|
496
|
-
- Bypass the `create-release.sh` script to tag manually without
|
|
517
|
+
- Bypass the `create-release.sh` script to tag manually without its approval step.
|