@taraxvoid/voidflow 0.14.0-next.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -386
- package/package.json +3 -3
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
|
-
|
|
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.
|
|
104
126
|
|
|
105
|
-
|
|
106
|
-
|
|
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.
|
|
107
129
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
scanner = "@socketsecurity/bun-security-scanner"
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
CI skips it by default. The scanner calls a third-party API on every install,
|
|
114
|
-
so a network blip or an API change could fail a build that otherwise passes,
|
|
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.
|
|
120
|
-
|
|
121
|
-
To run it in CI too, pass `socket-scanner: true` to `site-ci.yml`. A workflow
|
|
122
|
-
that installs on its own (outside `site-ci.yml`) gets the same default from
|
|
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.
|
|
142
|
-
|
|
143
|
-
`site-ci.yml` runs these `package.json` scripts: `lint`, `check`, `build`,
|
|
144
|
-
`test:unit`, `check:licenses`, `test:e2e:full` (PRs into `main`),
|
|
145
|
-
`test:e2e:all` (PRs into `next` and `live`), `test:e2e:a11y` (`next` and
|
|
146
|
-
`live`), and `test:e2e:lighthouse` (`live`). A script the site does not define
|
|
147
|
-
is skipped with a notice, so none of them is required.
|
|
148
|
-
|
|
149
|
-
### Tool validation
|
|
139
|
+
## Tool CI
|
|
150
140
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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,105 +160,88 @@ 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
|
-
after a failure too, so the findings that failed a run are the ones uploaded.
|
|
189
|
-
|
|
190
|
-
`upload-sarif.yml` sends them to code scanning, which is what voidlight
|
|
191
|
-
counts. It is a separate workflow because it needs `security-events: write`,
|
|
192
|
-
and a reusable workflow can't ask for more than its caller grants: adding it to
|
|
193
|
-
the validate workflows would break every caller that doesn't. Add a second job:
|
|
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.
|
|
194
174
|
|
|
195
175
|
```yaml
|
|
196
176
|
jobs:
|
|
197
|
-
|
|
198
|
-
uses: taraxvoid/voidflow/.github/workflows/site-ci.yml@<sha> # <version>
|
|
199
|
-
sarif:
|
|
177
|
+
resolve-env:
|
|
200
178
|
needs: validate
|
|
201
|
-
if:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
179
|
+
if: github.event_name == 'push'
|
|
180
|
+
uses: taraxvoid/voidflow/.github/workflows/resolve-env.yml@<sha> # vX.Y.Z
|
|
181
|
+
|
|
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 }}
|
|
207
194
|
```
|
|
208
195
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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.
|
|
204
|
+
|
|
205
|
+
## npm package
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
bun add -d @taraxvoid/voidflow # or: pnpm add -D @taraxvoid/voidflow
|
|
209
|
+
```
|
|
213
210
|
|
|
214
|
-
|
|
211
|
+
Also published unscoped as `voidflow` with identical contents.
|
|
215
212
|
|
|
216
|
-
`
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
[Unlighthouse](https://unlighthouse.dev/) against it, reusing Playwright's
|
|
220
|
-
Chromium. It exits non-zero if a category budget in the site's config fails.
|
|
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.
|
|
221
216
|
|
|
222
|
-
|
|
223
|
-
`package.json`:
|
|
217
|
+
### Unlighthouse budgets
|
|
224
218
|
|
|
225
219
|
```json
|
|
226
220
|
"test:e2e:lighthouse": "bun run build && unlighthouse-runner --dir dist"
|
|
227
221
|
```
|
|
228
222
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
Unlighthouse version). Start from
|
|
232
|
-
[`scripts/unlighthouse/unlighthouse.config.example.ts`](scripts/unlighthouse/unlighthouse.config.example.ts)
|
|
233
|
-
and add `.unlighthouse/` to the site's `.gitignore`.
|
|
223
|
+
Serves the built directory and runs [Unlighthouse](https://unlighthouse.dev/)
|
|
224
|
+
with Playwright's Chromium. Fails if a category budget fails.
|
|
234
225
|
|
|
235
|
-
|
|
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
|
|
236
230
|
|
|
237
|
-
|
|
238
|
-
change. Sites implement that script with the shared check, which runs
|
|
239
|
-
`license-checker-rseidelsohn` in the site's repo root and fails on GPL, AGPL,
|
|
240
|
-
SSPL and similar copyleft licenses (LGPL is allowed). Voidflow pins the
|
|
241
|
-
checker to an exact version as its own dependency, so sites don't need to
|
|
242
|
-
install it and every site runs the same version (sites that added
|
|
243
|
-
`license-checker-rseidelsohn` as a devDependency for 0.12.2 can drop it).
|
|
231
|
+
Add `.unlighthouse/` to `.gitignore`.
|
|
244
232
|
|
|
245
|
-
|
|
246
|
-
`package.json`:
|
|
233
|
+
### License check
|
|
247
234
|
|
|
248
235
|
```json
|
|
249
236
|
"check:licenses": "check-licenses"
|
|
250
237
|
```
|
|
251
238
|
|
|
252
|
-
|
|
253
|
-
duplicates the version pin outside the lockfile and Renovate, and `bunx` reads
|
|
254
|
-
the global `~/.bunfig.toml` rather than the site's, so a global
|
|
255
|
-
`minimumReleaseAge` that doesn't exclude `@taraxvoid/voidflow` blocks it
|
|
256
|
-
locally (including in the pre-commit hook) for 3 days after every release.
|
|
257
|
-
The devDependency's bins are installed under the site's own `bunfig.toml`.
|
|
239
|
+
Fails on GPL, AGPL, SSPL and similar copyleft licenses. LGPL is allowed.
|
|
258
240
|
|
|
259
|
-
|
|
260
|
-
(`#!/usr/bin/env bun`), so `bun` must be on `PATH` even when the site uses
|
|
261
|
-
npm or pnpm. The `@taraxvoid/voidflow/playwright` export is plain ESM and runs
|
|
262
|
-
on Node 22+ without Bun.
|
|
241
|
+
### Playwright config
|
|
263
242
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
Shared `playwright.config` for Astro sites that run e2e against
|
|
267
|
-
`astro preview`. It picks a free port outside CI, sets
|
|
268
|
-
`ASTRO_PREVIEW_BACKGROUND=false` (Astro backgrounds `preview` in agent
|
|
269
|
-
environments, which makes Playwright think the server exited), and drops the
|
|
270
|
-
`webServer` when `PLAYWRIGHT_BASE_URL` is set.
|
|
271
|
-
|
|
272
|
-
```sh
|
|
273
|
-
bun add -d @taraxvoid/voidflow # or: pnpm add -D @taraxvoid/voidflow
|
|
274
|
-
```
|
|
243
|
+
For Astro sites that run e2e against `astro preview`. Requires
|
|
244
|
+
`@playwright/test`. Runs on Node 22+ without Bun.
|
|
275
245
|
|
|
276
246
|
```js
|
|
277
247
|
// playwright.config.js
|
|
@@ -280,23 +250,14 @@ import { definePreviewConfig } from '@taraxvoid/voidflow/playwright'
|
|
|
280
250
|
export default definePreviewConfig({ ciPort: 4141 })
|
|
281
251
|
```
|
|
282
252
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
`
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
Options: `ciPort` (required in CI), `runner` (default `bun`, e.g. `pnpm`),
|
|
289
|
-
`timeout` (default 30000), `projects` (default Pixel 7 and Desktop Chrome),
|
|
290
|
-
`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`).
|
|
291
257
|
|
|
292
|
-
|
|
258
|
+
### Biome
|
|
293
259
|
|
|
294
|
-
|
|
295
|
-
indent, LF, width 80, single quotes, semicolons only where needed, trailing
|
|
296
|
-
commas, the `recommended` rule preset, organized imports, CSS lint off, and
|
|
297
|
-
unused-import/variable rules off in `.astro` files (Biome can't see template
|
|
298
|
-
usage). With `@taraxvoid/voidflow` as a devDependency, the site's `biome.json`
|
|
299
|
-
extends it and adds only what is specific to the site:
|
|
260
|
+
Needs Biome 2. In the site's `biome.json`:
|
|
300
261
|
|
|
301
262
|
```json
|
|
302
263
|
{
|
|
@@ -308,16 +269,12 @@ extends it and adds only what is specific to the site:
|
|
|
308
269
|
}
|
|
309
270
|
```
|
|
310
271
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
`
|
|
315
|
-
installed.
|
|
316
|
-
|
|
317
|
-
## Dependency updates (Renovate)
|
|
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. It has no ignore lists. Lists like `files.includes` and
|
|
275
|
+
`overrides` merge with the base; single values in the site win.
|
|
318
276
|
|
|
319
|
-
|
|
320
|
-
`renovate.json`:
|
|
277
|
+
## Renovate
|
|
321
278
|
|
|
322
279
|
```json
|
|
323
280
|
{
|
|
@@ -326,220 +283,78 @@ installed.
|
|
|
326
283
|
}
|
|
327
284
|
```
|
|
328
285
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
script is skipped; `<pm>` is pnpm when the repo has a `pnpm-lock.yaml`, bun
|
|
345
|
-
otherwise):
|
|
346
|
-
|
|
347
|
-
- `pre-commit`: dependency guard, Biome 1.x warning, `format` (formatted files are re-staged),
|
|
348
|
-
`lint:actions`, `check`, `test:unit`, and `check:licenses` / `audit` when the
|
|
349
|
-
lockfile changed.
|
|
350
|
-
- `commit-msg`: advisory Conventional Commits hint (never blocks).
|
|
351
|
-
- `pre-push`: bail if the remote branch is ahead, then `test:push`; skipped when
|
|
352
|
-
the push carries no commit the remote lacks (branch deletes, and tags or
|
|
353
|
-
branches at already-pushed commits).
|
|
354
|
-
|
|
355
|
-
Before `test:unit` and `test:push`, the hooks unset the variables git exports to
|
|
356
|
-
hooks (`GIT_DIR`, `GIT_INDEX_FILE`, ...; the list comes from
|
|
357
|
-
`git rev-parse --local-env-vars`). Without that, a test that runs
|
|
358
|
-
`git -C <tmpdir> commit` operates on, and commits into, the repo being hooked.
|
|
286
|
+
Requires the Renovate GitHub App. Based on `config:best-practices`: 3-day
|
|
287
|
+
release age (except voidflow and security fixes), `chore(deps):` commits,
|
|
288
|
+
grouped updates, Monday mornings. It also bumps the lefthook remote `ref`, and
|
|
289
|
+
`justfile` tool versions annotated with
|
|
290
|
+
`# renovate: datasource=... depName=...` on the line above.
|
|
291
|
+
|
|
292
|
+
## Git hooks
|
|
293
|
+
|
|
294
|
+
Shared lefthook hooks for bun or pnpm repos. Each step runs the repo's own
|
|
295
|
+
`package.json` script and is skipped if the script is missing.
|
|
296
|
+
|
|
297
|
+
- `pre-commit`: dependency guard, `format` (re-staged), `lint:actions`,
|
|
298
|
+
`check`, `test:unit`, and `check:licenses` / `audit` when the lockfile changed
|
|
299
|
+
- `commit-msg`: Conventional Commits hint (never blocks)
|
|
300
|
+
- `pre-push`: stop if the remote is ahead, then `test:push`
|
|
359
301
|
|
|
360
302
|
```yaml
|
|
361
|
-
# lefthook.yml
|
|
303
|
+
# lefthook.yml
|
|
362
304
|
remotes:
|
|
363
305
|
- git_url: https://github.com/taraxvoid/voidflow
|
|
364
|
-
ref:
|
|
306
|
+
ref: vX.Y.Z # a tag, not a SHA
|
|
365
307
|
configs:
|
|
366
308
|
- lefthook/base.yml
|
|
367
|
-
|
|
368
309
|
```
|
|
369
310
|
|
|
370
|
-
|
|
371
|
-
entry to `lefthook/base.yml` when you bump `ref`. The hooks are otherwise the
|
|
372
|
-
same. `lefthook/site.yml` is now a stub whose `pre-commit` and `pre-push` fail
|
|
373
|
-
with that instruction, so a `ref` bump (for example from Renovate) that leaves
|
|
374
|
-
`configs` alone stops commits instead of turning the hooks off silently.
|
|
375
|
-
|
|
376
|
-
Lefthook merges `lefthook.yml`, then remotes, then `lefthook-local.yml`, so a
|
|
377
|
-
repo's own `lefthook.yml` can add commands (for example image compression) but
|
|
378
|
-
cannot override ones defined here. Differences go through `package.json`
|
|
379
|
-
scripts: define `test:unit:precommit` (for example excluding tests that need a
|
|
380
|
-
built `dist/`) and the pre-commit unit step runs it instead of `test:unit`.
|
|
381
|
-
Likewise, define `check:precommit` and the typecheck step runs it instead of
|
|
382
|
-
`check`. CI keeps calling the full scripts, so these only narrow what runs
|
|
383
|
-
locally. Common uses:
|
|
384
|
-
|
|
385
|
-
- bun: `"test:unit:precommit": "bun test --changed"` runs only the test files
|
|
386
|
-
whose imports reach an uncommitted change.
|
|
387
|
-
- pnpm workspace: `pnpm -r --filter '...[HEAD]' run <script>` runs only the
|
|
388
|
-
changed packages and their dependents. Fall back to the full run when a
|
|
389
|
-
root-level file (lockfile, shared tsconfig) changed, since those don't
|
|
390
|
-
belong to any package. If the root `package.json` defines the same script
|
|
391
|
-
as a recursive run, add `--filter '!{.}'` so it doesn't recurse.
|
|
392
|
-
|
|
393
|
-
Hook-level options merge too. Don't set `parallel: true` under `pre-commit:` in
|
|
394
|
-
the repo's `lefthook.yml`: it applies to the merged hook, so every step,
|
|
395
|
-
including the shared ones, starts at once and their `priority` order is ignored
|
|
396
|
-
(tests that share a database or build output can then time out).
|
|
397
|
-
|
|
398
|
-
Install the binaries through mise. A starter `mise.toml` for a repo using
|
|
399
|
-
these hooks and voidflow CI:
|
|
311
|
+
Install the binaries with mise:
|
|
400
312
|
|
|
401
313
|
```toml
|
|
314
|
+
# mise.toml
|
|
402
315
|
[tools]
|
|
403
316
|
lefthook = "2.2.1"
|
|
404
317
|
gitleaks = "8.30.1"
|
|
405
318
|
jactionlint = "2.1.0"
|
|
406
319
|
```
|
|
407
320
|
|
|
408
|
-
|
|
409
|
-
wires the hooks, but make it tolerate a missing binary:
|
|
321
|
+
and wire the hooks on install without failing where lefthook is absent (CI):
|
|
410
322
|
|
|
411
323
|
```json
|
|
412
324
|
"prepare": "lefthook install || echo 'lefthook not installed (run mise install); git hooks skipped'"
|
|
413
325
|
```
|
|
414
326
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
images, and CI never runs git hooks anyway. `ref` cannot be a commit SHA; pin a
|
|
418
|
-
release tag.
|
|
327
|
+
To run less locally, define `test:unit:precommit` or `check:precommit`;
|
|
328
|
+
pre-commit runs those instead. CI still runs the full scripts. For example:
|
|
419
329
|
|
|
420
|
-
|
|
330
|
+
- bun: `"test:unit:precommit": "bun test --changed"`
|
|
331
|
+
- pnpm workspace: `pnpm -r --filter '...[HEAD]' run <script>` (add
|
|
332
|
+
`--filter '!{.}'` if the root defines the same script recursively)
|
|
421
333
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
(Netlify or not, check names, CODEOWNERS, extra bypass actors). Sites use
|
|
426
|
-
`site()` and tools use `tool()` (main only, no Netlify, requires
|
|
427
|
-
`validate / Validate` from `tool-ci.yml`). Private repos are left out: on a
|
|
428
|
-
plan without rulesets for private repos, the API returns 403. Hand-edited
|
|
429
|
-
rulesets drift, and drift is how a `next` ruleset ends up requiring a check
|
|
430
|
-
name that no workflow reports any more.
|
|
431
|
-
|
|
432
|
-
```sh
|
|
433
|
-
just rulesets-plan # read-only: diff every repo against the config
|
|
434
|
-
just rulesets-plan --repo soundry
|
|
435
|
-
just rulesets-plan --check # exit 1 on drift, for CI
|
|
436
|
-
just rulesets-apply # create/update rulesets to match
|
|
437
|
-
just rulesets-apply --prune # also delete rulesets a repo lists under `retire`
|
|
438
|
-
```
|
|
334
|
+
Your `lefthook.yml` can add commands but not override shared ones. Don't set
|
|
335
|
+
`parallel: true` on `pre-commit`; it applies to the shared steps too and breaks
|
|
336
|
+
their ordering.
|
|
439
337
|
|
|
440
|
-
|
|
441
|
-
`
|
|
442
|
-
manages `main`, `prerelease` (`next`) and `maintenance` (`release/*`) and sets
|
|
443
|
-
`linearHistory: false` so `next` can be promoted into `main` with a merge commit.
|
|
444
|
-
Uses the `gh` CLI for auth (needs admin on the repos). Rulesets that are not in
|
|
445
|
-
the config are reported and left alone; only `retire` entries are ever deleted,
|
|
446
|
-
and only with `--prune`. Private repos on a plan without rulesets (GitHub answers
|
|
447
|
-
403, e.g. a free personal account) are skipped and listed at the end rather than
|
|
448
|
-
failing the run, so `--check` stays green and they pick up their rulesets once the
|
|
449
|
-
repo goes public or the plan changes. It is repo tooling, not part of the published
|
|
450
|
-
package.
|
|
451
|
-
|
|
452
|
-
## Deployments
|
|
453
|
-
|
|
454
|
-
Example deployment job added to your workflow above. `resolve-env` maps the
|
|
455
|
-
branch to dev/staging/prod once, up front, so its output can be used both to
|
|
456
|
-
drive the deploy and to label the job (`Deploy [dev]`, `Deploy [staging]`,
|
|
457
|
-
`Deploy [prod]`). A job's `name:` can reference `needs.<job>.outputs.*` but
|
|
458
|
-
not a step output from within itself, hence the split:
|
|
338
|
+
Upgrading from `lefthook/site.yml` (v0.13 and earlier): change `configs` to
|
|
339
|
+
`lefthook/base.yml`. The old file now fails commits with that instruction.
|
|
459
340
|
|
|
460
|
-
|
|
461
|
-
jobs:
|
|
462
|
-
resolve-env:
|
|
463
|
-
needs: validate
|
|
464
|
-
if: github.event_name == 'push'
|
|
465
|
-
uses: taraxvoid/voidflow/.github/workflows/resolve-env.yml@15caea203066d42cb7be021eedb6356fac6ffdec # v0.8.1
|
|
466
|
-
|
|
467
|
-
deploy:
|
|
468
|
-
name: Deploy [${{ needs.resolve-env.outputs.env }}]
|
|
469
|
-
needs: resolve-env
|
|
470
|
-
if: github.event_name == 'push'
|
|
471
|
-
environment: ${{ needs.resolve-env.outputs.env }}
|
|
472
|
-
steps:
|
|
473
|
-
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
474
|
-
- uses: taraxvoid/voidflow/actions/cloudflare-deploy@15caea203066d42cb7be021eedb6356fac6ffdec # v0.8.1
|
|
475
|
-
with:
|
|
476
|
-
env: ${{ needs.resolve-env.outputs.env }}
|
|
477
|
-
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
Sites with a single production environment and per-PR previews (no `next` or
|
|
481
|
-
`live` branch) pass `with: { single-environment: true }` to `resolve-env.yml`
|
|
482
|
-
(or the same input on `actions/branch-env-map`). `main` then maps to `prod` and
|
|
483
|
-
`pull_request` events map to `preview`; any other ref fails.
|
|
484
|
-
|
|
485
|
-
### Versioning
|
|
486
|
-
|
|
487
|
-
Semver, released with [release-please](https://github.com/googleapis/release-please)
|
|
488
|
-
([`release-please.yml`](.github/workflows/release-please.yml)) on three branch-based channels
|
|
489
|
-
(full flow in [`RELEASING.md`](RELEASING.md)):
|
|
490
|
-
|
|
491
|
-
| Channel | Branch | Example | npm dist-tag |
|
|
492
|
-
|---|---|---|---|
|
|
493
|
-
| stable | `main` | `0.10.0` | `latest` |
|
|
494
|
-
| prerelease | `next` | `0.10.0-next`, `0.10.0-next.1` | `next` |
|
|
495
|
-
| maintenance | `release/N.x` | `0.9.4` | `release-N.x` |
|
|
496
|
-
|
|
497
|
-
release-please keeps a standing release PR open on each channel's branch, updated as commits land.
|
|
498
|
-
Merging it bumps `package.json` (and [`CHANGELOG.md`](CHANGELOG.md) on stable), tags `vX.Y.Z` (`vX.Y.Z-next[.N]` on `next`),
|
|
499
|
-
creates the GitHub release and publishes to npm and JSR under the channel's dist-tag. The PR is
|
|
500
|
-
opened with the org's release-bot GitHub App token (`RELEASE_BOT_CLIENT_ID` /
|
|
501
|
-
`RELEASE_BOT_APP_PRIVATE_KEY`) so CI runs on it.
|
|
502
|
-
|
|
503
|
-
Feature PRs target `next`; promotion to `main` is a merge-commit PR (`just promote`).
|
|
504
|
-
|
|
505
|
-
Commit messages should follow [Conventional Commits](https://www.conventionalcommits.org),
|
|
506
|
-
enforced loosely, as an advisory `commit-msg` hint (see
|
|
507
|
-
[`scripts/check-commit-msg.sh`](scripts/check-commit-msg.sh)), not a blocking check. They
|
|
508
|
-
drive the version bump and generated changelog.
|
|
509
|
-
|
|
510
|
-
Ways to consume a release:
|
|
511
|
-
|
|
512
|
-
- **Floating major tag**: `@v0` follows the newest stable 0.x release. Easiest to track, and
|
|
513
|
-
mutable like any floating tag.
|
|
514
|
-
- **SHA pin with a version comment**, e.g. `@15caea2... # v0.8.1`: what I use. Same hardening
|
|
515
|
-
as any third-party action, and Renovate or Dependabot can bump it.
|
|
516
|
-
- **Try unreleased changes**: pin the commit SHA of a `vX.Y.Z-next` / `vX.Y.Z-next.N` prerelease tag.
|
|
517
|
-
|
|
518
|
-
Treat `@main` and `@next` as unstable.
|
|
519
|
-
|
|
520
|
-
## Self-validation
|
|
521
|
-
|
|
522
|
-
This repo validates its own workflows and composite actions (`.github/workflows/ci.yml`):
|
|
341
|
+
## Used by
|
|
523
342
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
- **zizmor**: security audit (unpinned refs, script injection via `${{ }}` in `run:`, excess permissions, etc.)
|
|
343
|
+
[taraxvoid.net](https://taraxvoid.net), [Queer Omaha](https://queeromaha.net),
|
|
344
|
+
[Synth Omaha](https://synthomaha.net), [Soundry](https://soundryomaha.org),
|
|
345
|
+
[ravenflight.io](https://ravenflight.io).
|
|
528
346
|
|
|
529
|
-
|
|
347
|
+
Optional badge for your README (follows light/dark mode):
|
|
530
348
|
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
|
|
349
|
+
```html
|
|
350
|
+
<a href="https://github.com/taraxvoid/voidflow">
|
|
351
|
+
<picture>
|
|
352
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/CI-voidflow-8b8fd9?labelColor=2b2d3a&style=flat">
|
|
353
|
+
<img alt="CI: voidflow" src="https://img.shields.io/badge/CI-voidflow-5a5fb8?labelColor=3a3d4d&style=flat">
|
|
354
|
+
</picture>
|
|
355
|
+
</a>
|
|
534
356
|
```
|
|
535
357
|
|
|
536
|
-
|
|
537
|
-
- `just lint-workflows` / `lint-actions` / `lint-shell` / `security`: run one check at a time
|
|
538
|
-
- `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`
|
|
539
|
-
- lefthook runs the fast checks (`lint-workflows`, `lint-actions`, `lint-shell`) on `pre-commit`, and `zizmor` on `pre-push`
|
|
540
|
-
|
|
541
|
-
**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.
|
|
542
|
-
|
|
543
|
-
**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.
|
|
358
|
+
## Contributing
|
|
544
359
|
|
|
545
|
-
|
|
360
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md). MIT licensed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taraxvoid/voidflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
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": [
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@biomejs/biome": "2.5.
|
|
65
|
-
"@playwright/test": "1.64.
|
|
64
|
+
"@biomejs/biome": "2.5.15",
|
|
65
|
+
"@playwright/test": "1.64.1",
|
|
66
66
|
"@socketsecurity/bun-security-scanner": "1.1.3"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|