@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
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Auto Work Items Workflow Guide
|
|
2
|
+
|
|
3
|
+
This document explains the automated branch workflow every repo using ebalzan/ci runs.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
When you push to any branch other than `main` or `dev`, the workflow automatically:
|
|
8
|
+
|
|
9
|
+
1. Creates or reuses a tracking **Issue** for the branch.
|
|
10
|
+
2. Creates or reuses a ready-for-review **PR** from the branch to `dev` (the `base-branch` input).
|
|
11
|
+
3. Links the PR to the issue (`Closes #<issue-number>`).
|
|
12
|
+
4. Applies the exact label set to both issue and PR (removes stale extras).
|
|
13
|
+
5. Applies milestone (if configured).
|
|
14
|
+
6. Adds both issue and PR to GitHub Project (v2) (if `AUTO_PROJECT_NUMBER` is set).
|
|
15
|
+
7. Assigns issue and PR to the latest commit author (if resolvable to a GitHub user).
|
|
16
|
+
8. Updates project fields: `Start date`, `Target date`, `Priority`, `Estimate`, and `Size`.
|
|
17
|
+
|
|
18
|
+
The process is idempotent and safe to run on every push.
|
|
19
|
+
|
|
20
|
+
## Workflow Files
|
|
21
|
+
|
|
22
|
+
- Reusable workflow: `.github/workflows/auto-work-items.yml` (this repo)
|
|
23
|
+
- Composite action it runs: `actions/auto-work-items/action.yml`
|
|
24
|
+
- Caller in each repo: `.github/workflows/auto-pr.yml` — only the trigger
|
|
25
|
+
(`push`, `branches-ignore: [main, dev]`), the permissions, and the `uses:` line.
|
|
26
|
+
|
|
27
|
+
## Script Architecture
|
|
28
|
+
|
|
29
|
+
The workflow is orchestrated by `actions/github-script`, but all logic is implemented in TypeScript modules.
|
|
30
|
+
|
|
31
|
+
Source of truth:
|
|
32
|
+
|
|
33
|
+
- `actions/auto-work-items/src/index.ts` (orchestrator)
|
|
34
|
+
- `actions/auto-work-items/src/issue.ts`
|
|
35
|
+
- `actions/auto-work-items/src/pr.ts`
|
|
36
|
+
- `actions/auto-work-items/src/project.ts`
|
|
37
|
+
- `actions/auto-work-items/src/config.ts`
|
|
38
|
+
- `actions/auto-work-items/src/labels.ts`
|
|
39
|
+
- `actions/auto-work-items/src/milestone.ts`
|
|
40
|
+
- `actions/auto-work-items/src/utils.ts`
|
|
41
|
+
- `actions/auto-work-items/src/types.ts`
|
|
42
|
+
|
|
43
|
+
Runtime files used by GitHub Actions:
|
|
44
|
+
|
|
45
|
+
- `actions/auto-work-items/dist/*.js`
|
|
46
|
+
|
|
47
|
+
Why both exist:
|
|
48
|
+
|
|
49
|
+
- `actions/github-script` executes JavaScript, not `.ts` directly.
|
|
50
|
+
- We keep typed source in `src/` and compile to `dist/` for runtime.
|
|
51
|
+
|
|
52
|
+
## Trigger Rules
|
|
53
|
+
|
|
54
|
+
The caller (`auto-pr.yml`) runs it on:
|
|
55
|
+
|
|
56
|
+
- `push` to any branch except `main` and `dev`
|
|
57
|
+
- tags are ignored
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
on:
|
|
61
|
+
push:
|
|
62
|
+
branches-ignore:
|
|
63
|
+
- main
|
|
64
|
+
- dev
|
|
65
|
+
tags-ignore:
|
|
66
|
+
- '**'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The reusable workflow also skips `chore/renovate-*` branches, whose PR
|
|
70
|
+
Renovate owns.
|
|
71
|
+
|
|
72
|
+
## Concurrency and Idempotency
|
|
73
|
+
|
|
74
|
+
Concurrency is enabled per branch:
|
|
75
|
+
|
|
76
|
+
- group: `auto-work-items-${{ github.ref }}`
|
|
77
|
+
- in-progress run for the same branch is canceled
|
|
78
|
+
|
|
79
|
+
Idempotency is handled by a marker in issue/PR bodies:
|
|
80
|
+
|
|
81
|
+
- `Marker: auto-work-item:<branch-name>`
|
|
82
|
+
|
|
83
|
+
If an issue/PR already exists for that branch, it is reused and updated.
|
|
84
|
+
|
|
85
|
+
## End-to-End Flow
|
|
86
|
+
|
|
87
|
+
1. Parse branch name and derive `branchType` (`feat`, `fix`, `chore`, etc.; any other prefix fails the run).
|
|
88
|
+
2. Build titles, body content, marker, labels, and priority decision.
|
|
89
|
+
3. Ensure labels exist and sync color/description to expected values.
|
|
90
|
+
4. Resolve milestone by number (preferred) or exact title.
|
|
91
|
+
5. Upsert tracking issue.
|
|
92
|
+
6. Upsert PR (opened ready for review, not draft) to `AUTO_BASE_BRANCH` (`dev`).
|
|
93
|
+
7. Ensure PR includes `Closes #<issue-number>`.
|
|
94
|
+
8. Add issue and PR to configured Project v2.
|
|
95
|
+
9. Resolve project fields by name (`Start date`, `Target date`, `Priority`, `Estimate`, `Size`).
|
|
96
|
+
10. Set date fields and metadata fields on both items.
|
|
97
|
+
11. Emit workflow notice with final issue/PR numbers.
|
|
98
|
+
|
|
99
|
+
## Branch Type Mapping
|
|
100
|
+
|
|
101
|
+
Current branch-type defaults:
|
|
102
|
+
|
|
103
|
+
| Branch Type | PR Prefix | Type Label | Default Priority | Label Color |
|
|
104
|
+
| ----------- | --------- | ---------- | ---------------- | ----------- |
|
|
105
|
+
| `feat` | `feat` | `feat` | `P2` | `1D76DB` |
|
|
106
|
+
| `fix` | `fix` | `fix` | `P1` | `D73A4A` |
|
|
107
|
+
| `docs` | `docs` | `docs` | `P3` | `5319E7` |
|
|
108
|
+
| `style` | `style` | `style` | `P4` | `C2E0C6` |
|
|
109
|
+
| `refact` | `refact` | `refact` | `P2` | `FBCA04` |
|
|
110
|
+
| `perf` | `perf` | `perf` | `P1` | `F9D0C4` |
|
|
111
|
+
| `test` | `test` | `test` | `P3` | `0E8A16` |
|
|
112
|
+
| `build` | `build` | `build` | `P3` | `0052CC` |
|
|
113
|
+
| `ci` | `ci` | `ci` | `P3` | `0052CC` |
|
|
114
|
+
| `chore` | `chore` | `chore` | `P4` | `BFD4F2` |
|
|
115
|
+
| `revert` | `revert` | `revert` | `P1` | `E99695` |
|
|
116
|
+
|
|
117
|
+
These are the only accepted branch types. Any other prefix (`feature/`, `hotfix/`,
|
|
118
|
+
`refactor/`, …) fails the run with no issue or PR created — rename the branch.
|
|
119
|
+
|
|
120
|
+
## Labels Applied
|
|
121
|
+
|
|
122
|
+
Always applied labels:
|
|
123
|
+
|
|
124
|
+
- type label from branch type (for example `feat`, `refact`, `chore`)
|
|
125
|
+
|
|
126
|
+
Optional extra labels from variables:
|
|
127
|
+
|
|
128
|
+
- `AUTO_ISSUE_LABELS` (CSV)
|
|
129
|
+
- `AUTO_PR_LABELS` (CSV)
|
|
130
|
+
|
|
131
|
+
Note:
|
|
132
|
+
|
|
133
|
+
- Reserved type labels (for example `feat`, `fix`, `chore`, `ci`) are controlled by branch type and filtered out from `AUTO_ISSUE_LABELS` and `AUTO_PR_LABELS`.
|
|
134
|
+
|
|
135
|
+
Label color behavior:
|
|
136
|
+
|
|
137
|
+
- Known labels (type labels) use configured fixed colors.
|
|
138
|
+
- Custom/extra labels use a deterministic non-gray palette.
|
|
139
|
+
- `priority:P0` through `priority:P4` map to priority-specific colors.
|
|
140
|
+
- Existing labels are updated when current color/description drift from expected values.
|
|
141
|
+
|
|
142
|
+
## Milestone Behavior
|
|
143
|
+
|
|
144
|
+
Milestone resolution order:
|
|
145
|
+
|
|
146
|
+
1. `AUTO_MILESTONE_NUMBER` (numeric)
|
|
147
|
+
2. `AUTO_MILESTONE_TITLE` (exact case-insensitive title match)
|
|
148
|
+
3. none only when both variables are unset
|
|
149
|
+
|
|
150
|
+
When resolved, milestone is set on both issue and PR.
|
|
151
|
+
A milestone that cannot be found (or is closed) produces a warning, not a
|
|
152
|
+
failure.
|
|
153
|
+
|
|
154
|
+
Setup guidance:
|
|
155
|
+
|
|
156
|
+
- If you want stable pinning, set `AUTO_MILESTONE_NUMBER`.
|
|
157
|
+
- If you want name-based matching, set `AUTO_MILESTONE_TITLE` and leave `AUTO_MILESTONE_NUMBER` unset.
|
|
158
|
+
- Do not keep a stale `AUTO_MILESTONE_NUMBER`; it takes precedence over title.
|
|
159
|
+
|
|
160
|
+
## Project v2 Behavior
|
|
161
|
+
|
|
162
|
+
Project selection:
|
|
163
|
+
|
|
164
|
+
- owner: `AUTO_PROJECT_OWNER` (defaults to repo owner)
|
|
165
|
+
- project number: `AUTO_PROJECT_NUMBER` — unset means no board: the issue and
|
|
166
|
+
PR are still created, and the board step is skipped
|
|
167
|
+
|
|
168
|
+
Field lookup is by name (case-insensitive):
|
|
169
|
+
|
|
170
|
+
- `Start date`
|
|
171
|
+
- `Target date`
|
|
172
|
+
- `Priority` (single-select)
|
|
173
|
+
- `Estimate`
|
|
174
|
+
- `Size`
|
|
175
|
+
|
|
176
|
+
There are no custom field-name variables in the current implementation.
|
|
177
|
+
|
|
178
|
+
Date values:
|
|
179
|
+
|
|
180
|
+
- Start = `today + AUTO_START_DATE_OFFSET_DAYS` (default `0`)
|
|
181
|
+
- Target = `today + AUTO_TARGET_DATE_OFFSET_DAYS` (default `7`)
|
|
182
|
+
|
|
183
|
+
Priority value:
|
|
184
|
+
|
|
185
|
+
1. `AUTO_PRIORITY_MAP_JSON[branchType]`
|
|
186
|
+
2. `AUTO_DEFAULT_PRIORITY`
|
|
187
|
+
3. branch default from mapping table
|
|
188
|
+
|
|
189
|
+
If the requested priority is unavailable in the project field options, the workflow falls back to the nearest available `P<number>` option.
|
|
190
|
+
|
|
191
|
+
Size value:
|
|
192
|
+
|
|
193
|
+
1. `AUTO_SIZE_MAP_JSON[branchType]`
|
|
194
|
+
2. `AUTO_DEFAULT_SIZE`
|
|
195
|
+
3. fallback: `M`
|
|
196
|
+
|
|
197
|
+
Estimate value:
|
|
198
|
+
|
|
199
|
+
1. `AUTO_ESTIMATE_MAP_JSON[branchType]`
|
|
200
|
+
2. `AUTO_DEFAULT_ESTIMATE`
|
|
201
|
+
3. fallback: `1`
|
|
202
|
+
|
|
203
|
+
## Strict vs Non-Strict Metadata Mode
|
|
204
|
+
|
|
205
|
+
`AUTO_STRICT_METADATA` controls failure behavior for project metadata sync.
|
|
206
|
+
|
|
207
|
+
- `true` (default): with a board configured, a missing project, field or
|
|
208
|
+
option fails the workflow.
|
|
209
|
+
- `false`: the same problems only emit warnings.
|
|
210
|
+
- Without `AUTO_PROJECT_NUMBER` neither applies: the board is skipped.
|
|
211
|
+
|
|
212
|
+
This is useful when metadata is mandatory for your process.
|
|
213
|
+
|
|
214
|
+
## Variables and Secret Reference
|
|
215
|
+
|
|
216
|
+
`auto-work-items.yml` passes the caller repo's variables whole
|
|
217
|
+
(`REPO_VARS_JSON: ${{ toJSON(vars) }}`) — the repo's `.github/actions.env`,
|
|
218
|
+
pushed by `ebalzan-ci bootstrap` — and the action takes their `AUTO_*` keys. A
|
|
219
|
+
knob the repo leaves unset is filled from `config/actions.env` at the action's
|
|
220
|
+
ref (`src/sharedVars.ts`), else the code default applies. So a knob is set in
|
|
221
|
+
the repo's file (then the bootstrap) or in `config/actions.env` (then a release
|
|
222
|
+
that moves the major tag) — never in the workflow. Defaults and meanings:
|
|
223
|
+
[CONFIGURATION.md](CONFIGURATION.md#actions-variables).
|
|
224
|
+
|
|
225
|
+
- `AUTO_BASE_BRANCH` (from the `base-branch` input, default `dev`)
|
|
226
|
+
- `AUTO_ISSUE_LABELS`
|
|
227
|
+
- `AUTO_PR_LABELS`
|
|
228
|
+
- `AUTO_MILESTONE_NUMBER`
|
|
229
|
+
- `AUTO_MILESTONE_TITLE`
|
|
230
|
+
- `AUTO_PROJECT_OWNER`
|
|
231
|
+
- `AUTO_PROJECT_NUMBER` (repo variable — one board per repo)
|
|
232
|
+
- `AUTO_DEFAULT_PRIORITY`
|
|
233
|
+
- `AUTO_PRIORITY_MAP_JSON`
|
|
234
|
+
- `AUTO_DEFAULT_SIZE`
|
|
235
|
+
- `AUTO_SIZE_MAP_JSON`
|
|
236
|
+
- `AUTO_DEFAULT_ESTIMATE`
|
|
237
|
+
- `AUTO_ESTIMATE_MAP_JSON`
|
|
238
|
+
- `AUTO_START_DATE_OFFSET_DAYS`
|
|
239
|
+
- `AUTO_TARGET_DATE_OFFSET_DAYS`
|
|
240
|
+
- `AUTO_STRICT_METADATA` (defaults to `true` if unset)
|
|
241
|
+
- `AUTO_DESCRIBE_MODEL`, `AUTO_DESCRIBE_EFFORT` (describe.ts)
|
|
242
|
+
- secret `ANTHROPIC_API_KEY` (describe.ts; without it the body is a stub)
|
|
243
|
+
|
|
244
|
+
Notes:
|
|
245
|
+
|
|
246
|
+
- Without `AUTO_PROJECT_NUMBER` the board is skipped, whatever `AUTO_STRICT_METADATA` says.
|
|
247
|
+
- `GH_PROJECT_TOKEN` is recommended when `github.token` cannot write Project v2 metadata.
|
|
248
|
+
|
|
249
|
+
## Permissions Used
|
|
250
|
+
|
|
251
|
+
Workflow permissions (the caller must grant them; a reusable workflow cannot
|
|
252
|
+
raise its caller's):
|
|
253
|
+
|
|
254
|
+
- `contents: read`
|
|
255
|
+
- `issues: write`
|
|
256
|
+
- `pull-requests: write`
|
|
257
|
+
- `repository-projects: write`
|
|
258
|
+
|
|
259
|
+
Token source used by workflow:
|
|
260
|
+
|
|
261
|
+
- `secrets.GH_PROJECT_TOKEN` if present
|
|
262
|
+
- otherwise falls back to `github.token`
|
|
263
|
+
|
|
264
|
+
## Build and Maintenance
|
|
265
|
+
|
|
266
|
+
Compile TypeScript workflow scripts:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
npm run build # in a checkout of ebalzan/ci
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Important:
|
|
273
|
+
|
|
274
|
+
- Commit both `src/` and `dist/` changes. CI (`npm run check`) fails when
|
|
275
|
+
`dist/` does not match `src/`.
|
|
276
|
+
- Callers run `dist/` straight from the action download — nothing builds it on
|
|
277
|
+
the runner. A change reaches the repos only once the major tag moves
|
|
278
|
+
([CONTRIBUTING → Releasing](../CONTRIBUTING.md#releasing-a-change)).
|
|
279
|
+
|
|
280
|
+
## Troubleshooting
|
|
281
|
+
|
|
282
|
+
### "Context access might be invalid"
|
|
283
|
+
|
|
284
|
+
This warning usually comes from static editor validation, not Actions runtime.
|
|
285
|
+
It is common with `vars.*` and `secrets.*` in workflow `env` blocks — which is
|
|
286
|
+
why the workflows pass `toJSON(vars)` instead of naming each variable.
|
|
287
|
+
|
|
288
|
+
### Common runtime failures
|
|
289
|
+
|
|
290
|
+
- Unsupported branch prefix (`feature/`, `hotfix/`, `refactor/`, …): rename the branch
|
|
291
|
+
- Project not found (`AUTO_PROJECT_OWNER` / `AUTO_PROJECT_NUMBER` mismatch)
|
|
292
|
+
- Missing required fields in Project v2 (`Start date`, `Target date`, `Priority`, `Estimate`, `Size`)
|
|
293
|
+
- Priority option text does not exist in the project field
|
|
294
|
+
- Token missing permission for project updates
|
|
295
|
+
- Milestone title not found
|
|
296
|
+
- Stale `AUTO_MILESTONE_NUMBER` overriding `AUTO_MILESTONE_TITLE`
|
|
297
|
+
|
|
298
|
+
### Quick verification checklist
|
|
299
|
+
|
|
300
|
+
1. Push a test branch like `chore/workflow-test`.
|
|
301
|
+
2. Confirm issue + PR are created.
|
|
302
|
+
3. Confirm PR body includes `Closes #...`.
|
|
303
|
+
4. Confirm issue and PR are added to the right project.
|
|
304
|
+
5. Confirm issue and PR assignees are set to the latest commit author.
|
|
305
|
+
6. Confirm `Start date`/`Target date`/`Priority`/`Estimate`/`Size` fields are set correctly.
|
package/docs/CI_FLOW.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# CI/CD Flow (shared stages)
|
|
2
|
+
|
|
3
|
+
The branch → `dev` → `main` → release flow every repo using ebalzan/ci runs.
|
|
4
|
+
This file covers the stages this repo owns; what happens **after** a release
|
|
5
|
+
(a container rollout, a Vercel deploy, an app store build) and each repo's own
|
|
6
|
+
build/test checks belong to that repo, chained on the stages below. This repo
|
|
7
|
+
runs the same flow on itself (`.github/workflows/self-*.yml`), where the release
|
|
8
|
+
also moves the major tag the other repos pin
|
|
9
|
+
([CONTRIBUTING → Releasing](../CONTRIBUTING.md#releasing-a-change)).
|
|
10
|
+
|
|
11
|
+
> **Model:** feature branch → `dev` → `main`.
|
|
12
|
+
> `dev` is the integration branch. `main` is the release branch.
|
|
13
|
+
> Every push to `main` produces a versioned release, which the repo then ships.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 1. Branching model
|
|
18
|
+
|
|
19
|
+
| Branch | Role | Receives merges from | Protected |
|
|
20
|
+
| ------------------------------- | ----------------------- | -------------------- | --------- |
|
|
21
|
+
| `main` | Releases | `dev` only | Yes ¹ |
|
|
22
|
+
| `dev` | Integration of features | feature branches | Yes ¹ |
|
|
23
|
+
| `feat/*`, `fix/*`, `chore/*`, … | Short-lived work | (you push to them) | No |
|
|
24
|
+
|
|
25
|
+
Supported work-branch prefixes (each maps to a release type for the
|
|
26
|
+
feature → `dev` PR check): `feat`, `fix`, `docs`, `style`, `refact`, `perf`,
|
|
27
|
+
`test`, `build`, `ci`, `chore`, `revert`. Any other prefix fails both the PR
|
|
28
|
+
check and the auto work-items run. A `refact/` branch takes a `refact:` title;
|
|
29
|
+
there is no `refactor` type, and no `feature/` or `hotfix/` alias.
|
|
30
|
+
|
|
31
|
+
¹ By the rulesets `bootstrap` applies: neither can be deleted, `main` cannot be
|
|
32
|
+
force-pushed, and an opt-in ruleset requires PRs ([REPO_RULES.md](REPO_RULES.md)).
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. The flow at a glance
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
feature branch ── git push
|
|
40
|
+
│
|
|
41
|
+
▼
|
|
42
|
+
(1) Auto Create Work Items auto-work-items.yml
|
|
43
|
+
│ Issue + PR (base = dev) + project card
|
|
44
|
+
▼
|
|
45
|
+
PR: feat → dev ── (2) PR Checks: pr-title.yml + the repo's own jobs
|
|
46
|
+
│ ✅ squash-merge into dev
|
|
47
|
+
▼
|
|
48
|
+
(3) Promote dev to main promote-dev-to-main.yml
|
|
49
|
+
│ ONE PR: dev → main (auto title + commit list)
|
|
50
|
+
▼
|
|
51
|
+
PR: dev → main ── (2) PR Checks again (base = main)
|
|
52
|
+
│ ✅ squash-merge into main ← this is the release approval
|
|
53
|
+
▼
|
|
54
|
+
(4) Release release.yml
|
|
55
|
+
│ bump from commits → tag vX.Y.Z → GitHub Release → dev synced
|
|
56
|
+
▼
|
|
57
|
+
(5) Ship — the repo's own job, chained on release.yml's `tag` / `sha` outputs
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Each stage is a reusable workflow here, called from a thin file in the repo
|
|
61
|
+
that holds only the trigger, the permissions and the `uses:` line.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 3. Stage by stage
|
|
66
|
+
|
|
67
|
+
### Stage 1 — Auto Create Work Items
|
|
68
|
+
|
|
69
|
+
- **Reusable workflow:** `.github/workflows/auto-work-items.yml`
|
|
70
|
+
- **Repo caller:** `.github/workflows/auto-pr.yml`, triggered on `push` to any
|
|
71
|
+
branch except `main` and `dev` (never on tags).
|
|
72
|
+
- **What it does:** creates or updates (idempotent per branch) a GitHub
|
|
73
|
+
**Issue**, a **PR** into `dev`, and the **project card** with labels,
|
|
74
|
+
milestone, priority, size, estimate and dates. Details:
|
|
75
|
+
[AUTO_WORK_ITEMS_WORKFLOW.md](./AUTO_WORK_ITEMS_WORKFLOW.md).
|
|
76
|
+
- **Token:** `GH_PROJECT_TOKEN` if present, else `github.token`. A classic PAT
|
|
77
|
+
with the `project` scope is required to write to a user-owned board, and so
|
|
78
|
+
that the created PR triggers Stage 2.
|
|
79
|
+
|
|
80
|
+
### Stage 2 — PR Checks
|
|
81
|
+
|
|
82
|
+
- **Repo file:** `.github/workflows/pr-checks.yml`, on `pull_request` into
|
|
83
|
+
`main` or `dev` (`opened`, `synchronize`, `reopened`, `ready_for_review`).
|
|
84
|
+
- **Shared job** (`pr-title.yml` → `actions/pr-title`):
|
|
85
|
+
1. `validate-conventional-subject.sh` — the title must be a Conventional
|
|
86
|
+
Commits subject (e.g. `feat(search): add filters`).
|
|
87
|
+
2. `verify-pr-release-type.mjs` — checks the title's release type against the
|
|
88
|
+
**branch**:
|
|
89
|
+
- **Feature branch** (`feat/...` → `dev`): the title's type must match the
|
|
90
|
+
branch type, unless the title marks a breaking change with `!`.
|
|
91
|
+
- **Promotion** (`dev` → `main`): only requires a valid conventional title;
|
|
92
|
+
the bump is computed later from the promoted commits.
|
|
93
|
+
3. Managed-file drift: if the repo still keeps a (deprecated) local copy of a
|
|
94
|
+
`managed/` file, it must equal this version's ([CLI → sync-managed](CLI.md#sync-managed)).
|
|
95
|
+
- **Repo jobs** (validate, build, image build, delivery preview, …) run in
|
|
96
|
+
parallel beside it. A bad title still fails the run.
|
|
97
|
+
- The check is reported as **`<caller job id> / Validate PR Title`**. Name the
|
|
98
|
+
caller job `title` (the template does): `title / Validate PR Title` is the
|
|
99
|
+
context the `ci: require pull request` ruleset requires.
|
|
100
|
+
|
|
101
|
+
### Stage 3 — Promote dev to main
|
|
102
|
+
|
|
103
|
+
- **Reusable workflow:** `.github/workflows/promote-dev-to-main.yml`
|
|
104
|
+
(`scripts/release/promote-dev-to-main.mjs`)
|
|
105
|
+
- **Repo caller:** `push` to `dev`, plus `workflow_dispatch`.
|
|
106
|
+
- **What it does:**
|
|
107
|
+
1. Compares `main...dev`. If `dev` is not ahead, it stops.
|
|
108
|
+
2. Computes the bump from the promoted commits and creates or updates a
|
|
109
|
+
single `dev → main` PR titled with the most significant type among them
|
|
110
|
+
(`feat` > `fix` > `refact` > `perf` > `revert` > `build` > `ci` > `docs` >
|
|
111
|
+
`test` > `style` > `chore`, `!` for breaking) plus the projected version,
|
|
112
|
+
e.g. `refact: promote dev to main (v0.6.3)`. The body lists every commit.
|
|
113
|
+
3. Best-effort metadata (a failure warns, never fails the run): assignee = who
|
|
114
|
+
pushed to `dev`; labels `release` + each promoted commit type (only ever
|
|
115
|
+
added); the `AUTO_MILESTONE_TITLE` milestone and the project board, when
|
|
116
|
+
configured.
|
|
117
|
+
- **Token:** `GH_PROJECT_TOKEN` if present, else `GITHUB_TOKEN`.
|
|
118
|
+
⚠️ A PR opened with the default `GITHUB_TOKEN` does not trigger other
|
|
119
|
+
workflows — without the PAT, PR Checks will not run on the promotion PR until
|
|
120
|
+
you push to it or reopen it.
|
|
121
|
+
|
|
122
|
+
### Stage 4 — Release
|
|
123
|
+
|
|
124
|
+
- **Reusable workflow:** `.github/workflows/release.yml`
|
|
125
|
+
(`scripts/release/release-main.mjs`)
|
|
126
|
+
- **Repo caller:** `push` to `main`.
|
|
127
|
+
- **Skip guard:** skipped when the head commit starts with `chore(release):`,
|
|
128
|
+
so the release commit cannot release again.
|
|
129
|
+
- **What it does:**
|
|
130
|
+
1. Reads the latest `vX.Y.Z` tag (none yet: the `package.json` version, or `0.0.0`).
|
|
131
|
+
2. Computes the bump from every Conventional Commit since it (§4).
|
|
132
|
+
3. With a `package.json`: bumps it (and the lockfile, if any), commits
|
|
133
|
+
`chore(release): X.Y.Z`, tags `vX.Y.Z`. Without one: tags the merged
|
|
134
|
+
commit as is (tag-only release). Pushes the branch and the tag.
|
|
135
|
+
4. Publishes a GitHub Release (associated PR + included commits).
|
|
136
|
+
5. `sync-dev`: force-points `dev` at the release commit (§4, "Squash
|
|
137
|
+
everything").
|
|
138
|
+
- **Outputs:** `tag` (`vX.Y.Z`, empty when nothing was released) and `sha`
|
|
139
|
+
(the release commit). The repo's own ship jobs chain on them.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 4. Version bump rules (commit-scan)
|
|
144
|
+
|
|
145
|
+
The bump is derived from **the Conventional Commits landed on `main` since the
|
|
146
|
+
previous tag**, not from any branch name. Highest wins:
|
|
147
|
+
|
|
148
|
+
| Found in any commit since last tag | Bump |
|
|
149
|
+
| ---------------------------------------------------------------------------------- | ----------------- |
|
|
150
|
+
| `!` after type, or `BREAKING CHANGE:` footer | `major` |
|
|
151
|
+
| `feat:` | `minor` |
|
|
152
|
+
| `fix`, `perf`, `docs`, `style`, `refact`, `test`, `build`, `ci`, `chore`, `revert` | `patch` |
|
|
153
|
+
| no Conventional Commit found | release **fails** |
|
|
154
|
+
|
|
155
|
+
Implementation: `highestRelease()` in `scripts/release/release-utils.mjs`, used
|
|
156
|
+
by both `release-main.mjs` (the bump) and `promote-dev-to-main.mjs` (the title
|
|
157
|
+
preview).
|
|
158
|
+
|
|
159
|
+
### Why this works for either merge method on `dev → main`
|
|
160
|
+
|
|
161
|
+
- **Squash merge:** `main` gets one commit using the PR title; because the
|
|
162
|
+
promotion title is generated to carry the highest bump, the scan still yields
|
|
163
|
+
the right version. **This is the method every repo uses** — the shared
|
|
164
|
+
repo settings allow nothing else.
|
|
165
|
+
- **Merge commit:** the feature commits flow into `main`; the scan (which skips
|
|
166
|
+
merge commits) sees each one.
|
|
167
|
+
|
|
168
|
+
> **Squash everything.** A squash (or rebase) rewrites the promoted commits, so
|
|
169
|
+
> `dev` and `main` end up with identical content under unrelated histories, and
|
|
170
|
+
> the _next_ promotion would re-merge everything from the last common ancestor
|
|
171
|
+
> and conflict against itself. `sync-dev` prevents that by force-pointing `dev`
|
|
172
|
+
> at the release commit after each release. Consequence: if you have local
|
|
173
|
+
> commits on `dev` that were not released, `git reset --hard origin/dev` after a
|
|
174
|
+
> release and branch off it.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 5. Developer flow (cheat sheet)
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
git checkout dev && git pull origin dev
|
|
182
|
+
git checkout -b feat/your-change
|
|
183
|
+
git commit -m "feat(search): add result filters" # husky checks it
|
|
184
|
+
git push -u origin feat/your-change # Stage 1 opens Issue + PR into dev
|
|
185
|
+
# merge into dev (squash) once PR Checks pass
|
|
186
|
+
# Stage 3 opens/updates dev → main; merge it (squash) to release
|
|
187
|
+
# Stage 4 versions, tags, releases; the repo ships the tag
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
You never edit versions, tags or release notes by hand.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 6. Troubleshooting
|
|
195
|
+
|
|
196
|
+
| Symptom | Likely cause / fix |
|
|
197
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
198
|
+
| Workflow fails to start: "workflow was not found" | Wrong `uses:` path or ref, or the repo's Actions policy does not allow actions from other repos (Settings → Actions → General). |
|
|
199
|
+
| "The nested job is requesting 'X', but is only allowed…" | The caller's `permissions:` lacks what the reusable job declares. Grant it in the caller. |
|
|
200
|
+
| No Issue/PR after pushing a feature branch | Branch was `dev`/`main` (ignored), unsupported prefix, or `GH_PROJECT_TOKEN` missing/insufficient. |
|
|
201
|
+
| `dev → main` PR exists but no checks ran | Opened with the default `GITHUB_TOKEN`. Set `GH_PROJECT_TOKEN`, or push to it. |
|
|
202
|
+
| PR Checks: "differs from ebalzan/ci's managed copy" | Delete the local copy and use the hook from the package ([CLI](CLI.md#commit-msg)). |
|
|
203
|
+
| Release failed: "Could not determine a release type" | No Conventional Commit since the last tag. |
|
|
204
|
+
| Behaviour did not change after editing ebalzan/ci | Callers pin `@v2`: the change ships only once it is released and `v2` moves. |
|
|
205
|
+
| Work items: "unsupported type" | The branch prefix is not one of the types above. Rename the branch. |
|