@ebalzan/ci 0.0.0-stage → 3.0.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/LICENSE +21 -0
- package/README.md +302 -2
- package/bin/_json.mjs +95 -0
- package/bin/_lib.sh +153 -0
- package/bin/cli.mjs +105 -0
- package/bin/gh-bootstrap.sh +622 -0
- package/bin/gh-reset.sh +148 -0
- package/bin/onboard.sh +340 -0
- package/bin/sync-managed.sh +81 -0
- package/config/actions.env +33 -0
- package/config/repo-settings.json +10 -0
- package/config/repos.env +25 -0
- package/config/rulesets/protect-dev.json +10 -0
- package/config/rulesets/protect-main.json +10 -0
- package/config/rulesets/require-pull-request.json +33 -0
- package/config/secrets.env.example +31 -0
- package/docs/ARCHITECTURE.md +477 -0
- package/docs/AUTO_WORK_ITEMS_WORKFLOW.md +305 -0
- package/docs/CI_FLOW.md +205 -0
- package/docs/CLI.md +229 -0
- package/docs/COMMIT_GUIDE.md +133 -0
- package/docs/CONFIGURATION.md +177 -0
- package/docs/GITHUB_ACTIONS_SETUP.md +315 -0
- package/docs/MIGRATION.md +175 -0
- package/docs/ONBOARDING.md +166 -0
- package/docs/REPO_RULES.md +116 -0
- package/managed/scripts/release/validate-conventional-subject.sh +32 -0
- package/package.json +66 -4
- package/renovate/tooling.json +45 -0
- package/templates/.github/actions.env +17 -0
- package/templates/.github/workflows/auto-pr.yml +33 -0
- package/templates/.github/workflows/pr-checks.yml +48 -0
- package/templates/.github/workflows/promote-dev-to-main.yml +26 -0
- package/templates/.github/workflows/release.yml +36 -0
- package/templates/.husky/commit-msg +18 -0
- package/templates/eslint.config.mjs +6 -0
- package/templates/renovate.json +4 -0
package/docs/CLI.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# CLI reference
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
npx @ebalzan/ci <command> [options]
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Install it as a devDependency (`npm install --save-dev @ebalzan/ci`) and the
|
|
8
|
+
binary is `ebalzan-ci` in npm scripts and husky hooks. Without installing,
|
|
9
|
+
prefix with `npx @ebalzan/ci`. From a git checkout of this repo, `node
|
|
10
|
+
bin/cli.mjs <command>` and the scripts in `bin/` work the same way.
|
|
11
|
+
|
|
12
|
+
The commands that change GitHub (`onboard`, `bootstrap`, `reset`) are bash
|
|
13
|
+
scripts and need [`gh`](https://cli.github.com) logged in as an **admin** of
|
|
14
|
+
the target repo. Each one prints a plan and asks before changing anything, and
|
|
15
|
+
each has `--dry-run`.
|
|
16
|
+
|
|
17
|
+
| Command | Summary |
|
|
18
|
+
| ------------------------------- | --------------------------------------------------------- |
|
|
19
|
+
| [`onboard`](#onboard) | Bring a repo under the shared rules in one step |
|
|
20
|
+
| [`bootstrap`](#bootstrap) | Sync variables, secrets, settings and rulesets onto repos |
|
|
21
|
+
| [`reset`](#reset) | Delete Actions variables/secrets on a repo |
|
|
22
|
+
| [`commit-msg`](#commit-msg) | Validate a commit message (husky hook) |
|
|
23
|
+
| [`path`](#path) | Print the package directory |
|
|
24
|
+
| [`sync-managed`](#sync-managed) | Deprecated: refresh local copies of managed files |
|
|
25
|
+
| `version`, `help` | Print the version / the command list |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## onboard
|
|
30
|
+
|
|
31
|
+
Adds everything a repo needs, on a branch, and opens it for review.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ebalzan-ci onboard [-C DIR] [options]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Option | Default | Effect |
|
|
38
|
+
| ----------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
39
|
+
| `-C, --repo-dir DIR` | `.` | Checkout of the repo to onboard |
|
|
40
|
+
| `-R, --repo OWNER/REPO` | from the git remote | Its slug |
|
|
41
|
+
| `--runs-on LABEL` | `ubuntu-latest` | Runner label written into every caller (e.g. `self-hosted`) |
|
|
42
|
+
| `--ref REF` | `v<major of the package>` | Ref the callers pin. Keep the major tag (`v2`); an exact version only freezes the workflow files ([why](ARCHITECTURE.md#9-versions-what-a-pin-freezes)) |
|
|
43
|
+
| `--branch NAME` | `ci/onboard-ebalzan-ci` | Branch the files are committed on |
|
|
44
|
+
| `--force` | off | Replace files that already exist (default: keep them) |
|
|
45
|
+
| `--no-npm` | off | Do not install packages, even with a `package.json` (files and `package.json` fields are still written) |
|
|
46
|
+
| `--no-config` | off | Do not wire `@ebalzan/config` (Prettier, ESLint) |
|
|
47
|
+
| `--no-push` | off | Stop after the local commit: GitHub is not touched at all |
|
|
48
|
+
| `-n, --dry-run` | off | Print the plan; change nothing |
|
|
49
|
+
| `-y, --yes` | off | No confirmation prompts (also for the bootstrap it runs) |
|
|
50
|
+
|
|
51
|
+
What it does, in order:
|
|
52
|
+
|
|
53
|
+
1. Checks the tree is clean and the branch does not exist yet.
|
|
54
|
+
2. Branches from `origin/dev`, or from the default branch when `dev` does not
|
|
55
|
+
exist yet.
|
|
56
|
+
3. Writes the [templates](../templates/) with `__CI_REPO__`, `__CI_REF__` and
|
|
57
|
+
`__RUNS_ON__` filled in. Existing files are kept unless `--force`. The husky
|
|
58
|
+
hook and `eslint.config.mjs` are only written for a repo with `package.json`
|
|
59
|
+
(and `eslint.config.mjs` only when it has no ESLint config of its own);
|
|
60
|
+
`renovate.json` is skipped when the repo has its own Renovate config.
|
|
61
|
+
4. Appends the `.github/secrets*.env` ignore lines to `.gitignore`, unless those
|
|
62
|
+
files are already ignored.
|
|
63
|
+
5. Node repos, code style (unless `--no-config`): sets `package.json`
|
|
64
|
+
`"prettier": "@ebalzan/config/prettier"` when the repo has no Prettier config
|
|
65
|
+
of its own. A `tsconfig.json` that does not extend
|
|
66
|
+
`@ebalzan/config/tsconfig/*` is reported, never edited.
|
|
67
|
+
6. Node repos, installs (unless `--no-npm`): `@ebalzan/ci@^<major>` and husky (if
|
|
68
|
+
missing), `scripts.prepare = "husky"` when there is no prepare script, and
|
|
69
|
+
`@ebalzan/config` with its peers `eslint` and `prettier` (if missing), at
|
|
70
|
+
exact versions. A failure here only warns.
|
|
71
|
+
7. Commits as `ci: onboard ebalzan/ci shared workflows and rules`.
|
|
72
|
+
8. Unless `--no-push`, after one confirmation: creates `dev` on GitHub from the
|
|
73
|
+
default branch if missing, adds the `REPOS_TOPIC` topic, runs
|
|
74
|
+
[`bootstrap`](#bootstrap), pushes the branch. The `auto-pr.yml` just added
|
|
75
|
+
runs on that push and opens the issue and the PR into `dev`.
|
|
76
|
+
|
|
77
|
+
Walkthrough: [ONBOARDING.md](ONBOARDING.md).
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## bootstrap
|
|
82
|
+
|
|
83
|
+
Applies the merged shared + repo config to GitHub. Idempotent: run it as often
|
|
84
|
+
as you like.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
ebalzan-ci bootstrap # the repo in the current directory
|
|
88
|
+
ebalzan-ci bootstrap -C ../my-repo # a local checkout
|
|
89
|
+
ebalzan-ci bootstrap -R owner/my-repo # no checkout: remote mode
|
|
90
|
+
ebalzan-ci bootstrap --all --dry-run # every managed repo, plan only
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
| Option | Effect |
|
|
94
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `-C, --repo-dir DIR` | Checkout whose `.github/` holds the repo layer (default: `.`) |
|
|
96
|
+
| `-R, --repo OWNER/REPO` | Target repo. Alone: remote mode, unless the current directory is a checkout of it. With `-C`: push DIR's config to that slug |
|
|
97
|
+
| `-a, --all` | Every repo `config/repos.env` selects (topic + list), each from its local checkout when there is one |
|
|
98
|
+
| `--skip LIST` | Comma-separated sections to leave alone: `vars`, `secrets`, `settings`, `rulesets` |
|
|
99
|
+
| `--prune` | Also delete what no file defines: variables, secrets, and rulesets named `ci: …` (asks first) |
|
|
100
|
+
| `-n, --dry-run` | Print the merged plan and, when gh is logged in, the settings diff and ruleset actions; change nothing |
|
|
101
|
+
| `-y, --yes` | No confirmation prompts |
|
|
102
|
+
|
|
103
|
+
Sections, in the order they are applied:
|
|
104
|
+
|
|
105
|
+
| Section | From | To GitHub |
|
|
106
|
+
| ---------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
107
|
+
| `vars` | the repo's `.github/actions.env` only (shared ones are read at run time) | repository variables |
|
|
108
|
+
| `secrets` | shared `secrets.env` + the repo's `secrets.env`, and `secrets.<env>.env` (from the repo secrets dir, below) | repository secrets; environment `<env>` (created) secrets |
|
|
109
|
+
| `settings` | `config/repo-settings.json` deep-merged with `.github/repo-settings.json` | `PATCH /repos/{repo}` (only changed keys are listed) |
|
|
110
|
+
| `rulesets` | each `config/rulesets/X.json` deep-merged with `.github/rulesets/X.json` | created, or updated by `name` |
|
|
111
|
+
|
|
112
|
+
**Where the shared secrets come from:** `$CI_SECRETS_DIR` if set; otherwise
|
|
113
|
+
`config/` when running from a git checkout of this repo; otherwise
|
|
114
|
+
`~/.config/ebalzan-ci/` (or `$XDG_CONFIG_HOME/ebalzan-ci/`). A missing file is
|
|
115
|
+
simply no shared secrets.
|
|
116
|
+
|
|
117
|
+
**Where a repo's own secrets come from:** `repos/<owner>/<repo>/` inside the
|
|
118
|
+
shared secrets dir when that folder exists (matched case-insensitively; the
|
|
119
|
+
checkout's `secrets*.env` are then ignored, with a warning); otherwise the
|
|
120
|
+
checkout's gitignored `.github/`. Keeping them in the shared secrets dir is
|
|
121
|
+
recommended: one folder for every repo, usable without checkouts
|
|
122
|
+
([ARCHITECTURE §7](ARCHITECTURE.md#7-where-you-run-commands)).
|
|
123
|
+
|
|
124
|
+
**Remote mode** (`-R` without a checkout) reads the repo's committed
|
|
125
|
+
`.github/actions.env`, `bootstrap.conf`, `repo-settings.json` and `rulesets/`
|
|
126
|
+
from its default branch, and its secrets from `repos/<owner>/<repo>/`. Without
|
|
127
|
+
that folder its secrets are unknown: only shared secrets are synced, and
|
|
128
|
+
secrets are never pruned.
|
|
129
|
+
|
|
130
|
+
**Guards:** empty and placeholder secret values (`xxxx`, `sk-ant-xxx`,
|
|
131
|
+
`github_pat_xxx`) are refused before anything is sent; a `_FILE` key sets the
|
|
132
|
+
secret from a file's contents; `LOCAL_ONLY_KEYS` in `.github/bootstrap.conf`
|
|
133
|
+
are never pushed. Details: [GITHUB_ACTIONS_SETUP.md §4](GITHUB_ACTIONS_SETUP.md#4-file-mechanics).
|
|
134
|
+
|
|
135
|
+
**Rulesets on a private repo** need GitHub Pro/Team. On the free plan GitHub
|
|
136
|
+
answers "Upgrade to GitHub Pro or make this repository public", and the step
|
|
137
|
+
warns and moves on.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## reset
|
|
142
|
+
|
|
143
|
+
Deletes Actions variables (default) and/or secrets on one repo, after showing
|
|
144
|
+
them and asking you to type the repo name. Prefer `bootstrap --prune` to remove
|
|
145
|
+
only what no file defines any more.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
ebalzan-ci reset --dry-run # what would go
|
|
149
|
+
ebalzan-ci reset -R owner/repo --include-secrets
|
|
150
|
+
ebalzan-ci reset --match '^AUTO_'
|
|
151
|
+
ebalzan-ci reset --env production --secrets-only --dry-run
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Option | Effect |
|
|
155
|
+
| ----------------------- | --------------------------------------------------- |
|
|
156
|
+
| `-C, --repo-dir DIR` | Checkout whose remote picks the repo (default: `.`) |
|
|
157
|
+
| `-R, --repo OWNER/REPO` | Target repo |
|
|
158
|
+
| `-e, --env NAME` | Act on environment `NAME` instead of the repository |
|
|
159
|
+
| `--include-secrets` | Also delete secrets |
|
|
160
|
+
| `--secrets-only` | Delete secrets only |
|
|
161
|
+
| `-m, --match REGEX` | Only names matching this extended regex |
|
|
162
|
+
| `-n, --dry-run` | List; delete nothing |
|
|
163
|
+
| `-y, --yes` | Skip the typed confirmation (dangerous) |
|
|
164
|
+
|
|
165
|
+
It never touches settings or rulesets.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## commit-msg
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
ebalzan-ci commit-msg <message-file | subject>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Exits 0 when the first non-comment line is a Conventional Commit subject with
|
|
176
|
+
one of the [allowed types](COMMIT_GUIDE.md#allowed-types), 1 (with an example)
|
|
177
|
+
otherwise. Subjects git writes itself (`Merge …`, `Revert "…"`, `fixup! …`,
|
|
178
|
+
`squash! …`, `amend! …`) pass. The rule is
|
|
179
|
+
`managed/scripts/release/validate-conventional-subject.sh` — the same file the
|
|
180
|
+
PR-title check runs in CI.
|
|
181
|
+
|
|
182
|
+
The hook `onboard` writes (`.husky/commit-msg`):
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
if [ -x node_modules/.bin/ebalzan-ci ]; then
|
|
186
|
+
exec node_modules/.bin/ebalzan-ci commit-msg "$1"
|
|
187
|
+
fi
|
|
188
|
+
# …falls back to a checkout of ebalzan/ci at ${CI_DIR:-../ci}, else warns and
|
|
189
|
+
# lets the commit through (CI still checks the PR title).
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## path
|
|
195
|
+
|
|
196
|
+
Prints the installed package directory, for scripts that want the shared shell
|
|
197
|
+
helpers or config:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
source "$(npx --no-install ebalzan-ci path)/bin/_lib.sh" # info/ok/warn/die, env_get, detect_repo, …
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## sync-managed
|
|
206
|
+
|
|
207
|
+
**Deprecated.** Before 2.0, repos could keep a verbatim copy of the validator
|
|
208
|
+
at `scripts/release/validate-conventional-subject.sh`; the PR-title check fails
|
|
209
|
+
when such a copy drifts. This refreshes those copies (`--install` creates them,
|
|
210
|
+
`--check` only reports). Prefer deleting the copy and using the hook above.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Environment variables
|
|
215
|
+
|
|
216
|
+
| Variable | Used by | Meaning |
|
|
217
|
+
| --------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
218
|
+
| `CI_SECRETS_DIR` | `bootstrap` | Directory with the shared `secrets.env` / `secrets.<env>.env`, and `repos/<owner>/<repo>/` for each repo's own |
|
|
219
|
+
| `CI_REPOS_ROOT` | `bootstrap --all`, `sync-managed --all` | Where local checkouts live: `<root>/<repo name>` (default: next to this package's checkout) |
|
|
220
|
+
| `CI_DIR` | the husky hook | A checkout of this repo, used when the package is not installed (default `../ci`) |
|
|
221
|
+
| `XDG_CONFIG_HOME` | `bootstrap` | Base of the default shared secrets dir |
|
|
222
|
+
| `NO_COLOR` / non-TTY output | all | Plain output without colours |
|
|
223
|
+
|
|
224
|
+
The pre-2.0 name `TRACEBIM_REPOS_ROOT` is still honoured.
|
|
225
|
+
|
|
226
|
+
## Exit codes
|
|
227
|
+
|
|
228
|
+
`0` success (including a completed dry run) · `1` a check failed, invalid
|
|
229
|
+
input, or the user aborted · `127` bash/sh not found.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Commit Guide
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This guide defines the commit conventions every repo using ebalzan/ci follows and how
|
|
6
|
+
commits fit into the branch workflow. The full pipeline, stage by stage, is in
|
|
7
|
+
[CI_FLOW.md](CI_FLOW.md).
|
|
8
|
+
|
|
9
|
+
## Workflow Context
|
|
10
|
+
|
|
11
|
+
- The model is work branch → `dev` → `main`. Both `dev` and `main` are long-lived.
|
|
12
|
+
- Work happens on short-lived branches (`feat/*`, `fix/*`, `docs/*`, …) cut from `dev`.
|
|
13
|
+
- Work PRs target `dev` and are squash-merged.
|
|
14
|
+
- `main` only receives the automated `dev` → `main` promotion PR.
|
|
15
|
+
- Every push to `main` creates a release (and the repo ships it), then
|
|
16
|
+
force-points `dev` at the release commit.
|
|
17
|
+
|
|
18
|
+
## Commit Message Format
|
|
19
|
+
|
|
20
|
+
Use Conventional Commits:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
<type>(<scope>): <description>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
feat(auth): add oauth login
|
|
30
|
+
fix(uploads): reject invalid status changes
|
|
31
|
+
docs(readme): update setup section
|
|
32
|
+
ci(actions): add pr checks workflow
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The rule is `managed/scripts/release/validate-conventional-subject.sh` in
|
|
36
|
+
ebalzan/ci. Each Node repo's husky `commit-msg` hook runs it through the npm
|
|
37
|
+
package (`ebalzan-ci commit-msg`), and the shared PR-title check runs it on
|
|
38
|
+
every PR in CI — so repos without the hook are checked too, at PR time.
|
|
39
|
+
|
|
40
|
+
## Allowed Types
|
|
41
|
+
|
|
42
|
+
- `feat`: new feature
|
|
43
|
+
- `fix`: bug fix
|
|
44
|
+
- `docs`: documentation changes
|
|
45
|
+
- `style`: formatting/style-only changes
|
|
46
|
+
- `refact`: code restructuring without behavior change (there is no `refactor`)
|
|
47
|
+
- `perf`: performance improvement
|
|
48
|
+
- `test`: tests added/updated
|
|
49
|
+
- `build`: build/tooling changes
|
|
50
|
+
- `ci`: CI/CD changes
|
|
51
|
+
- `chore`: maintenance tasks
|
|
52
|
+
- `revert`: revert previous commit
|
|
53
|
+
|
|
54
|
+
Branch prefixes are the same types (`refact/…`); there is no `feature/` or
|
|
55
|
+
`hotfix/`.
|
|
56
|
+
|
|
57
|
+
## Practical Rules
|
|
58
|
+
|
|
59
|
+
1. Keep `type` and `scope` lowercase.
|
|
60
|
+
2. Use imperative description (`add`, `fix`, `update`).
|
|
61
|
+
3. Do not end description with a period.
|
|
62
|
+
4. Prefer specific scope names: the module or area touched (`auth`,
|
|
63
|
+
`uploads`, `api`, `db`, `actions`, `deps`).
|
|
64
|
+
5. Keep the header at 72 characters or fewer.
|
|
65
|
+
|
|
66
|
+
## Typical Developer Flow
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# 1) Branch from dev
|
|
70
|
+
git switch dev
|
|
71
|
+
git pull origin dev
|
|
72
|
+
git switch -c feat/your-change
|
|
73
|
+
|
|
74
|
+
# 2) Commit with conventional format
|
|
75
|
+
git add .
|
|
76
|
+
git commit -m "feat(search): add result filters"
|
|
77
|
+
|
|
78
|
+
# 3) Push: the work-items workflow opens a PR into dev with a tracking issue
|
|
79
|
+
git push -u origin feat/your-change
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Release Notes and Versioning
|
|
83
|
+
|
|
84
|
+
Release notes and semantic versioning are automatic.
|
|
85
|
+
|
|
86
|
+
- A push to `main` runs the repo's `release.yml`, which calls this repo's
|
|
87
|
+
reusable `release.yml`.
|
|
88
|
+
- The bump is computed by scanning every Conventional Commit since the previous
|
|
89
|
+
tag. Branch names don't affect it, and if no commit is conventional the release
|
|
90
|
+
fails.
|
|
91
|
+
- With a `package.json`, it updates it (and the lockfile) to the release
|
|
92
|
+
version and commits `chore(release): <version>`. The workflow skips commits
|
|
93
|
+
with that prefix, so the release commit doesn't trigger another release.
|
|
94
|
+
Without one, the release is the tag alone.
|
|
95
|
+
- It creates tag `vX.Y.Z` and a GitHub Release; the repo then ships that tag.
|
|
96
|
+
- It force-points `dev` at the release commit. If a later `git pull` on `dev`
|
|
97
|
+
conflicts, run `git reset --hard origin/dev` and branch from there.
|
|
98
|
+
- `PR Checks` validates PR titles so squash merges always produce a conventional commit.
|
|
99
|
+
|
|
100
|
+
Never bump the version, create a tag, or write a `chore(release):` commit by hand.
|
|
101
|
+
|
|
102
|
+
Version impact by commit convention:
|
|
103
|
+
|
|
104
|
+
- `feat` -> `minor`
|
|
105
|
+
- `fix`, `perf`, `docs`, `style`, `refact`, `test`, `build`, `ci`, `chore`, `revert` -> `patch`
|
|
106
|
+
- `BREAKING CHANGE` (or `!`) -> `major`
|
|
107
|
+
|
|
108
|
+
## PR Titles and Squash Merges
|
|
109
|
+
|
|
110
|
+
GitHub squash merge uses the PR title as the commit title.
|
|
111
|
+
|
|
112
|
+
- keep PR titles in Conventional Commit format
|
|
113
|
+
- make the PR title match the release impact implied by the branch type (`feat` -> `minor`; every other supported type -> `patch`)
|
|
114
|
+
- if the change is breaking, mark the PR title with `!` to force a `major` release, for example `feat(api)!: remove legacy endpoint`
|
|
115
|
+
- the `dev` → `main` promotion PR is titled automatically from the promoted commits; it only needs to stay a valid Conventional Commit
|
|
116
|
+
|
|
117
|
+
## Troubleshooting
|
|
118
|
+
|
|
119
|
+
### Invalid commit message
|
|
120
|
+
|
|
121
|
+
Use the required format:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
type(scope): description
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Committed to wrong branch
|
|
128
|
+
|
|
129
|
+
Prefer safe history-preserving fixes:
|
|
130
|
+
|
|
131
|
+
- `git cherry-pick` to move commit
|
|
132
|
+
- `git revert` to undo shared commits
|
|
133
|
+
- Avoid `git reset --hard` on shared branches
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Configuration reference
|
|
2
|
+
|
|
3
|
+
Every knob, its default, and who reads it. For _where_ a value should live
|
|
4
|
+
(variable or secret, repository or environment) read
|
|
5
|
+
[GITHUB_ACTIONS_SETUP.md](GITHUB_ACTIONS_SETUP.md); for settings and rulesets,
|
|
6
|
+
[REPO_RULES.md](REPO_RULES.md).
|
|
7
|
+
|
|
8
|
+
- [The layers](#the-layers)
|
|
9
|
+
- [Actions variables](#actions-variables)
|
|
10
|
+
- [Secrets](#secrets)
|
|
11
|
+
- [Workflow inputs](#workflow-inputs)
|
|
12
|
+
- [Config files](#config-files)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The layers
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
SHARED (lower precedence) REPO (wins)
|
|
20
|
+
Actions variables config/actions.env + .github/actions.env
|
|
21
|
+
repository secrets <shared secrets dir>/secrets.env + <repo secrets dir>/secrets.env
|
|
22
|
+
environment secrets <shared secrets dir>/secrets.<env>.env + <repo secrets dir>/secrets.<env>.env
|
|
23
|
+
repository settings config/repo-settings.json + .github/repo-settings.json
|
|
24
|
+
branch rulesets config/rulesets/<file>.json + .github/rulesets/<file>.json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **Variables** — the shared file is part of this package and is read **at run
|
|
28
|
+
time** by the actions, from the version the caller pins (`@v2`). Only the
|
|
29
|
+
repo's own keys are pushed to GitHub, by `ebalzan-ci bootstrap`.
|
|
30
|
+
- **Secrets** — merged at sync time by `bootstrap`. The shared secrets dir is
|
|
31
|
+
`$CI_SECRETS_DIR`, else `config/` in a git checkout of this repo, else
|
|
32
|
+
`~/.config/ebalzan-ci/`. A repo's secrets dir is `repos/<owner>/<repo>/`
|
|
33
|
+
inside it when that folder exists (recommended), else the checkout's
|
|
34
|
+
`.github/`.
|
|
35
|
+
- **Settings and rulesets** — JSON, deep-merged by `bootstrap`: objects merge
|
|
36
|
+
key by key, arrays are replaced whole. Top-level keys starting with `$` are
|
|
37
|
+
comments.
|
|
38
|
+
|
|
39
|
+
A repo **inherits** a shared key by not mentioning it, **overrides** it by
|
|
40
|
+
setting the same key, and **adds** any key of its own. An empty repo variable
|
|
41
|
+
counts as unset, so it cannot blank out a shared value — override it with a
|
|
42
|
+
real one.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Actions variables
|
|
47
|
+
|
|
48
|
+
All optional. Set them in a repo's `.github/actions.env` (then `ebalzan-ci
|
|
49
|
+
bootstrap`), or for every repo in `config/actions.env` (then a release of this
|
|
50
|
+
repo). The workflows pass the caller's variables whole
|
|
51
|
+
(`REPO_VARS_JSON: ${{ toJSON(vars) }}`), so a new key needs no workflow edit;
|
|
52
|
+
only `AUTO_*` keys are read.
|
|
53
|
+
|
|
54
|
+
### Project board
|
|
55
|
+
|
|
56
|
+
| Variable | Default | Read by | Meaning |
|
|
57
|
+
| ------------------------------ | ---------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `AUTO_PROJECT_NUMBER` | unset → no board | work items, promote | Projects (v2) board number (the `N` in `/users/me/projects/N`). One board per repo is typical. |
|
|
59
|
+
| `AUTO_PROJECT_OWNER` | the repo owner | work items, promote | Account or organization owning the board. |
|
|
60
|
+
| `AUTO_STRICT_METADATA` | `true` | work items | With a board configured: fail the run when the board, a field or an option is missing. `false` only warns. No effect without a board. |
|
|
61
|
+
| `AUTO_START_DATE_OFFSET_DAYS` | `0` | work items | `Start date` = today + N days. |
|
|
62
|
+
| `AUTO_TARGET_DATE_OFFSET_DAYS` | `7` | work items | `Target date` = today + N days. |
|
|
63
|
+
|
|
64
|
+
The board needs the fields `Start date`, `Target date`, `Priority`
|
|
65
|
+
(single-select with options `P0`…`P4`), `Estimate` and `Size` (looked up by
|
|
66
|
+
name, case-insensitive). See [AUTO_WORK_ITEMS_WORKFLOW.md](AUTO_WORK_ITEMS_WORKFLOW.md#project-v2-behavior).
|
|
67
|
+
|
|
68
|
+
### Priority, size, estimate
|
|
69
|
+
|
|
70
|
+
| Variable | Default (shared file) | Meaning |
|
|
71
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
72
|
+
| `AUTO_PRIORITY_MAP_JSON` | unset → per type: `fix perf revert` P1, `feat refact` P2, `docs test build ci` P3, `style chore` P4 | `{"<type>": "P<n>"}` override per branch type. |
|
|
73
|
+
| `AUTO_DEFAULT_PRIORITY` | unset | Priority for types the map does not list (before the per-type default). |
|
|
74
|
+
| `AUTO_SIZE_MAP_JSON` | `feat refact perf` M, the rest S | `{"<type>": "<size option>"}`. |
|
|
75
|
+
| `AUTO_DEFAULT_SIZE` | `M` | Size for types the map does not list. |
|
|
76
|
+
| `AUTO_ESTIMATE_MAP_JSON` | `feat` 3, `fix refact perf` 2, the rest 1 | `{"<type>": "<number>"}`. |
|
|
77
|
+
| `AUTO_DEFAULT_ESTIMATE` | `1` | Estimate for types the map does not list. |
|
|
78
|
+
|
|
79
|
+
### Labels and milestone
|
|
80
|
+
|
|
81
|
+
| Variable | Default | Meaning |
|
|
82
|
+
| ----------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
|
|
83
|
+
| `AUTO_ISSUE_LABELS` | unset | Extra labels for every tracking issue (comma-separated). Type labels are reserved and filtered out. |
|
|
84
|
+
| `AUTO_PR_LABELS` | unset | Extra labels for every work PR. |
|
|
85
|
+
| `AUTO_MILESTONE_TITLE` | unset | Milestone (exact title, case-insensitive) put on work issues/PRs and the promotion PR. |
|
|
86
|
+
| `AUTO_MILESTONE_NUMBER` | unset | Milestone by number; takes precedence over the title for work items (the promotion PR uses the title). |
|
|
87
|
+
|
|
88
|
+
### Descriptions
|
|
89
|
+
|
|
90
|
+
| Variable | Default | Meaning |
|
|
91
|
+
| ---------------------- | ------------------- | ---------------------------------------------------------------------------------- |
|
|
92
|
+
| `AUTO_DESCRIBE_MODEL` | `claude-sonnet-5-5` | Claude model that writes issue/PR descriptions from the branch's commits and diff. |
|
|
93
|
+
| `AUTO_DESCRIBE_EFFORT` | `low` | `low` · `medium` · `high` · `xhigh` · `max`. Ignored for Haiku models. |
|
|
94
|
+
|
|
95
|
+
Needs the `ANTHROPIC_API_KEY` secret; without it the body is a fixed template.
|
|
96
|
+
|
|
97
|
+
### This repo only
|
|
98
|
+
|
|
99
|
+
| Variable | Default | Meaning |
|
|
100
|
+
| ------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
101
|
+
| `NPM_PUBLISH` | `false` | `true` makes `self-release.yml` publish every release to npm ([CONTRIBUTING](../CONTRIBUTING.md#publishing-to-npm)). |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Secrets
|
|
106
|
+
|
|
107
|
+
| Secret | Needed by | Required? | What |
|
|
108
|
+
| ------------------- | ------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
109
|
+
| `GH_PROJECT_TOKEN` | work items, promote | recommended | **Classic** PAT, scopes `repo` + `project`. Lets the bots write to Projects (v2) boards and makes the PRs they open trigger PR Checks. Falls back to `GITHUB_TOKEN` (no board writes; bot PRs do not start checks until you push to them). |
|
|
110
|
+
| `ANTHROPIC_API_KEY` | work items | optional | Writes the issue/PR descriptions. <https://console.anthropic.com/settings/keys> |
|
|
111
|
+
|
|
112
|
+
`GITHUB_TOKEN` is provided by Actions; never set it. Secrets are passed by name
|
|
113
|
+
in the caller (`secrets: inherit` is only guaranteed within one organization).
|
|
114
|
+
|
|
115
|
+
Why a classic PAT: a fine-grained token only has a Projects permission under
|
|
116
|
+
_organization_ permissions, so it cannot reach a board owned by a personal
|
|
117
|
+
account. The cost is that `repo` cannot be narrowed to selected repos — keep
|
|
118
|
+
the expiry short. Details: [GITHUB_ACTIONS_SETUP.md](GITHUB_ACTIONS_SETUP.md#fill-in-the-secrets).
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Workflow inputs
|
|
123
|
+
|
|
124
|
+
Set in the caller's `with:`. Everything else is a variable, so callers stay
|
|
125
|
+
four lines long.
|
|
126
|
+
|
|
127
|
+
| Workflow | Input | Default | Meaning |
|
|
128
|
+
| --------------------- | ------------- | --------------- | -------------------------------------------------------------------------------------- |
|
|
129
|
+
| all four | `runs-on` | `ubuntu-latest` | Runner label (e.g. `self-hosted`). |
|
|
130
|
+
| `auto-work-items.yml` | `base-branch` | `dev` | Branch the work PRs target. |
|
|
131
|
+
| `release.yml` | `sync-dev` | `true` | Point `dev` at the release commit afterwards. Keep it on unless the repo has no `dev`. |
|
|
132
|
+
|
|
133
|
+
`release.yml` outputs `tag` (`vX.Y.Z`, empty when nothing was released) and
|
|
134
|
+
`sha` (the release commit).
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Config files
|
|
139
|
+
|
|
140
|
+
### In each repo (`.github/`)
|
|
141
|
+
|
|
142
|
+
| File | Committed? | Contents |
|
|
143
|
+
| ---------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
144
|
+
| `actions.env` | yes | The repo's own variables (dotenv). |
|
|
145
|
+
| `secrets.env` | **no** (gitignored) | The repo's own repository secrets, when they are not in `repos/<owner>/<repo>/` of the shared secrets dir. `secrets.env.example` beside it is committed. |
|
|
146
|
+
| `secrets.<env>.env` | **no** | Secrets for GitHub environment `<env>`, created by bootstrap. Same fallback rule. |
|
|
147
|
+
| `bootstrap.conf` | yes | `LOCAL_ONLY_KEYS=A B C` — secret keys only local tools read; never pushed. |
|
|
148
|
+
| `repo-settings.json` | yes | Overrides of `config/repo-settings.json`. |
|
|
149
|
+
| `rulesets/<file>.json` | yes | Overrides of the shared ruleset with the same file name, or extra rulesets. |
|
|
150
|
+
|
|
151
|
+
### In this repo (`config/`)
|
|
152
|
+
|
|
153
|
+
| File | Published to npm? | Contents |
|
|
154
|
+
| ------------------------------ | -------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `actions.env` | yes | Shared variables (above). |
|
|
156
|
+
| `repo-settings.json` | yes | Shared repository settings. |
|
|
157
|
+
| `rulesets/*.json` | yes | Shared branch rulesets. |
|
|
158
|
+
| `repos.env` | yes | `REPOS_TOPIC`, `REPOS_OWNER`, `REPOS` — what `--all` selects; `CI_REPO` — this repo's slug. |
|
|
159
|
+
| `secrets.env.example` | yes | Template for the shared secrets. |
|
|
160
|
+
| `secrets.env`, `secrets.*.env` | **never** (gitignored, not in `files`) | Shared secrets, when you run the tools from a checkout. |
|
|
161
|
+
|
|
162
|
+
`repos.env` keys:
|
|
163
|
+
|
|
164
|
+
| Key | Default | Meaning |
|
|
165
|
+
| ------------- | ------------ | --------------------------------------------------------------------------------------- |
|
|
166
|
+
| `REPOS_TOPIC` | `ebalzan-ci` | `--all` includes every repo of `REPOS_OWNER` with this topic. Empty disables discovery. |
|
|
167
|
+
| `REPOS_OWNER` | the gh user | Account searched for the topic. |
|
|
168
|
+
| `REPOS` | empty | Extra `OWNER/REPO` slugs (space-separated), e.g. repos of an organization. |
|
|
169
|
+
| `CI_REPO` | `ebalzan/ci` | The slug the callers `uses:`. Change it only in a fork. |
|
|
170
|
+
|
|
171
|
+
### dotenv format
|
|
172
|
+
|
|
173
|
+
`KEY=VALUE` per line; `#` comments and blank lines are ignored; one pair of
|
|
174
|
+
surrounding quotes is stripped. No interpolation, no `export`. A secret whose
|
|
175
|
+
key ends in `_FILE` is set from that file's contents under the stripped name
|
|
176
|
+
(for multi-line values such as SSH keys); a relative path is relative to the
|
|
177
|
+
env file.
|