start-vibing-stacks 2.6.0 → 2.7.3
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 +83 -135
- package/dist/index.js +16 -2
- package/dist/migrate.d.ts +27 -0
- package/dist/migrate.js +217 -0
- package/dist/setup.js +10 -0
- package/package.json +1 -1
- package/stacks/_shared/agents/claude-md-compactor.md +1 -0
- package/stacks/_shared/agents/commit-manager.md +1 -0
- package/stacks/_shared/agents/documenter.md +1 -0
- package/stacks/_shared/agents/domain-updater.md +1 -0
- package/stacks/_shared/agents/research-web.md +1 -0
- package/stacks/_shared/agents/security-auditor.md +168 -0
- package/stacks/_shared/agents/tester.md +1 -0
- package/stacks/_shared/hooks/final-check.ts +205 -0
- package/stacks/_shared/hooks/stop-validator.ts +77 -1
- package/stacks/_shared/skills/accessibility-wcag22/SKILL.md +284 -0
- package/stacks/_shared/skills/ci-pipelines/SKILL.md +166 -0
- package/stacks/_shared/skills/codebase-knowledge/SKILL.md +5 -0
- package/stacks/_shared/skills/database-migrations/SKILL.md +256 -0
- package/stacks/_shared/skills/debugging-patterns/SKILL.md +5 -0
- package/stacks/_shared/skills/docker-patterns/SKILL.md +5 -0
- package/stacks/_shared/skills/docs-tracker/SKILL.md +5 -0
- package/stacks/_shared/skills/error-handling/SKILL.md +335 -0
- package/stacks/_shared/skills/final-check/SKILL.md +74 -37
- package/stacks/_shared/skills/git-workflow/SKILL.md +5 -0
- package/stacks/_shared/skills/hook-development/SKILL.md +5 -0
- package/stacks/_shared/skills/observability/SKILL.md +351 -0
- package/stacks/_shared/skills/performance-patterns/SKILL.md +5 -0
- package/stacks/_shared/skills/playwright-automation/SKILL.md +5 -0
- package/stacks/_shared/skills/quality-gate/SKILL.md +5 -0
- package/stacks/_shared/skills/research-cache/SKILL.md +5 -0
- package/stacks/_shared/skills/secrets-management/SKILL.md +245 -0
- package/stacks/_shared/skills/security-baseline/SKILL.md +202 -0
- package/stacks/_shared/skills/test-coverage/SKILL.md +5 -0
- package/stacks/_shared/skills/ui-ux-audit/SKILL.md +5 -0
- package/stacks/frontend/react/skills/preline-ui/SKILL.md +5 -0
- package/stacks/frontend/react/skills/react-patterns/SKILL.md +5 -0
- package/stacks/frontend/react/skills/react-standards/SKILL.md +5 -0
- package/stacks/frontend/react/skills/react-ui-patterns/SKILL.md +5 -0
- package/stacks/frontend/react/skills/shadcn-ui/SKILL.md +5 -0
- package/stacks/frontend/react/skills/tailwind-patterns/SKILL.md +5 -0
- package/stacks/frontend/react/skills/zod-validation/SKILL.md +5 -0
- package/stacks/frontend/react-inertia/skills/inertia-react/SKILL.md +5 -0
- package/stacks/frontend/react-inertia/skills/react-standards/SKILL.md +5 -0
- package/stacks/nodejs/skills/api-security-node/SKILL.md +275 -0
- package/stacks/nodejs/skills/bun-runtime/SKILL.md +5 -0
- package/stacks/nodejs/skills/mongoose-patterns/SKILL.md +5 -0
- package/stacks/nodejs/skills/nextjs-app-router/SKILL.md +5 -0
- package/stacks/nodejs/skills/trpc-api/SKILL.md +5 -0
- package/stacks/nodejs/skills/typescript-strict/SKILL.md +5 -0
- package/stacks/nodejs/stack.json +2 -1
- package/stacks/nodejs/workflows/ci.yml +90 -0
- package/stacks/nodejs/workflows/security.yml +45 -0
- package/stacks/php/skills/api-design/SKILL.md +5 -0
- package/stacks/php/skills/api-security/SKILL.md +5 -0
- package/stacks/php/skills/composer-workflow/SKILL.md +5 -0
- package/stacks/php/skills/external-api-patterns/SKILL.md +5 -0
- package/stacks/php/skills/inertia-react/SKILL.md +5 -0
- package/stacks/php/skills/laravel-inertia-i18n/SKILL.md +5 -0
- package/stacks/php/skills/laravel-octane/SKILL.md +5 -0
- package/stacks/php/skills/laravel-patterns/SKILL.md +5 -0
- package/stacks/php/skills/mariadb-octane/SKILL.md +5 -0
- package/stacks/php/skills/php-patterns/SKILL.md +5 -0
- package/stacks/php/skills/phpstan-analysis/SKILL.md +5 -0
- package/stacks/php/skills/phpunit-testing/SKILL.md +5 -0
- package/stacks/php/skills/security-scan-php/SKILL.md +5 -0
- package/stacks/php/workflows/ci.yml +106 -0
- package/stacks/php/workflows/security.yml +36 -0
- package/stacks/python/skills/api-security-python/SKILL.md +312 -0
- package/stacks/python/skills/async-patterns/SKILL.md +5 -0
- package/stacks/python/skills/django-patterns/SKILL.md +5 -0
- package/stacks/python/skills/fastapi-patterns/SKILL.md +5 -0
- package/stacks/python/skills/pydantic-validation/SKILL.md +5 -0
- package/stacks/python/skills/pytest-testing/SKILL.md +5 -0
- package/stacks/python/skills/python-patterns/SKILL.md +5 -0
- package/stacks/python/skills/python-performance/SKILL.md +5 -0
- package/stacks/python/skills/scripting-automation/SKILL.md +5 -0
- package/stacks/python/stack.json +2 -1
- package/stacks/python/workflows/ci.yml +76 -0
- package/stacks/python/workflows/security.yml +56 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accessibility-wcag22
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: WCAG 2.2 AA checklist + axe-core / Playwright integration. Invoke when implementing UI features or auditing accessibility. Pairs with ui-ux-audit.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Accessibility — WCAG 2.2 AA
|
|
8
|
+
|
|
9
|
+
**ALWAYS invoke when implementing UI, forms, navigation, or any user-facing component.**
|
|
10
|
+
|
|
11
|
+
> Accessibility is a feature, not a polish step. Building it in costs ~10x less than retrofitting.
|
|
12
|
+
|
|
13
|
+
## Targeting WCAG 2.2 AA
|
|
14
|
+
|
|
15
|
+
WCAG 2.2 (October 2023) adds 9 new success criteria over 2.1. Key additions:
|
|
16
|
+
|
|
17
|
+
| New (2.2) | What | Most relevant when |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| 2.4.11 / 2.4.12 Focus Not Obscured | Focused element must not be fully hidden by sticky headers/dialogs | Sticky nav, modals |
|
|
20
|
+
| 2.4.13 Focus Appearance (AAA) | Focus indicator size/contrast | Custom focus styles |
|
|
21
|
+
| 2.5.7 Dragging Movements | Drag operations must have a non-drag alternative | Kanban, sliders, reordering |
|
|
22
|
+
| 2.5.8 Target Size (Min) | Targets ≥ 24×24 CSS px | Buttons, icon links, list items |
|
|
23
|
+
| 3.2.6 Consistent Help | Help mechanism in same location across pages | Multi-page apps |
|
|
24
|
+
| 3.3.7 / 3.3.8 Redundant Entry / Accessible Authentication | Don't require re-entering data; offer non-cognitive auth | Multi-step forms, login |
|
|
25
|
+
|
|
26
|
+
## The "Big 8" — Catches 80% of Issues
|
|
27
|
+
|
|
28
|
+
1. **Semantic HTML** — `<button>` not `<div onClick>`. `<a href>` for navigation, `<button>` for actions.
|
|
29
|
+
2. **Keyboard navigation** — every interactive thing reachable via Tab, activatable via Enter/Space.
|
|
30
|
+
3. **Focus indicator** — visible outline on focus. Don't `outline: none` without a replacement.
|
|
31
|
+
4. **Labels** — every form input has `<label for>` or `aria-label`.
|
|
32
|
+
5. **Color contrast** — text 4.5:1 (3:1 for ≥18pt or 14pt bold). Non-text UI 3:1.
|
|
33
|
+
6. **Alt text** — every meaningful image has `alt`; decorative images have `alt=""`.
|
|
34
|
+
7. **Heading order** — one `<h1>` per page; no skipping levels (h2 → h4).
|
|
35
|
+
8. **ARIA only when needed** — first rule of ARIA: don't use ARIA. Native HTML almost always wins.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## React + Semantic HTML
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// WRONG
|
|
43
|
+
<div className="button" onClick={handleClick}>Submit</div>
|
|
44
|
+
|
|
45
|
+
// CORRECT
|
|
46
|
+
<button type="button" onClick={handleClick}>Submit</button>
|
|
47
|
+
|
|
48
|
+
// WRONG — visually a link, semantically a button
|
|
49
|
+
<a onClick={navigate}>Settings</a>
|
|
50
|
+
|
|
51
|
+
// CORRECT
|
|
52
|
+
<a href="/settings">Settings</a>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Accessible custom controls
|
|
56
|
+
If you must build a custom widget (Combobox, Disclosure, Tabs), follow the **WAI-ARIA Authoring Practices**: https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
57
|
+
|
|
58
|
+
Better — use a headless library that already implements them:
|
|
59
|
+
- **Radix UI** (used by shadcn/ui)
|
|
60
|
+
- **React Aria** (Adobe)
|
|
61
|
+
- **Headless UI** (Tailwind Labs)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Forms
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<label htmlFor="email">Email</label>
|
|
69
|
+
<input
|
|
70
|
+
id="email"
|
|
71
|
+
type="email"
|
|
72
|
+
name="email"
|
|
73
|
+
required
|
|
74
|
+
aria-invalid={!!error}
|
|
75
|
+
aria-describedby={error ? 'email-error' : undefined}
|
|
76
|
+
autoComplete="email"
|
|
77
|
+
/>
|
|
78
|
+
{error && <p id="email-error" role="alert">{error}</p>}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Rules:
|
|
82
|
+
- Every input has a label (visible or `aria-label` for icon-only).
|
|
83
|
+
- `autoComplete` set for personal info fields (helps password managers + screen readers + 3.3.7).
|
|
84
|
+
- Error messages: `role="alert"` for live announcement OR `aria-describedby`.
|
|
85
|
+
- Don't disable submit while form is invalid; let user submit and show errors (better UX + a11y).
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Focus Management
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// Modal opens → move focus into the modal; closes → restore to trigger
|
|
93
|
+
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
94
|
+
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (isOpen) dialogRef.current?.focus();
|
|
98
|
+
else triggerRef.current?.focus();
|
|
99
|
+
}, [isOpen]);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Use Radix / React Aria `Dialog` — they handle focus trap, escape, and `aria-modal` correctly.
|
|
103
|
+
|
|
104
|
+
### Skip links
|
|
105
|
+
```tsx
|
|
106
|
+
<a href="#main" className="sr-only focus:not-sr-only">Skip to content</a>
|
|
107
|
+
<main id="main" tabIndex={-1}>...</main>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Color & Contrast
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
/* Background and foreground must hit 4.5:1 */
|
|
116
|
+
.btn-primary {
|
|
117
|
+
background: #2563eb; /* blue-600 */
|
|
118
|
+
color: #ffffff; /* 8.59:1 — passes AAA */
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Tools:
|
|
123
|
+
- Browser devtools have built-in contrast checkers
|
|
124
|
+
- `@axe-core/react` shows warnings inline during dev
|
|
125
|
+
- VS Code: a11y-themes / Color Picker extensions
|
|
126
|
+
|
|
127
|
+
**Never** use color alone to convey meaning (e.g. red border = error). Pair with icon + text.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Keyboard Testing — Manual
|
|
132
|
+
|
|
133
|
+
For every UI:
|
|
134
|
+
1. Tab through — every interactive thing is reachable
|
|
135
|
+
2. Tab order is logical (visual order matches DOM order)
|
|
136
|
+
3. Enter activates buttons/links; Space activates buttons (and toggles checkboxes)
|
|
137
|
+
4. Esc closes modals/menus
|
|
138
|
+
5. Arrow keys in radio groups, tabs, menus, listboxes
|
|
139
|
+
6. Focus is visible at every step
|
|
140
|
+
7. Focus is never lost or trapped (other than intentional in modals)
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Automated Testing — axe-core + Playwright
|
|
145
|
+
|
|
146
|
+
### Install
|
|
147
|
+
```bash
|
|
148
|
+
bun add -d @axe-core/playwright
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Per-page check
|
|
152
|
+
```ts
|
|
153
|
+
// tests/e2e/a11y.spec.ts
|
|
154
|
+
import { test, expect } from '@playwright/test';
|
|
155
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
156
|
+
|
|
157
|
+
test.describe('a11y', () => {
|
|
158
|
+
for (const path of ['/', '/login', '/dashboard', '/settings']) {
|
|
159
|
+
test(`no axe violations on ${path}`, async ({ page }) => {
|
|
160
|
+
await page.goto(path);
|
|
161
|
+
const results = await new AxeBuilder({ page })
|
|
162
|
+
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
|
|
163
|
+
.analyze();
|
|
164
|
+
expect(results.violations, JSON.stringify(results.violations, null, 2)).toEqual([]);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Per-component check
|
|
171
|
+
```ts
|
|
172
|
+
test('Modal is accessible', async ({ page }) => {
|
|
173
|
+
await page.goto('/components/modal-demo');
|
|
174
|
+
await page.click('text=Open');
|
|
175
|
+
const results = await new AxeBuilder({ page })
|
|
176
|
+
.include('[role="dialog"]')
|
|
177
|
+
.withTags(['wcag2aa', 'wcag22aa'])
|
|
178
|
+
.analyze();
|
|
179
|
+
expect(results.violations).toEqual([]);
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Dev-time inline checks (React)
|
|
184
|
+
```ts
|
|
185
|
+
// app/providers.tsx — only in dev
|
|
186
|
+
if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined') {
|
|
187
|
+
import('@axe-core/react').then(axe => {
|
|
188
|
+
import('react-dom').then(ReactDOM => axe.default(React, ReactDOM, 1000));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Lighthouse CI (Pages)
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
# .github/workflows/ci.yml — append to e2e job
|
|
199
|
+
- name: Lighthouse CI
|
|
200
|
+
uses: treosh/lighthouse-ci-action@v11
|
|
201
|
+
with:
|
|
202
|
+
urls: |
|
|
203
|
+
http://localhost:3000/
|
|
204
|
+
http://localhost:3000/dashboard
|
|
205
|
+
configPath: ./.lighthouserc.json
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`.lighthouserc.json`:
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"ci": {
|
|
212
|
+
"assert": {
|
|
213
|
+
"assertions": {
|
|
214
|
+
"categories:accessibility": ["error", { "minScore": 0.95 }]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Mobile / Touch (WCAG 2.2 §2.5.8)
|
|
224
|
+
|
|
225
|
+
```css
|
|
226
|
+
/* Minimum touch target — 24×24 CSS px (44×44 recommended for mobile) */
|
|
227
|
+
button, [role="button"], a {
|
|
228
|
+
min-width: 44px;
|
|
229
|
+
min-height: 44px;
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
For dense UIs, the spec allows smaller if "equivalent functionality" exists elsewhere — but it's safer to just hit 44px.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Screen Reader Testing
|
|
238
|
+
|
|
239
|
+
You can't ship a11y without ever using a screen reader. Quick smoke:
|
|
240
|
+
- macOS: VoiceOver (`Cmd+F5`)
|
|
241
|
+
- Windows: NVDA (free) or Narrator
|
|
242
|
+
- Mobile: VoiceOver (iOS) / TalkBack (Android)
|
|
243
|
+
|
|
244
|
+
Test the golden path: load page → tab through → fill form → submit. If the screen reader announces what's happening, you're 80% there.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Pre-Commit Checklist
|
|
249
|
+
|
|
250
|
+
- [ ] Semantic HTML (no `div onClick` for buttons/links)
|
|
251
|
+
- [ ] Every form input has a label
|
|
252
|
+
- [ ] Every image has `alt` (or `alt=""` if decorative)
|
|
253
|
+
- [ ] Heading levels are sequential (no skipping)
|
|
254
|
+
- [ ] Color contrast ≥ 4.5:1 for body text, 3:1 for large/UI
|
|
255
|
+
- [ ] Focus visible on all interactive elements
|
|
256
|
+
- [ ] Keyboard test passes (Tab/Enter/Esc/Arrows)
|
|
257
|
+
- [ ] axe-core test added for new pages/components
|
|
258
|
+
- [ ] Touch targets ≥ 24×24 px (44 recommended)
|
|
259
|
+
- [ ] Drag operations have non-drag alternative (WCAG 2.2 §2.5.7)
|
|
260
|
+
- [ ] No `outline: none` without replacement
|
|
261
|
+
- [ ] No `aria-*` on the wrong element type
|
|
262
|
+
|
|
263
|
+
## FORBIDDEN
|
|
264
|
+
|
|
265
|
+
| Pattern | Why |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `<div onClick>` for an action | Not keyboard-reachable, no role |
|
|
268
|
+
| `<a>` without `href` | Not keyboard-reachable, no link semantics |
|
|
269
|
+
| `outline: none` with no `:focus-visible` style | Invisible focus = unusable |
|
|
270
|
+
| `aria-label` on a `<div>` that should be a `<button>` | Cargo-cult ARIA — fix the element |
|
|
271
|
+
| Color-only error indication | Colorblind users can't see |
|
|
272
|
+
| `role="button"` on a `<button>` | Redundant; native role wins |
|
|
273
|
+
| Placeholder as label | Disappears on input, fails contrast |
|
|
274
|
+
| Auto-playing video/audio with sound | WCAG 1.4.2 |
|
|
275
|
+
| Drag-only interactions | WCAG 2.2 §2.5.7 — provide buttons |
|
|
276
|
+
| Modal that traps tab AND has unreachable close button | Keyboard dead-end |
|
|
277
|
+
|
|
278
|
+
## See Also
|
|
279
|
+
|
|
280
|
+
- `ui-ux-audit` — broader UX audit including responsive
|
|
281
|
+
- `playwright-automation` — where the axe tests live
|
|
282
|
+
- `react-patterns` — Radix / React Aria preferred over custom widgets
|
|
283
|
+
- WCAG 2.2 quick-reference: https://www.w3.org/WAI/WCAG22/quickref/
|
|
284
|
+
- WAI-ARIA Authoring Practices: https://www.w3.org/WAI/ARIA/apg/
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ci-pipelines
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: GitHub Actions pipelines for typecheck, lint, test, security scan, build, and release. Stack-aware templates live in stacks/<stack>/workflows/.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# CI Pipelines
|
|
8
|
+
|
|
9
|
+
**ALWAYS invoke when setting up CI, modifying `.github/workflows/`, or wiring quality gates.**
|
|
10
|
+
|
|
11
|
+
> CI is the only enforcement that survives human pressure. Anything not in CI will eventually be skipped.
|
|
12
|
+
|
|
13
|
+
## Pipeline Stages (Required Order)
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. checkout + setup
|
|
17
|
+
2. install deps (cache locked)
|
|
18
|
+
3. typecheck / static analysis — fail fast, cheapest
|
|
19
|
+
4. lint
|
|
20
|
+
5. unit tests
|
|
21
|
+
6. integration tests (with services)
|
|
22
|
+
7. security audit (npm/pip/composer audit + gitleaks)
|
|
23
|
+
8. build
|
|
24
|
+
9. e2e (only on main / PRs to main)
|
|
25
|
+
10. publish artifacts (only on tag)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Order matters**: cheaper gates first. A typo found by typecheck in 30s saves 10 minutes of e2e runtime.
|
|
29
|
+
|
|
30
|
+
## Required Workflows per Repo
|
|
31
|
+
|
|
32
|
+
| File | Triggers | Purpose |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `ci.yml` | `pull_request`, `push` to main | Main pipeline |
|
|
35
|
+
| `security.yml` | weekly cron + push | Audits, gitleaks, CodeQL |
|
|
36
|
+
| `release.yml` | `release: published` | Publish to npm / PyPI / Docker / etc. |
|
|
37
|
+
|
|
38
|
+
## Universal Best Practices
|
|
39
|
+
|
|
40
|
+
### Concurrency (cancel stale runs)
|
|
41
|
+
```yaml
|
|
42
|
+
concurrency:
|
|
43
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
44
|
+
cancel-in-progress: true
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Permissions (least privilege)
|
|
48
|
+
```yaml
|
|
49
|
+
permissions:
|
|
50
|
+
contents: read # default; bump only what each job needs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Bump per job:
|
|
54
|
+
```yaml
|
|
55
|
+
jobs:
|
|
56
|
+
release:
|
|
57
|
+
permissions:
|
|
58
|
+
contents: write # to create tags
|
|
59
|
+
id-token: write # to use OIDC for npm provenance
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Pin actions by SHA in production
|
|
63
|
+
```yaml
|
|
64
|
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`Dependabot` will keep them updated. Tags can be re-pointed; SHAs cannot.
|
|
68
|
+
|
|
69
|
+
### Secrets
|
|
70
|
+
|
|
71
|
+
- Never `echo "$SECRET"` — masking is best-effort.
|
|
72
|
+
- Pass to runtime via env, not CLI args (CLI shows in logs on some setups).
|
|
73
|
+
- Use **OIDC** for cloud deploys (AWS / GCP / Vercel) — no long-lived keys in repo secrets.
|
|
74
|
+
|
|
75
|
+
### Caching
|
|
76
|
+
|
|
77
|
+
| Stack | Cache key |
|
|
78
|
+
|---|---|
|
|
79
|
+
| Node.js | `package-lock.json` / `bun.lock` / `pnpm-lock.yaml` |
|
|
80
|
+
| Python | `requirements*.txt` / `uv.lock` / `poetry.lock` |
|
|
81
|
+
| PHP | `composer.lock` |
|
|
82
|
+
|
|
83
|
+
`actions/setup-node` / `setup-python` provide `cache:` parameter — use it.
|
|
84
|
+
|
|
85
|
+
### Matrix builds for multi-version coverage
|
|
86
|
+
```yaml
|
|
87
|
+
strategy:
|
|
88
|
+
matrix:
|
|
89
|
+
node: [20, 22]
|
|
90
|
+
fail-fast: false # see all failures, don't bail on first
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Branch Protection (mandatory)
|
|
94
|
+
|
|
95
|
+
Configure on GitHub: Settings → Branches → main:
|
|
96
|
+
- Require PRs (no direct push to main)
|
|
97
|
+
- Require status checks: `ci / typecheck`, `ci / test`, `ci / security`
|
|
98
|
+
- Require linear history (no merge commits, only squash/rebase)
|
|
99
|
+
- Require signed commits (optional but recommended)
|
|
100
|
+
- Require up-to-date branch before merge
|
|
101
|
+
|
|
102
|
+
## Templates Per Stack
|
|
103
|
+
|
|
104
|
+
Templates ship in `stacks/<stack>/workflows/`. Run `npx start-vibing` (or copy manually) to install them into the project's `.github/workflows/`.
|
|
105
|
+
|
|
106
|
+
| Stack | Template files |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `nodejs` | `ci.yml`, `security.yml` |
|
|
109
|
+
| `python` | `ci.yml`, `security.yml` |
|
|
110
|
+
| `php` | `ci.yml`, `security.yml` |
|
|
111
|
+
|
|
112
|
+
## Release / Publish
|
|
113
|
+
|
|
114
|
+
For npm packages:
|
|
115
|
+
```yaml
|
|
116
|
+
# .github/workflows/release.yml
|
|
117
|
+
on:
|
|
118
|
+
release:
|
|
119
|
+
types: [published]
|
|
120
|
+
permissions:
|
|
121
|
+
contents: read
|
|
122
|
+
id-token: write # OIDC for provenance
|
|
123
|
+
jobs:
|
|
124
|
+
publish:
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
steps:
|
|
127
|
+
- uses: actions/checkout@v4
|
|
128
|
+
- uses: actions/setup-node@v4
|
|
129
|
+
with:
|
|
130
|
+
node-version: 20
|
|
131
|
+
registry-url: https://registry.npmjs.org
|
|
132
|
+
- run: npm ci
|
|
133
|
+
- run: npm run build
|
|
134
|
+
- run: npm publish --access public --provenance
|
|
135
|
+
env:
|
|
136
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`--provenance` is the 2024+ default — gives users SLSA-attested supply chain.
|
|
140
|
+
|
|
141
|
+
## Pre-Commit Checklist for CI Changes
|
|
142
|
+
|
|
143
|
+
- [ ] `concurrency` group set
|
|
144
|
+
- [ ] `permissions: contents: read` at top, bump per job
|
|
145
|
+
- [ ] Secrets via `${{ secrets.X }}`, never inline
|
|
146
|
+
- [ ] Actions pinned by SHA (or at least major+minor for non-critical)
|
|
147
|
+
- [ ] Quality gates ordered cheapest → most expensive
|
|
148
|
+
- [ ] Caching configured for the package manager
|
|
149
|
+
- [ ] Branch protection rules applied to main on GitHub
|
|
150
|
+
|
|
151
|
+
## FORBIDDEN
|
|
152
|
+
|
|
153
|
+
| Pattern | Reason |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `if: always()` on the final reporter that swallows failures | CI green when tests fail |
|
|
156
|
+
| `continue-on-error: true` outside of allowed-fail matrix | Hides regressions |
|
|
157
|
+
| `--no-verify` in CI git operations | Bypasses other guardrails |
|
|
158
|
+
| Plain text secrets in env files committed to repo | See `secrets-management` |
|
|
159
|
+
| Workflow with `permissions: write-all` | Overly broad, supply chain risk |
|
|
160
|
+
| Long-lived cloud creds in repo secrets | Use OIDC instead |
|
|
161
|
+
|
|
162
|
+
## See Also
|
|
163
|
+
|
|
164
|
+
- `secrets-management` — repo secrets & OIDC patterns
|
|
165
|
+
- `quality-gate` — local quality gates that mirror CI
|
|
166
|
+
- `git-workflow` — branch + commit conventions CI relies on
|