@taraxvoid/voidflow 0.14.0 → 0.15.1
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 +211 -362
- package/biome/base.json +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
# voidflow
|
|
2
2
|
|
|
3
|
-
Shared CI and
|
|
3
|
+
Shared CI, deploy and repo config for Astro sites on Cloudflare Workers or
|
|
4
|
+
Netlify, and for bun tools and libraries. Defaults assume bun, Astro and
|
|
5
|
+
Playwright; pnpm is supported.
|
|
6
|
+
|
|
7
|
+
| Piece | What it does |
|
|
8
|
+
|---|---|
|
|
9
|
+
| [`site-ci.yml`](#site-ci) | Lint, typecheck, build, unit, license, Playwright, axe and Unlighthouse for sites |
|
|
10
|
+
| [`tool-ci.yml`](#tool-ci) | gitleaks, lint, typecheck, build, test, workflow audit for bun tools |
|
|
11
|
+
| [`resolve-env.yml`, deploy actions](#deploys) | Map branch to environment, deploy to Cloudflare or Netlify |
|
|
12
|
+
| [`unlighthouse-runner`, `check-licenses`](#npm-package) | Performance budgets and copyleft license check (npm bins) |
|
|
13
|
+
| [`@taraxvoid/voidflow/playwright`](#playwright-config) | Playwright config for `astro preview` |
|
|
14
|
+
| [`@taraxvoid/voidflow/biome`](#biome) | Shared Biome config |
|
|
15
|
+
| [`github>taraxvoid/voidflow`](#renovate) | Shared Renovate preset |
|
|
16
|
+
| [`lefthook/base.yml`](#git-hooks) | Shared git hooks |
|
|
17
|
+
|
|
18
|
+
## Pinning
|
|
19
|
+
|
|
20
|
+
Pin to a release commit SHA with the version in a comment, like any
|
|
21
|
+
third-party action. Renovate and Dependabot can bump it.
|
|
4
22
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- **In production.** It runs the pipelines for the sites listed under [Used by](#used-by), including the marketing site for [RavenFlight Industries, LLC](https://rvnflt.com/github).
|
|
8
|
-
|
|
9
|
-
It is semi-opinionated: the defaults match how my own sites are built (bun, Astro, Playwright).
|
|
10
|
-
|
|
11
|
-
## Used by
|
|
12
|
-
|
|
13
|
-
- [taraxvoid.net](https://taraxvoid.net), my portfolio
|
|
14
|
-
- [Queer Omaha](https://queeromaha.net), [Synth Omaha](https://synthomaha.net) and [Soundry](https://soundryomaha.org), free community sites for Omaha's queer and music scenes
|
|
15
|
-
- [ravenflight.io](https://ravenflight.io), RavenFlight's marketing site
|
|
16
|
-
|
|
17
|
-
### Show you use it
|
|
18
|
-
|
|
19
|
-
Optional. Paste this into your site's README. It follows light and dark mode.
|
|
20
|
-
|
|
21
|
-
```html
|
|
22
|
-
<a href="https://github.com/taraxvoid/voidflow">
|
|
23
|
-
<picture>
|
|
24
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/CI-voidflow-8b8fd9?labelColor=2b2d3a&style=flat">
|
|
25
|
-
<img alt="CI: voidflow" src="https://img.shields.io/badge/CI-voidflow-5a5fb8?labelColor=3a3d4d&style=flat">
|
|
26
|
-
</picture>
|
|
27
|
-
</a>
|
|
23
|
+
```yaml
|
|
24
|
+
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@<sha> # vX.Y.Z
|
|
28
25
|
```
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
`@v0` follows the newest stable 0.x release if you'd rather float. To try
|
|
28
|
+
unreleased changes, pin the SHA of a `vX.Y.Z-next.N` tag. Don't use `@main` or
|
|
29
|
+
`@next`. Release channels are described in [`RELEASING.md`](RELEASING.md).
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
## Site CI
|
|
33
32
|
|
|
34
33
|
```yaml
|
|
34
|
+
# .github/workflows/ci.yml
|
|
35
35
|
name: CI
|
|
36
36
|
on:
|
|
37
37
|
pull_request:
|
|
@@ -41,26 +41,50 @@ on:
|
|
|
41
41
|
|
|
42
42
|
jobs:
|
|
43
43
|
validate:
|
|
44
|
-
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml
|
|
44
|
+
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@<sha> # vX.Y.Z
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
It runs whichever of these `package.json` scripts the site defines (missing
|
|
48
|
+
ones are skipped with a notice):
|
|
49
|
+
|
|
50
|
+
| Script | When |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `lint`, `check`, `build`, `test:unit` | always |
|
|
53
|
+
| `check:licenses` | lockfile or `scripts/` changed |
|
|
54
|
+
| `test:e2e:full` | PRs into `main` |
|
|
55
|
+
| `test:e2e:all`, `test:e2e:a11y` | PRs into `next` and `live` |
|
|
56
|
+
| `test:e2e:lighthouse` | PRs into `live` |
|
|
57
|
+
|
|
58
|
+
Inputs:
|
|
59
|
+
|
|
60
|
+
| Input | Default | |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `runner` | `ubuntu-latest` | Runner label |
|
|
63
|
+
| `package-manager` | `bun` | `bun`, `pnpm` or `aube` (see below) |
|
|
64
|
+
| `single-environment` | `false` | See below |
|
|
65
|
+
| `build` | `true` | Run `build` as its own step. Pass `false` when the e2e scripts already build |
|
|
66
|
+
| `build-env` | `{}` | JSON object of build env vars. Visible in logs, so no secrets |
|
|
67
|
+
| `code-paths` | everything except Markdown, `docs/`, `LICENSE*`, issue templates | JSON array of globs (`!` excludes) that count as code |
|
|
68
|
+
| `e2e-scripts` | `[]` | JSON array of extra e2e scripts (see below) |
|
|
69
|
+
| `e2e-extra-paths` | `["wrangler.jsonc", "site.vars.json"]` | JSON array of globs that also trigger e2e |
|
|
70
|
+
| `socket-scanner` | `false` | Run the bun install security scanner in CI (see below) |
|
|
71
|
+
| `playwright-install-with-deps` | `true` | Install Playwright system deps |
|
|
72
|
+
|
|
73
|
+
Pull requests outside `code-paths` skip install, lint and tests but still pass
|
|
74
|
+
`validate / Validate`. Pushes and manual runs always run everything.
|
|
75
|
+
|
|
76
|
+
### pnpm and aube
|
|
77
|
+
|
|
78
|
+
pnpm takes its version from `packageManager` and Node from `volta` or
|
|
79
|
+
`engines`. aube needs the site's `mise.toml` to pin it (`aube = "<version>"`),
|
|
80
|
+
then follows `packageManager` and takes Node from `devEngines.runtime`. Both
|
|
81
|
+
use `pnpm-lock.yaml`.
|
|
58
82
|
|
|
59
83
|
### Single-environment sites
|
|
60
84
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
For sites where `main` deploys to prod and PRs get previews (no `next`/`live`),
|
|
86
|
+
pass `single-environment: true` and add a `push` trigger so direct pushes to
|
|
87
|
+
`main` are validated:
|
|
64
88
|
|
|
65
89
|
```yaml
|
|
66
90
|
on:
|
|
@@ -73,90 +97,53 @@ on:
|
|
|
73
97
|
|
|
74
98
|
jobs:
|
|
75
99
|
validate:
|
|
76
|
-
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@<sha> #
|
|
100
|
+
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@<sha> # vX.Y.Z
|
|
77
101
|
with:
|
|
78
102
|
single-environment: true
|
|
79
103
|
```
|
|
80
104
|
|
|
81
|
-
`main` then
|
|
82
|
-
`test:e2e:lighthouse`)
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
`main` then runs the production tier (`test:e2e:all`, `test:e2e:a11y`,
|
|
106
|
+
`test:e2e:lighthouse`).
|
|
107
|
+
|
|
108
|
+
### Extra e2e scripts
|
|
85
109
|
|
|
86
|
-
|
|
110
|
+
Scripts listed in `e2e-scripts` run in order after the built-in tiers whenever
|
|
111
|
+
e2e-relevant files changed. Unlike the built-in tiers, a missing one fails the
|
|
112
|
+
job.
|
|
87
113
|
|
|
88
114
|
```yaml
|
|
89
|
-
jobs:
|
|
90
|
-
validate:
|
|
91
|
-
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@<sha> # <version>
|
|
92
115
|
with:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
build-env: ${{ format('{{"PUBLIC_SITE_URL":"https://example.com","GIT_SHA":"{0}"}}', github.sha) }}
|
|
116
|
+
e2e-scripts: '["test:e2e", "test:e2e:gtm", "test:e2e:editor"]'
|
|
117
|
+
build: false
|
|
96
118
|
```
|
|
97
119
|
|
|
98
|
-
|
|
99
|
-
`engines` in `package.json`. The lockfile (`bun.lock` or `pnpm-lock.yaml`)
|
|
100
|
-
follows the package manager. `build-env` is a JSON object, visible in logs, so
|
|
101
|
-
no secrets.
|
|
120
|
+
### Socket install scanner
|
|
102
121
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```toml
|
|
109
|
-
[install.security]
|
|
110
|
-
scanner = "@socketsecurity/bun-security-scanner"
|
|
111
|
-
```
|
|
122
|
+
If `bunfig.toml` sets `install.security.scanner`, CI installs with that line
|
|
123
|
+
removed (everything else still applies) and leaves a notice, so a flaky
|
|
124
|
+
third-party API can't fail the build. Local installs still run it. Pass
|
|
125
|
+
`socket-scanner: true` to run it in CI too.
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
and rerunning the same commit could give a different result. `site-ci.yml`
|
|
116
|
-
installs with a copy of `bunfig.toml` minus the scanner line (every other
|
|
117
|
-
setting, e.g. `minimumReleaseAge`, still applies) and leaves a notice in the
|
|
118
|
-
run. Local `bun install` reads `bunfig.toml` as is, so the scanner still runs
|
|
119
|
-
on your machine.
|
|
127
|
+
Write it as a plain `scanner = "..."` line or dotted `security.scanner`. An
|
|
128
|
+
inline table can't be removed cleanly, so the install fails instead.
|
|
120
129
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
the composite action:
|
|
130
|
+
Workflows that install outside `site-ci.yml` get the same behavior from the
|
|
131
|
+
composite action:
|
|
124
132
|
|
|
125
133
|
```yaml
|
|
126
134
|
- uses: oven-sh/setup-bun@<sha> # <version>
|
|
127
|
-
- uses: taraxvoid/voidflow/actions/bun-install@<sha> #
|
|
135
|
+
- uses: taraxvoid/voidflow/actions/bun-install@<sha> # vX.Y.Z
|
|
128
136
|
# with: { socket-scanner: 'true' }
|
|
129
137
|
```
|
|
130
138
|
|
|
131
|
-
|
|
132
|
-
with only `install.security.scanner` removed. Set the scanner as a plain
|
|
133
|
-
`scanner = "..."` line (or the dotted `security.scanner` form); if it can't be
|
|
134
|
-
removed cleanly, e.g. an inline table, the install fails rather than run or
|
|
135
|
-
skip the scanner silently. pnpm sites are unaffected.
|
|
136
|
-
|
|
137
|
-
## Workflow Types
|
|
138
|
-
|
|
139
|
-
### Validation / Checks
|
|
140
|
-
|
|
141
|
-
Runs lint, typecheck, check for GPL licenses, e2e with Playwright.
|
|
139
|
+
## Tool CI
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
### Tool validation
|
|
150
|
-
|
|
151
|
-
`tool-ci.yml` is the validation job for bun tools and libraries (CLIs, TUIs,
|
|
152
|
-
packages): gitleaks over the full history, install (with the same Socket
|
|
153
|
-
scanner handling as `site-ci.yml`), tools from the repo's `mise.toml` when it
|
|
154
|
-
has one, then whichever of `lint`, `check`, `build` and `test` `package.json`
|
|
155
|
-
defines, and `check:licenses` when dependencies or `scripts/` changed. When
|
|
156
|
-
workflows change it also runs jactionlint (same profile handling as
|
|
157
|
-
`site-ci.yml`) and
|
|
158
|
-
[zizmor](https://github.com/zizmorcore/zizmor). The bun version comes from
|
|
159
|
-
`.bun-version` when present.
|
|
141
|
+
For bun CLIs, TUIs and libraries. Runs gitleaks over full history, installs
|
|
142
|
+
(same scanner handling), installs tools from `mise.toml` if present, then
|
|
143
|
+
whichever of `lint`, `check`, `build` and `test` exist, plus `check:licenses`
|
|
144
|
+
when dependencies or `scripts/` change. When workflows change it also runs
|
|
145
|
+
jactionlint and [zizmor](https://github.com/zizmorcore/zizmor). Bun version
|
|
146
|
+
comes from `.bun-version` if present. Inputs: `runner`, `socket-scanner`.
|
|
160
147
|
|
|
161
148
|
```yaml
|
|
162
149
|
name: CI
|
|
@@ -173,74 +160,89 @@ permissions:
|
|
|
173
160
|
|
|
174
161
|
jobs:
|
|
175
162
|
validate:
|
|
176
|
-
uses: taraxvoid/voidflow/.github/workflows/tool-ci.yml@<
|
|
163
|
+
uses: taraxvoid/voidflow/.github/workflows/tool-ci.yml@<sha> # vX.Y.Z
|
|
177
164
|
```
|
|
178
165
|
|
|
179
|
-
The
|
|
180
|
-
[`rulesets/index.ts`](#branch-rulesets-as-code) requires.
|
|
166
|
+
The required status check is `validate / Validate`.
|
|
181
167
|
|
|
182
|
-
|
|
168
|
+
## Deploys
|
|
183
169
|
|
|
184
|
-
`
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Chromium. It exits non-zero if a category budget in the site's config fails.
|
|
170
|
+
`resolve-env.yml` maps the branch to an environment: `main` to `dev`, `next`
|
|
171
|
+
to `staging`, `live` to `prod`. With `single-environment: true`, `main` maps to
|
|
172
|
+
`prod`, pull requests to `preview`, and any other ref fails. It's a separate
|
|
173
|
+
job so the deploy job's `name:` can show the environment.
|
|
189
174
|
|
|
190
|
-
|
|
191
|
-
|
|
175
|
+
```yaml
|
|
176
|
+
jobs:
|
|
177
|
+
resolve-env:
|
|
178
|
+
needs: validate
|
|
179
|
+
if: github.event_name == 'push'
|
|
180
|
+
uses: taraxvoid/voidflow/.github/workflows/resolve-env.yml@<sha> # vX.Y.Z
|
|
192
181
|
|
|
193
|
-
|
|
194
|
-
|
|
182
|
+
deploy:
|
|
183
|
+
name: Deploy [${{ needs.resolve-env.outputs.env }}]
|
|
184
|
+
needs: resolve-env
|
|
185
|
+
if: github.event_name == 'push'
|
|
186
|
+
runs-on: ubuntu-latest
|
|
187
|
+
environment: ${{ needs.resolve-env.outputs.env }}
|
|
188
|
+
steps:
|
|
189
|
+
- uses: actions/checkout@<sha> # <version>
|
|
190
|
+
- uses: taraxvoid/voidflow/actions/cloudflare-deploy@<sha> # vX.Y.Z
|
|
191
|
+
with:
|
|
192
|
+
env: ${{ needs.resolve-env.outputs.env }}
|
|
193
|
+
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
195
194
|
```
|
|
196
195
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
- `actions/cloudflare-deploy`: `wrangler deploy` for the environment, with an
|
|
197
|
+
optional D1 migration first (`migrate-d1`). Other inputs:
|
|
198
|
+
`cloudflare-account-id`, `working-directory`, `command`.
|
|
199
|
+
- `actions/netlify-deploy`: `prod` is a production deploy, `dev`/`staging` are
|
|
200
|
+
alias deploys. Inputs: `env`, `publish-dir`, `netlify-auth-token`,
|
|
201
|
+
`netlify-site-id`, `working-directory`, `github-token`. Turn off Netlify's
|
|
202
|
+
own GitHub auto-deploy for the site, or the two will double-deploy.
|
|
203
|
+
- `actions/branch-env-map`: the mapping step on its own, for use inside a job.
|
|
202
204
|
|
|
203
|
-
|
|
205
|
+
## npm package
|
|
204
206
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
SSPL and similar copyleft licenses (LGPL is allowed). Voidflow pins the
|
|
209
|
-
checker to an exact version as its own dependency, so sites don't need to
|
|
210
|
-
install it and every site runs the same version (sites that added
|
|
211
|
-
`license-checker-rseidelsohn` as a devDependency for 0.12.2 can drop it).
|
|
207
|
+
```sh
|
|
208
|
+
bun add -d @taraxvoid/voidflow # or: pnpm add -D @taraxvoid/voidflow
|
|
209
|
+
```
|
|
212
210
|
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
Also published unscoped as `voidflow` with identical contents.
|
|
212
|
+
|
|
213
|
+
Call the bins from `package.json` scripts rather than `bunx --package ...`, so
|
|
214
|
+
the version stays in the lockfile and the site's `bunfig.toml` applies. Both
|
|
215
|
+
bins need `bun` on `PATH`, even in npm or pnpm projects.
|
|
216
|
+
|
|
217
|
+
### Unlighthouse budgets
|
|
215
218
|
|
|
216
219
|
```json
|
|
217
|
-
"
|
|
220
|
+
"test:e2e:lighthouse": "bun run build && unlighthouse-runner --dir dist"
|
|
218
221
|
```
|
|
219
222
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
the global `~/.bunfig.toml` rather than the site's, so a global
|
|
223
|
-
`minimumReleaseAge` that doesn't exclude `@taraxvoid/voidflow` blocks it
|
|
224
|
-
locally (including in the pre-commit hook) for 3 days after every release.
|
|
225
|
-
The devDependency's bins are installed under the site's own `bunfig.toml`.
|
|
223
|
+
Serves the built directory and runs [Unlighthouse](https://unlighthouse.dev/)
|
|
224
|
+
with Playwright's Chromium. Fails if a category budget fails.
|
|
226
225
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
- `--dir`: default `dist`; use `dist/client` for the Cloudflare adapter
|
|
227
|
+
- `--config`: default `unlighthouse.config.ts`; start from
|
|
228
|
+
[the example](scripts/unlighthouse/unlighthouse.config.example.ts)
|
|
229
|
+
- `--version`: Unlighthouse version
|
|
231
230
|
|
|
232
|
-
|
|
231
|
+
Add `.unlighthouse/` to `.gitignore`.
|
|
233
232
|
|
|
234
|
-
|
|
235
|
-
`astro preview`. It picks a free port outside CI, sets
|
|
236
|
-
`ASTRO_PREVIEW_BACKGROUND=false` (Astro backgrounds `preview` in agent
|
|
237
|
-
environments, which makes Playwright think the server exited), and drops the
|
|
238
|
-
`webServer` when `PLAYWRIGHT_BASE_URL` is set.
|
|
233
|
+
### License check
|
|
239
234
|
|
|
240
|
-
```
|
|
241
|
-
|
|
235
|
+
```json
|
|
236
|
+
"check:licenses": "check-licenses"
|
|
242
237
|
```
|
|
243
238
|
|
|
239
|
+
Fails on GPL, AGPL, SSPL and similar copyleft licenses. LGPL is allowed.
|
|
240
|
+
|
|
241
|
+
### Playwright config
|
|
242
|
+
|
|
243
|
+
For Astro sites that run e2e against `astro preview`. Requires
|
|
244
|
+
`@playwright/test`. Runs on Node 22+ without Bun.
|
|
245
|
+
|
|
244
246
|
```js
|
|
245
247
|
// playwright.config.js
|
|
246
248
|
import { definePreviewConfig } from '@taraxvoid/voidflow/playwright'
|
|
@@ -248,23 +250,14 @@ import { definePreviewConfig } from '@taraxvoid/voidflow/playwright'
|
|
|
248
250
|
export default definePreviewConfig({ ciPort: 4141 })
|
|
249
251
|
```
|
|
250
252
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
`
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
Options: `ciPort` (required in CI), `runner` (default `bun`, e.g. `pnpm`),
|
|
257
|
-
`timeout` (default 30000), `projects` (default Pixel 7 and Desktop Chrome),
|
|
258
|
-
`testDir` (default `./test/e2e`). Requires `@playwright/test` in the site.
|
|
253
|
+
Picks a free port locally and skips `webServer` when `PLAYWRIGHT_BASE_URL` is
|
|
254
|
+
set. Options: `ciPort` (required in CI), `runner` (default `bun`), `timeout`
|
|
255
|
+
(default 30000), `projects` (default Pixel 7 and Desktop Chrome), `testDir`
|
|
256
|
+
(default `./test/e2e`).
|
|
259
257
|
|
|
260
|
-
|
|
258
|
+
### Biome
|
|
261
259
|
|
|
262
|
-
|
|
263
|
-
indent, LF, width 80, single quotes, semicolons only where needed, trailing
|
|
264
|
-
commas, the `recommended` rule preset, organized imports, CSS lint off, and
|
|
265
|
-
unused-import/variable rules off in `.astro` files (Biome can't see template
|
|
266
|
-
usage). With `@taraxvoid/voidflow` as a devDependency, the site's `biome.json`
|
|
267
|
-
extends it and adds only what is specific to the site:
|
|
260
|
+
Needs Biome 2. In the site's `biome.json`:
|
|
268
261
|
|
|
269
262
|
```json
|
|
270
263
|
{
|
|
@@ -276,16 +269,14 @@ extends it and adds only what is specific to the site:
|
|
|
276
269
|
}
|
|
277
270
|
```
|
|
278
271
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
272
|
+
The base sets formatting (4-space indent, width 80, single quotes, semicolons
|
|
273
|
+
as needed), the `recommended` rules, and turns off unused-import/variable rules
|
|
274
|
+
in `.astro` files. HTML formatting uses `whitespaceSensitivity: "strict"`, so
|
|
275
|
+
the formatter never drops a space at the edge of an element's text (such as
|
|
276
|
+
`<a href="/x">docs </a>and`). It has no ignore lists. Lists like `files.includes` and
|
|
277
|
+
`overrides` merge with the base; single values in the site win.
|
|
284
278
|
|
|
285
|
-
##
|
|
286
|
-
|
|
287
|
-
[`default.json`](default.json) is the shared Renovate preset. Sites add a
|
|
288
|
-
`renovate.json`:
|
|
279
|
+
## Renovate
|
|
289
280
|
|
|
290
281
|
```json
|
|
291
282
|
{
|
|
@@ -294,220 +285,78 @@ installed.
|
|
|
294
285
|
}
|
|
295
286
|
```
|
|
296
287
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
script is skipped; `<pm>` is pnpm when the repo has a `pnpm-lock.yaml`, bun
|
|
313
|
-
otherwise):
|
|
314
|
-
|
|
315
|
-
- `pre-commit`: dependency guard, Biome 1.x warning, `format` (formatted files are re-staged),
|
|
316
|
-
`lint:actions`, `check`, `test:unit`, and `check:licenses` / `audit` when the
|
|
317
|
-
lockfile changed.
|
|
318
|
-
- `commit-msg`: advisory Conventional Commits hint (never blocks).
|
|
319
|
-
- `pre-push`: bail if the remote branch is ahead, then `test:push`; skipped when
|
|
320
|
-
the push carries no commit the remote lacks (branch deletes, and tags or
|
|
321
|
-
branches at already-pushed commits).
|
|
322
|
-
|
|
323
|
-
Before `test:unit` and `test:push`, the hooks unset the variables git exports to
|
|
324
|
-
hooks (`GIT_DIR`, `GIT_INDEX_FILE`, ...; the list comes from
|
|
325
|
-
`git rev-parse --local-env-vars`). Without that, a test that runs
|
|
326
|
-
`git -C <tmpdir> commit` operates on, and commits into, the repo being hooked.
|
|
288
|
+
Requires the Renovate GitHub App. Based on `config:best-practices`: 3-day
|
|
289
|
+
release age (except voidflow and security fixes), `chore(deps):` commits,
|
|
290
|
+
grouped updates, Monday mornings. It also bumps the lefthook remote `ref`, and
|
|
291
|
+
`justfile` tool versions annotated with
|
|
292
|
+
`# renovate: datasource=... depName=...` on the line above.
|
|
293
|
+
|
|
294
|
+
## Git hooks
|
|
295
|
+
|
|
296
|
+
Shared lefthook hooks for bun or pnpm repos. Each step runs the repo's own
|
|
297
|
+
`package.json` script and is skipped if the script is missing.
|
|
298
|
+
|
|
299
|
+
- `pre-commit`: dependency guard, `format` (re-staged), `lint:actions`,
|
|
300
|
+
`check`, `test:unit`, and `check:licenses` / `audit` when the lockfile changed
|
|
301
|
+
- `commit-msg`: Conventional Commits hint (never blocks)
|
|
302
|
+
- `pre-push`: stop if the remote is ahead, then `test:push`
|
|
327
303
|
|
|
328
304
|
```yaml
|
|
329
|
-
# lefthook.yml
|
|
305
|
+
# lefthook.yml
|
|
330
306
|
remotes:
|
|
331
307
|
- git_url: https://github.com/taraxvoid/voidflow
|
|
332
|
-
ref:
|
|
308
|
+
ref: vX.Y.Z # a tag, not a SHA
|
|
333
309
|
configs:
|
|
334
310
|
- lefthook/base.yml
|
|
335
|
-
|
|
336
311
|
```
|
|
337
312
|
|
|
338
|
-
|
|
339
|
-
entry to `lefthook/base.yml` when you bump `ref`. The hooks are otherwise the
|
|
340
|
-
same. `lefthook/site.yml` is now a stub whose `pre-commit` and `pre-push` fail
|
|
341
|
-
with that instruction, so a `ref` bump (for example from Renovate) that leaves
|
|
342
|
-
`configs` alone stops commits instead of turning the hooks off silently.
|
|
343
|
-
|
|
344
|
-
Lefthook merges `lefthook.yml`, then remotes, then `lefthook-local.yml`, so a
|
|
345
|
-
repo's own `lefthook.yml` can add commands (for example image compression) but
|
|
346
|
-
cannot override ones defined here. Differences go through `package.json`
|
|
347
|
-
scripts: define `test:unit:precommit` (for example excluding tests that need a
|
|
348
|
-
built `dist/`) and the pre-commit unit step runs it instead of `test:unit`.
|
|
349
|
-
Likewise, define `check:precommit` and the typecheck step runs it instead of
|
|
350
|
-
`check`. CI keeps calling the full scripts, so these only narrow what runs
|
|
351
|
-
locally. Common uses:
|
|
352
|
-
|
|
353
|
-
- bun: `"test:unit:precommit": "bun test --changed"` runs only the test files
|
|
354
|
-
whose imports reach an uncommitted change.
|
|
355
|
-
- pnpm workspace: `pnpm -r --filter '...[HEAD]' run <script>` runs only the
|
|
356
|
-
changed packages and their dependents. Fall back to the full run when a
|
|
357
|
-
root-level file (lockfile, shared tsconfig) changed, since those don't
|
|
358
|
-
belong to any package. If the root `package.json` defines the same script
|
|
359
|
-
as a recursive run, add `--filter '!{.}'` so it doesn't recurse.
|
|
360
|
-
|
|
361
|
-
Hook-level options merge too. Don't set `parallel: true` under `pre-commit:` in
|
|
362
|
-
the repo's `lefthook.yml`: it applies to the merged hook, so every step,
|
|
363
|
-
including the shared ones, starts at once and their `priority` order is ignored
|
|
364
|
-
(tests that share a database or build output can then time out).
|
|
365
|
-
|
|
366
|
-
Install the binaries through mise. A starter `mise.toml` for a repo using
|
|
367
|
-
these hooks and voidflow CI:
|
|
313
|
+
Install the binaries with mise:
|
|
368
314
|
|
|
369
315
|
```toml
|
|
316
|
+
# mise.toml
|
|
370
317
|
[tools]
|
|
371
318
|
lefthook = "2.2.1"
|
|
372
319
|
gitleaks = "8.30.1"
|
|
373
320
|
jactionlint = "2.1.0"
|
|
374
321
|
```
|
|
375
322
|
|
|
376
|
-
|
|
377
|
-
wires the hooks, but make it tolerate a missing binary:
|
|
323
|
+
and wire the hooks on install without failing where lefthook is absent (CI):
|
|
378
324
|
|
|
379
325
|
```json
|
|
380
326
|
"prepare": "lefthook install || echo 'lefthook not installed (run mise install); git hooks skipped'"
|
|
381
327
|
```
|
|
382
328
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
images, and CI never runs git hooks anyway. `ref` cannot be a commit SHA; pin a
|
|
386
|
-
release tag.
|
|
387
|
-
|
|
388
|
-
## Branch rulesets as code
|
|
389
|
-
|
|
390
|
-
Branch protection for the fleet lives in [`rulesets/index.ts`](rulesets/index.ts):
|
|
391
|
-
one canonical `main` / `next` / `live` definition (plus `prerelease` and `maintenance`
|
|
392
|
-
tiers for repos with release channels) and a small per-repo table
|
|
393
|
-
(Netlify or not, check names, CODEOWNERS, extra bypass actors). Sites use
|
|
394
|
-
`site()` and tools use `tool()` (main only, no Netlify, requires
|
|
395
|
-
`validate / Validate` from `tool-ci.yml`). Private repos are left out: on a
|
|
396
|
-
plan without rulesets for private repos, the API returns 403. Hand-edited
|
|
397
|
-
rulesets drift, and drift is how a `next` ruleset ends up requiring a check
|
|
398
|
-
name that no workflow reports any more.
|
|
399
|
-
|
|
400
|
-
```sh
|
|
401
|
-
just rulesets-plan # read-only: diff every repo against the config
|
|
402
|
-
just rulesets-plan --repo soundry
|
|
403
|
-
just rulesets-plan --check # exit 1 on drift, for CI
|
|
404
|
-
just rulesets-apply # create/update rulesets to match
|
|
405
|
-
just rulesets-apply --prune # also delete rulesets a repo lists under `retire`
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
Single-environment sites (a stable `main` that deploys to prod) manage only the
|
|
409
|
-
`main` tier; their old `next`/`live` rulesets are listed under `retire`. voidflow itself
|
|
410
|
-
manages `main`, `prerelease` (`next`) and `maintenance` (`release/*`) and sets
|
|
411
|
-
`linearHistory: false` so `next` can be promoted into `main` with a merge commit.
|
|
412
|
-
Uses the `gh` CLI for auth (needs admin on the repos). Rulesets that are not in
|
|
413
|
-
the config are reported and left alone; only `retire` entries are ever deleted,
|
|
414
|
-
and only with `--prune`. Private repos on a plan without rulesets (GitHub answers
|
|
415
|
-
403, e.g. a free personal account) are skipped and listed at the end rather than
|
|
416
|
-
failing the run, so `--check` stays green and they pick up their rulesets once the
|
|
417
|
-
repo goes public or the plan changes. It is repo tooling, not part of the published
|
|
418
|
-
package.
|
|
419
|
-
|
|
420
|
-
## Deployments
|
|
421
|
-
|
|
422
|
-
Example deployment job added to your workflow above. `resolve-env` maps the
|
|
423
|
-
branch to dev/staging/prod once, up front, so its output can be used both to
|
|
424
|
-
drive the deploy and to label the job (`Deploy [dev]`, `Deploy [staging]`,
|
|
425
|
-
`Deploy [prod]`). A job's `name:` can reference `needs.<job>.outputs.*` but
|
|
426
|
-
not a step output from within itself, hence the split:
|
|
427
|
-
|
|
428
|
-
```yaml
|
|
429
|
-
jobs:
|
|
430
|
-
resolve-env:
|
|
431
|
-
needs: validate
|
|
432
|
-
if: github.event_name == 'push'
|
|
433
|
-
uses: taraxvoid/voidflow/.github/workflows/resolve-env.yml@15caea203066d42cb7be021eedb6356fac6ffdec # v0.8.1
|
|
434
|
-
|
|
435
|
-
deploy:
|
|
436
|
-
name: Deploy [${{ needs.resolve-env.outputs.env }}]
|
|
437
|
-
needs: resolve-env
|
|
438
|
-
if: github.event_name == 'push'
|
|
439
|
-
environment: ${{ needs.resolve-env.outputs.env }}
|
|
440
|
-
steps:
|
|
441
|
-
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
442
|
-
- uses: taraxvoid/voidflow/actions/cloudflare-deploy@15caea203066d42cb7be021eedb6356fac6ffdec # v0.8.1
|
|
443
|
-
with:
|
|
444
|
-
env: ${{ needs.resolve-env.outputs.env }}
|
|
445
|
-
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
Sites with a single production environment and per-PR previews (no `next` or
|
|
449
|
-
`live` branch) pass `with: { single-environment: true }` to `resolve-env.yml`
|
|
450
|
-
(or the same input on `actions/branch-env-map`). `main` then maps to `prod` and
|
|
451
|
-
`pull_request` events map to `preview`; any other ref fails.
|
|
452
|
-
|
|
453
|
-
### Versioning
|
|
454
|
-
|
|
455
|
-
Semver, released with [release-please](https://github.com/googleapis/release-please)
|
|
456
|
-
([`release-please.yml`](.github/workflows/release-please.yml)) on three branch-based channels
|
|
457
|
-
(full flow in [`RELEASING.md`](RELEASING.md)):
|
|
458
|
-
|
|
459
|
-
| Channel | Branch | Example | npm dist-tag |
|
|
460
|
-
|---|---|---|---|
|
|
461
|
-
| stable | `main` | `0.10.0` | `latest` |
|
|
462
|
-
| prerelease | `next` | `0.10.0-next`, `0.10.0-next.1` | `next` |
|
|
463
|
-
| maintenance | `release/N.x` | `0.9.4` | `release-N.x` |
|
|
464
|
-
|
|
465
|
-
release-please keeps a standing release PR open on each channel's branch, updated as commits land.
|
|
466
|
-
Merging it bumps `package.json` (and [`CHANGELOG.md`](CHANGELOG.md) on stable), tags `vX.Y.Z` (`vX.Y.Z-next[.N]` on `next`),
|
|
467
|
-
creates the GitHub release and publishes to npm and JSR under the channel's dist-tag. The PR is
|
|
468
|
-
opened with the org's release-bot GitHub App token (`RELEASE_BOT_CLIENT_ID` /
|
|
469
|
-
`RELEASE_BOT_APP_PRIVATE_KEY`) so CI runs on it.
|
|
329
|
+
To run less locally, define `test:unit:precommit` or `check:precommit`;
|
|
330
|
+
pre-commit runs those instead. CI still runs the full scripts. For example:
|
|
470
331
|
|
|
471
|
-
|
|
332
|
+
- bun: `"test:unit:precommit": "bun test --changed"`
|
|
333
|
+
- pnpm workspace: `pnpm -r --filter '...[HEAD]' run <script>` (add
|
|
334
|
+
`--filter '!{.}'` if the root defines the same script recursively)
|
|
472
335
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
drive the version bump and generated changelog.
|
|
336
|
+
Your `lefthook.yml` can add commands but not override shared ones. Don't set
|
|
337
|
+
`parallel: true` on `pre-commit`; it applies to the shared steps too and breaks
|
|
338
|
+
their ordering.
|
|
477
339
|
|
|
478
|
-
|
|
340
|
+
Upgrading from `lefthook/site.yml` (v0.13 and earlier): change `configs` to
|
|
341
|
+
`lefthook/base.yml`. The old file now fails commits with that instruction.
|
|
479
342
|
|
|
480
|
-
|
|
481
|
-
mutable like any floating tag.
|
|
482
|
-
- **SHA pin with a version comment**, e.g. `@15caea2... # v0.8.1`: what I use. Same hardening
|
|
483
|
-
as any third-party action, and Renovate or Dependabot can bump it.
|
|
484
|
-
- **Try unreleased changes**: pin the commit SHA of a `vX.Y.Z-next` / `vX.Y.Z-next.N` prerelease tag.
|
|
485
|
-
|
|
486
|
-
Treat `@main` and `@next` as unstable.
|
|
487
|
-
|
|
488
|
-
## Self-validation
|
|
489
|
-
|
|
490
|
-
This repo validates its own workflows and composite actions (`.github/workflows/ci.yml`):
|
|
343
|
+
## Used by
|
|
491
344
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
- **zizmor**: security audit (unpinned refs, script injection via `${{ }}` in `run:`, excess permissions, etc.)
|
|
345
|
+
[taraxvoid.net](https://taraxvoid.net), [Queer Omaha](https://queeromaha.net),
|
|
346
|
+
[Synth Omaha](https://synthomaha.net), [Soundry](https://soundryomaha.org),
|
|
347
|
+
[ravenflight.io](https://ravenflight.io).
|
|
496
348
|
|
|
497
|
-
|
|
349
|
+
Optional badge for your README (follows light/dark mode):
|
|
498
350
|
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
|
|
351
|
+
```html
|
|
352
|
+
<a href="https://github.com/taraxvoid/voidflow">
|
|
353
|
+
<picture>
|
|
354
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/CI-voidflow-8b8fd9?labelColor=2b2d3a&style=flat">
|
|
355
|
+
<img alt="CI: voidflow" src="https://img.shields.io/badge/CI-voidflow-5a5fb8?labelColor=3a3d4d&style=flat">
|
|
356
|
+
</picture>
|
|
357
|
+
</a>
|
|
502
358
|
```
|
|
503
359
|
|
|
504
|
-
|
|
505
|
-
- `just lint-workflows` / `lint-actions` / `lint-shell` / `security`: run one check at a time
|
|
506
|
-
- `just dry-run`: full local run of `ci.yml` via [`act`](https://github.com/nektos/act) (needs Docker running); pass extra args, e.g. `just dry-run -j validation`
|
|
507
|
-
- lefthook runs the fast checks (`lint-workflows`, `lint-actions`, `lint-shell`) on `pre-commit`, and `zizmor` on `pre-push`
|
|
508
|
-
|
|
509
|
-
**Colima users:** `.actrc` already disables the docker-socket mount (`--container-daemon-socket -`). `act` binds `/var/run/docker.sock` by default, which doesn't exist under Colima and fails with `operation not supported`. None of these workflow steps need docker-in-docker, so this is safe.
|
|
510
|
-
|
|
511
|
-
**Run `just dry-run` from a normal checkout, not a linked git worktree.** `act` copies the working tree via `docker cp` rather than a real clone, so a worktree's `.git` pointer back to the parent repo doesn't resolve inside the container, so `git ls-files`-based steps (schema validation, `scripts/shellcheck-actions.ts`) silently see zero files instead of erroring. Verified clean end-to-end (`🏁 Job succeeded`) from a plain clone.
|
|
360
|
+
## Contributing
|
|
512
361
|
|
|
513
|
-
|
|
362
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md). MIT licensed.
|
package/biome/base.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taraxvoid/voidflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Enter a flow state of security, stability, and developer ergonomics - batteries-included Biome, vitest, Astro, local hooks, CI and deployments on Netlify and Cloudflare support with GitHub rulesets, branch environments, releases and security checks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|