@taraxvoid/voidflow 0.13.0-next.1 → 0.14.0-next

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 CHANGED
@@ -1,9 +1,9 @@
1
1
  # voidflow
2
2
 
3
- Shared CI and deploy pipeline for a small fleet of Astro sites on Cloudflare Workers. One set of reusable GitHub Actions workflows and composite actions keeps lint, type checks, license policy, Playwright and axe-core accessibility tests, Unlighthouse budgets, and deploys identical across every site.
3
+ Shared CI and deploy pipeline for a small fleet of Astro sites on Cloudflare Workers, and the bun tools around them. One set of reusable GitHub Actions workflows and composite actions keeps lint, type checks, license policy, Playwright and axe-core accessibility tests, Unlighthouse budgets, and deploys identical across every site.
4
4
 
5
5
  - **SHA-pinned.** Every third-party action in this repo is pinned to a full commit SHA with a version comment.
6
- - **Audited.** Workflows are checked with [zizmor](https://github.com/zizmorcore/zizmor), [actionlint](https://github.com/rhysd/actionlint), check-jsonschema and shellcheck on every change (see [Self-validation](#self-validation)).
6
+ - **Audited.** Workflows are checked with [zizmor](https://github.com/zizmorcore/zizmor), [jactionlint](https://github.com/jdx/jactionlint), check-jsonschema and shellcheck on every change (see [Self-validation](#self-validation)).
7
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
8
 
9
9
  It is semi-opinionated: the defaults match how my own sites are built (bun, Astro, Playwright).
@@ -11,9 +11,22 @@ It is semi-opinionated: the defaults match how my own sites are built (bun, Astr
11
11
  ## Used by
12
12
 
13
13
  - [taraxvoid.net](https://taraxvoid.net), my portfolio
14
- - [Queer Omaha](https://queeromaha.net), [Synth Omaha](https://synthomaha.net) and [Soundry](https://soundryomaha.net), free community sites for Omaha's queer and music scenes
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
15
  - [ravenflight.io](https://ravenflight.io), RavenFlight's marketing site
16
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>
28
+ ```
29
+
17
30
  ## Usage
18
31
 
19
32
  Point `uses` in your configuration at a workflow, e.g. in your `.github/workflows/ci.yml`. Pin to a release commit SHA with the version in a comment, the same way you would any third-party action (see [Versioning](#versioning)).
@@ -82,11 +95,45 @@ jobs:
82
95
  build-env: ${{ format('{{"PUBLIC_SITE_URL":"https://example.com","GIT_SHA":"{0}"}}', github.sha) }}
83
96
  ```
84
97
 
85
- With `pnpm`, the pnpm version comes from `packageManager` and Node from `volta`
86
- or `engines` in `package.json`. The lockfile (`bun.lock` or `pnpm-lock.yaml`)
98
+ With `pnpm`, the pnpm version comes from `packageManager` and Node from
99
+ `engines` in `package.json`. The lockfile (`bun.lock` or `pnpm-lock.yaml`)
87
100
  follows the package manager. `build-env` is a JSON object, visible in logs, so
88
101
  no secrets.
89
102
 
103
+ ### Install-time security scanner (Socket)
104
+
105
+ A bun site can set a security scanner in `bunfig.toml`, which checks every
106
+ package on install:
107
+
108
+ ```toml
109
+ [install.security]
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:
124
+
125
+ ```yaml
126
+ - uses: oven-sh/setup-bun@<sha> # <version>
127
+ - uses: taraxvoid/voidflow/actions/bun-install@<sha> # <version>
128
+ # with: { socket-scanner: 'true' }
129
+ ```
130
+
131
+ The copy is checked by parsing both files: it must equal your `bunfig.toml`
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
+
90
137
  ## Workflow Types
91
138
 
92
139
  ### Validation / Checks
@@ -99,6 +146,39 @@ Runs lint, typecheck, check for GPL licenses, e2e with Playwright.
99
146
  `live`), and `test:e2e:lighthouse` (`live`). A script the site does not define
100
147
  is skipped with a notice, so none of them is required.
101
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.
160
+
161
+ ```yaml
162
+ name: CI
163
+ on:
164
+ pull_request:
165
+ types: [opened, synchronize, reopened, ready_for_review]
166
+ push:
167
+ branches: [main]
168
+ workflow_dispatch:
169
+
170
+ permissions:
171
+ contents: read
172
+ pull-requests: read
173
+
174
+ jobs:
175
+ validate:
176
+ uses: taraxvoid/voidflow/.github/workflows/tool-ci.yml@<release SHA> # vX.Y.Z
177
+ ```
178
+
179
+ The job reports as `validate / Validate`, the check the `tool()` entry in
180
+ [`rulesets/index.ts`](#branch-rulesets-as-code) requires.
181
+
102
182
  ### Unlighthouse (performance / SEO budgets)
103
183
 
104
184
  `site-ci.yml` runs `bun run test:e2e:lighthouse` on PRs into `live`. Sites
@@ -124,8 +204,11 @@ and add `.unlighthouse/` to the site's `.gitignore`.
124
204
 
125
205
  `site-ci.yml` runs `bun run check:licenses` when the lockfile or `scripts/`
126
206
  change. Sites implement that script with the shared check, which runs
127
- `license-checker` in the site's repo root and fails on GPL, AGPL, SSPL and
128
- similar copyleft licenses (LGPL is allowed).
207
+ `license-checker-rseidelsohn` in the site's repo root and fails on GPL, AGPL,
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).
129
212
 
130
213
  With `@taraxvoid/voidflow` as a devDependency, call the bin from the site's
131
214
  `package.json`:
@@ -141,6 +224,11 @@ the global `~/.bunfig.toml` rather than the site's, so a global
141
224
  locally (including in the pre-commit hook) for 3 days after every release.
142
225
  The devDependency's bins are installed under the site's own `bunfig.toml`.
143
226
 
227
+ Both bins (`check-licenses`, `unlighthouse-runner`) are TypeScript run by Bun
228
+ (`#!/usr/bin/env bun`), so `bun` must be on `PATH` even when the site uses
229
+ npm or pnpm. The `@taraxvoid/voidflow/playwright` export is plain ESM and runs
230
+ on Node 22+ without Bun.
231
+
144
232
  ### Playwright preview config
145
233
 
146
234
  Shared `playwright.config` for Astro sites that run e2e against
@@ -169,6 +257,31 @@ Options: `ciPort` (required in CI), `runner` (default `bun`, e.g. `pnpm`),
169
257
  `timeout` (default 30000), `projects` (default Pixel 7 and Desktop Chrome),
170
258
  `testDir` (default `./test/e2e`). Requires `@playwright/test` in the site.
171
259
 
260
+ ## Shared Biome config
261
+
262
+ [`biome/base.json`](biome/base.json) is the house style for the fleet: 4-space
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:
268
+
269
+ ```json
270
+ {
271
+ "$schema": "https://biomejs.dev/schemas/2.5.14/schema.json",
272
+ "extends": ["@taraxvoid/voidflow/biome"],
273
+ "files": {
274
+ "includes": ["**", "!**/bun.lock", "!public/admin/**"]
275
+ }
276
+ }
277
+ ```
278
+
279
+ Lists such as `files.includes` and `overrides` combine with the base's rather
280
+ than replace them; single values set in the site win. The base has no ignore
281
+ lists, so each site keeps its own. It needs Biome 2: the pre-commit hook and
282
+ `site-ci.yml` print a warning (they don't fail) when a site has Biome 1.x
283
+ installed.
284
+
172
285
  ## Dependency updates (Renovate)
173
286
 
174
287
  [`default.json`](default.json) is the shared Renovate preset. Sites add a
@@ -193,30 +306,43 @@ on the repo.
193
306
 
194
307
  ## Git hooks (lefthook)
195
308
 
196
- [`lefthook/site.yml`](lefthook/site.yml) is the shared hook set for the site
197
- stack, consumed as a lefthook remote. It calls the site's own `package.json`
198
- scripts (`<pm> run --if-present`, so a missing script is skipped; `<pm>` is pnpm
199
- when the repo has a `pnpm-lock.yaml`, bun otherwise):
309
+ [`lefthook/base.yml`](lefthook/base.yml) is the shared hook set for any bun
310
+ (or pnpm) repo, sites and tools alike, consumed as a lefthook remote. It calls
311
+ the repo's own `package.json` scripts (`<pm> run --if-present`, so a missing
312
+ script is skipped; `<pm>` is pnpm when the repo has a `pnpm-lock.yaml`, bun
313
+ otherwise):
200
314
 
201
- - `pre-commit`: dependency guard, `format` (formatted files are re-staged),
315
+ - `pre-commit`: dependency guard, Biome 1.x warning, `format` (formatted files are re-staged),
202
316
  `lint:actions`, `check`, `test:unit`, and `check:licenses` / `audit` when the
203
317
  lockfile changed.
204
318
  - `commit-msg`: advisory Conventional Commits hint (never blocks).
205
319
  - `pre-push`: bail if the remote branch is ahead, then `test:push`; skipped when
206
- there is nothing new to push.
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.
207
327
 
208
328
  ```yaml
209
- # lefthook.yml in the site
329
+ # lefthook.yml in the repo
210
330
  remotes:
211
331
  - git_url: https://github.com/taraxvoid/voidflow
212
332
  ref: v0.10.0 # a tag or branch; lefthook passes it to `git clone --branch`
213
333
  configs:
214
- - lefthook/site.yml
334
+ - lefthook/base.yml
215
335
 
216
336
  ```
217
337
 
338
+ Upgrading from `lefthook/site.yml` (v0.13 and earlier): change the `configs`
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
+
218
344
  Lefthook merges `lefthook.yml`, then remotes, then `lefthook-local.yml`, so a
219
- site's own `lefthook.yml` can add commands (for example image compression) but
345
+ repo's own `lefthook.yml` can add commands (for example image compression) but
220
346
  cannot override ones defined here. Differences go through `package.json`
221
347
  scripts: define `test:unit:precommit` (for example excluding tests that need a
222
348
  built `dist/`) and the pre-commit unit step runs it instead of `test:unit`.
@@ -232,9 +358,23 @@ locally. Common uses:
232
358
  belong to any package. If the root `package.json` defines the same script
233
359
  as a recursive run, add `--filter '!{.}'` so it doesn't recurse.
234
360
 
235
- Install the binary through mise (`lefthook = "<version>"` in `mise.toml`) and
236
- set the `prepare` script so `bun install` wires the hooks, but make it tolerate a
237
- missing binary:
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:
368
+
369
+ ```toml
370
+ [tools]
371
+ lefthook = "2.2.1"
372
+ gitleaks = "8.30.1"
373
+ jactionlint = "2.1.0"
374
+ ```
375
+
376
+ Renovate bumps `mise.toml` natively. Set the `prepare` script so `bun install`
377
+ wires the hooks, but make it tolerate a missing binary:
238
378
 
239
379
  ```json
240
380
  "prepare": "lefthook install || echo 'lefthook not installed (run mise install); git hooks skipped'"
@@ -250,7 +390,10 @@ release tag.
250
390
  Branch protection for the fleet lives in [`rulesets/index.ts`](rulesets/index.ts):
251
391
  one canonical `main` / `next` / `live` definition (plus `prerelease` and `maintenance`
252
392
  tiers for repos with release channels) and a small per-repo table
253
- (Netlify or not, check names, CODEOWNERS, extra bypass actors). Hand-edited
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
254
397
  rulesets drift, and drift is how a `next` ruleset ends up requiring a check
255
398
  name that no workflow reports any more.
256
399
 
@@ -268,7 +411,11 @@ manages `main`, `prerelease` (`next`) and `maintenance` (`release/*`) and sets
268
411
  `linearHistory: false` so `next` can be promoted into `main` with a merge commit.
269
412
  Uses the `gh` CLI for auth (needs admin on the repos). Rulesets that are not in
270
413
  the config are reported and left alone; only `retire` entries are ever deleted,
271
- and only with `--prune`. It is repo tooling, not part of the published package.
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.
272
419
 
273
420
  ## Deployments
274
421
 
@@ -342,16 +489,16 @@ Treat `@main` and `@next` as unstable.
342
489
 
343
490
  This repo validates its own workflows and composite actions (`.github/workflows/ci.yml`):
344
491
 
345
- - **actionlint**: workflow schema/logic; also shellchecks `run:` steps in `.github/workflows/*.yml` automatically (`shellcheck` ships on `ubuntu-latest`)
492
+ - **jactionlint**: workflow schema/logic; also shellchecks `run:` steps in `.github/workflows/*.yml` automatically (`shellcheck` ships on `ubuntu-latest`)
346
493
  - **check-jsonschema**: schema-checks `actions/**/action.yml` and the workflow files against [SchemaStore](https://www.schemastore.org/)'s `github-action.json` / `github-workflow.json`
347
- - **shellcheck** (`scripts/shellcheck-actions.ts`): checks `run:` steps inside `actions/**/action.yml`, which actionlint doesn't reach
494
+ - **shellcheck** (`scripts/shellcheck-actions.ts`): checks `run:` steps inside `actions/**/action.yml`, which jactionlint doesn't reach
348
495
  - **zizmor**: security audit (unpinned refs, script injection via `${{ }}` in `run:`, excess permissions, etc.)
349
496
 
350
497
  ### Local dev setup
351
498
 
352
499
  ```sh
353
- brew install just lefthook actionlint shellcheck bun act uv
354
- lefthook install
500
+ mise install # tools pinned in mise.toml
501
+ bun install # its prepare script runs `lefthook install`
355
502
  ```
356
503
 
357
504
  - `just validate`: run everything CI runs, locally
@@ -0,0 +1,63 @@
1
+ {
2
+ "vcs": {
3
+ "enabled": true,
4
+ "clientKind": "git",
5
+ "useIgnoreFile": true
6
+ },
7
+ "assist": {
8
+ "enabled": true,
9
+ "actions": {
10
+ "source": {
11
+ "organizeImports": "on"
12
+ }
13
+ }
14
+ },
15
+ "formatter": {
16
+ "enabled": true,
17
+ "indentStyle": "space",
18
+ "indentWidth": 4,
19
+ "lineEnding": "lf",
20
+ "lineWidth": 80,
21
+ "useEditorconfig": true
22
+ },
23
+ "javascript": {
24
+ "formatter": {
25
+ "arrowParentheses": "always",
26
+ "jsxQuoteStyle": "double",
27
+ "quoteProperties": "asNeeded",
28
+ "quoteStyle": "single",
29
+ "semicolons": "asNeeded",
30
+ "trailingCommas": "all"
31
+ }
32
+ },
33
+ "html": {
34
+ "formatter": {
35
+ "indentScriptAndStyle": false,
36
+ "selfCloseVoidElements": "always"
37
+ }
38
+ },
39
+ "css": {
40
+ "linter": {
41
+ "enabled": false
42
+ }
43
+ },
44
+ "linter": {
45
+ "enabled": true,
46
+ "rules": {
47
+ "preset": "recommended"
48
+ }
49
+ },
50
+ "overrides": [
51
+ {
52
+ "includes": ["**/*.astro"],
53
+ "linter": {
54
+ "rules": {
55
+ "correctness": {
56
+ "noUnusedImports": "off",
57
+ "noUnusedVariables": "off"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ]
63
+ }
package/package.json CHANGED
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "name": "@taraxvoid/voidflow",
3
- "version": "0.13.0-next.1",
4
- "description": "Shared workflows, actions and tooling for @taraxvoid sites",
3
+ "version": "0.14.0-next",
4
+ "description": "Shared workflows, hooks and tooling for @taraxvoid repos",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "github-actions",
8
8
  "ci",
9
9
  "reusable-workflows",
10
+ "lefthook",
11
+ "renovate",
12
+ "bun",
10
13
  "playwright",
11
14
  "unlighthouse",
12
- "astro",
13
15
  "cloudflare"
14
16
  ],
15
17
  "homepage": "https://github.com/taraxvoid/voidflow#readme",
@@ -33,24 +35,37 @@
33
35
  "types": "./playwright/index.d.mts",
34
36
  "default": "./playwright/index.mjs"
35
37
  },
38
+ "./biome": "./biome/base.json",
36
39
  "./package.json": "./package.json"
37
40
  },
38
41
  "files": [
42
+ "biome",
39
43
  "playwright",
40
44
  "scripts/unlighthouse",
41
45
  "scripts/check-licenses.ts"
42
46
  ],
43
47
  "engines": {
44
- "bun": ">=1.4.0"
48
+ "bun": ">=1.4.0",
49
+ "node": ">=22"
45
50
  },
46
51
  "scripts": {
52
+ "prepare": "lefthook install || echo 'lefthook not installed (run mise install); git hooks skipped'",
47
53
  "test": "bun test"
48
54
  },
49
55
  "peerDependencies": {
50
56
  "@playwright/test": ">=1.40"
51
57
  },
58
+ "peerDependenciesMeta": {
59
+ "@playwright/test": {
60
+ "optional": true
61
+ }
62
+ },
52
63
  "devDependencies": {
53
- "@playwright/test": "^1.40.0",
54
- "@socketsecurity/bun-security-scanner": "^1.1.3"
64
+ "@biomejs/biome": "2.5.14",
65
+ "@playwright/test": "1.64.0",
66
+ "@socketsecurity/bun-security-scanner": "1.1.3"
67
+ },
68
+ "dependencies": {
69
+ "license-checker-rseidelsohn": "5.0.1"
55
70
  }
56
71
  }
@@ -2,14 +2,18 @@
2
2
  /**
3
3
  * Shared license policy check for the Astro/bun site stack: fail on GPL kudzu.
4
4
  *
5
- * Runs `license-checker` against the current working directory (the caller's
5
+ * Runs `license-checker-rseidelsohn` against the current working directory (the caller's
6
6
  * repo root) and exits non-zero if any installed package carries a blocked
7
- * license.
7
+ * license. The checker is voidflow's own exact-pinned dependency, resolved from
8
+ * voidflow's install rather than `bunx`, so every site runs the same version
9
+ * and nothing is fetched at check time.
8
10
  *
9
11
  * Usage: bun check-licenses.ts
10
12
  */
11
13
 
12
14
  import { spawnSync } from 'node:child_process'
15
+ import { readFileSync } from 'node:fs'
16
+ import { dirname, join } from 'node:path'
13
17
 
14
18
  export const FAIL_ON = [
15
19
  'GPL-1.0',
@@ -41,15 +45,26 @@ export const FAIL_ON = [
41
45
  // modifying/statically linking the library, not on depending on it as-is.
42
46
  ].join(';')
43
47
 
48
+ const checkerPkg = Bun.resolveSync(
49
+ 'license-checker-rseidelsohn/package.json',
50
+ import.meta.dir,
51
+ )
52
+ const checkerBin = join(
53
+ dirname(checkerPkg),
54
+ JSON.parse(readFileSync(checkerPkg, 'utf8')).bin[
55
+ 'license-checker-rseidelsohn'
56
+ ],
57
+ )
58
+
44
59
  const result = spawnSync(
45
- 'bunx',
46
- ['license-checker', '--summary', '--failOn', FAIL_ON],
60
+ process.execPath,
61
+ [checkerBin, '--summary', '--failOn', FAIL_ON],
47
62
  { cwd: process.cwd(), stdio: 'inherit' },
48
63
  )
49
64
 
50
65
  if (result.error) {
51
66
  console.error(
52
- `\ncheck:licenses failed - could not run license-checker: ${result.error.message}`,
67
+ `\ncheck:licenses failed - could not run license-checker-rseidelsohn: ${result.error.message}`,
53
68
  )
54
69
  process.exit(1)
55
70
  }