@bensandee/tooling 0.69.0 → 0.71.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 CHANGED
@@ -32,13 +32,14 @@ The tool auto-detects project structure, CI platform, project type, and Docker p
32
32
  | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
33
33
  | `tooling repo:sync [dir]` | Detect, generate, and sync project tooling (idempotent). First run prompts for release strategy, CI platform (if not detected), and formatter (if Prettier found). Subsequent runs are non-interactive. |
34
34
  | `tooling repo:sync --check [dir]` | Dry-run drift detection. Exits 1 if files would change. CI-friendly. |
35
- | `tooling checks:run` | Run project checks (build, docker:build, typecheck, lint, test, format, knip, tooling:check, docker:check). Flags: `--skip`, `--add`, `--fail-fast`, `--verbose`, `--with-coverage`, `--no-coverage`. |
35
+ | `tooling checks:run` | Run project checks -- `bst checks:run --help` lists the ordered steps. Flags: `--skip`, `--add`, `--fail-fast`, `--verbose`, `--with-coverage`, `--coverage`, `--no-coverage`. |
36
+ | `tooling lint:types` | Type-aware linting (`oxlint --type-aware`, backed by `oxlint-tsgolint`). Flags: `--json`. See [Type-aware linting](#type-aware-linting) below. |
36
37
 
37
- **Flags:** `--yes` (accept all defaults), `--no-ci`, `--no-prompt`, `--eslint-plugin`
38
+ **Flags:** `--yes` (accept all defaults), `--no-ci` (negates `--ci`), `--no-prompt` (negates `--prompt`), `--eslint-plugin`
38
39
 
39
40
  #### `checks:run`
40
41
 
41
- Runs checks in order: build, docker:build, typecheck, lint, test, format (--check), knip, tooling:check, docker:check. Checks without a matching script in `package.json` are silently skipped.
42
+ Runs checks in order: build, typecheck, lint, ascii:check (opt-in), lint:types, test, format (--check), knip, tooling:check, health:check (opt-in), docs:check, docker:build, docker:check. Checks without a matching script in `package.json` are silently skipped.
42
43
 
43
44
  The `--skip` flag supports glob patterns via picomatch:
44
45
 
@@ -61,6 +62,7 @@ tooling checks:run --add e2e
61
62
  Explicit flags always win, regardless of branch:
62
63
 
63
64
  - `--with-coverage` -- force the `test` step to run with coverage.
65
+ - `--coverage` -- the same thing, spelled as the positive of `--no-coverage`.
64
66
  - `--no-coverage` -- force it to run without coverage.
65
67
 
66
68
  If both are passed, `--no-coverage` wins.
@@ -81,6 +83,16 @@ Because the decision is branch-aware, the generated CI workflow runs plain `pnpm
81
83
 
82
84
  The generated `ci:check` script defaults to `pnpm check --skip 'docker:*'` since CI environments typically lack Docker support.
83
85
 
86
+ ### Type-aware linting
87
+
88
+ For `stack: "typescript"` repos, `repo:sync` generates a `lint:types` script (`bst lint:types`, backed by a managed `oxlint-tsgolint` devDependency) and inserts a `lint:types` step into `checks:run` right after `lint`. It runs `oxlint --type-aware` and selects its findings by subtracting a plain `oxlint` run from it -- there is no maintained list of type-aware rule names, so a rule oxlint adds lands the day it ships.
89
+
90
+ **Two phases, and the tooling's own version carries them.** The **report** phase prints every finding, states plainly that they will block once a later tooling release flips to **enforce**, and exits 0. The flip is one constant in the tooling's source, so it reaches every consumer as an ordinary `@bensandee/tooling` version bump -- no coordinated migration, nothing to remember. `.tooling.json#lint.strictTypeAware` (default `false`) lets a repo opt into blocking during the report phase, ahead of the fleet-wide flip.
91
+
92
+ **The whole default rule set ships, and the tooling excludes nothing.** A repo's own `oxlint.config.ts` is the per-rule control -- turning a rule off there (`"typescript/<rule>": "off"`) is honored by the type-aware run, and since plain `oxlint` ignores type-aware rules entirely, such an entry never weakens `pnpm lint`. In increasing order of bluntness: turn off one rule in `oxlint.config.ts`, or skip the whole step with `.tooling.json#checks.skip: ["lint:types"]`.
93
+
94
+ A missing `oxlint-tsgolint` binary degrades to a notice at exit 0 in the report phase, and fails only when `lint.strictTypeAware` is set -- a repo that has not armed enforcement should not have CI broken by a platform gap, and a repo that has asked for the gate should not silently lose it.
95
+
84
96
  ### Custom blocks in workflows
85
97
 
86
98
  Generated workflow files are regenerated on each `repo:sync` run. To preserve custom additions (extra steps, environment variables, etc.), wrap them in custom block markers:
@@ -125,24 +137,24 @@ Opt out entirely with `"pnpmWorkspace": false` in `.tooling.json`; the generator
125
137
 
126
138
  ### Repository setup
127
139
 
128
- | Command | Description |
129
- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
130
- | `tooling setup:secrets` | Configure CI secrets (Forgejo or GitHub). Auto-detects platform from `package.json` repository URL. Flags: `--dry-run`, `--no-docker`. |
131
- | `tooling forgejo:secrets` | Manage Forgejo Actions secrets directly. Subcommands: `set`, `list`, `delete`. |
140
+ | Command | Description |
141
+ | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
142
+ | `tooling setup:secrets` | Configure CI secrets (Forgejo or GitHub). Auto-detects platform from `package.json` repository URL. Flags: `--dry-run`, `--no-docker` (negates `--docker`). |
143
+ | `tooling forgejo:secrets` | Manage Forgejo Actions secrets directly. Subcommands: `set`, `list`, `delete`. |
132
144
 
133
145
  `setup:secrets` detects the hosting platform from the `repository` field in `package.json`. For Forgejo repositories, it prompts for username and password, creates an access token with appropriate scopes, then sets `RELEASE_TOKEN` (and Docker secrets if Docker packages are detected). For GitHub repositories, it prompts for an existing Personal Access Token and uses `gh secret set` to configure secrets.
134
146
 
135
147
  ### Release management
136
148
 
137
- | Command | Description |
138
- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
139
- | `tooling release:changesets` | Changesets version/publish for Forgejo CI. Flags: `--dry-run`, `--verbose`. Env: `FORGEJO_SERVER_URL`, `FORGEJO_REPOSITORY`, `RELEASE_TOKEN`. |
140
- | `tooling release:simple` | Streamlined release using commit-and-tag-version. Flags: `--release-as`, `--first-release`, `--prerelease`, `--verbose`. Env: `RELEASE_TOKEN` (required on every platform). |
141
- | `tooling release:trigger` | Trigger a release workflow. |
142
- | `tooling workflow:dispatch` | Dispatch a `workflow_dispatch` event for any workflow in any repo on the same git host. Flags: `--repo`, `--workflow` (required), `--ref`, `--input key=value` (repeatable). |
143
- | `tooling forgejo:create-release` | Create a Forgejo release from a tag. |
144
- | `tooling changesets:merge` | Merge a changesets version PR. Gated on a live read of the PR; refuses an empty version PR. |
145
- | `tooling webhook:send <url>` | POST a structured JSON release payload with HMAC-SHA256 auth. Flags: `--tag`, `--repository`, `--server-url`, `--actor`, `--prerelease`. Secret from `WEBHOOK_SECRET` env var. |
149
+ | Command | Description |
150
+ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
151
+ | `tooling release:changesets` | Changesets version/publish for Forgejo CI. Flags: `--dry-run`, `--verbose`. Env: `FORGEJO_SERVER_URL`, `FORGEJO_REPOSITORY`, `RELEASE_TOKEN`. |
152
+ | `tooling release:simple` | Streamlined release using commit-and-tag-version. Flags: `--release-as`, `--first-release`, `--prerelease`, `--verbose`. Env: `RELEASE_TOKEN` (required on every platform). |
153
+ | `tooling release:trigger` | Trigger a release workflow. |
154
+ | `tooling workflow:dispatch` | Dispatch a `workflow_dispatch` event for any workflow in any repo on the same git host. Flags: `--repo`, `--workflow` (required), `--ref`, `--input key=value` (repeatable). |
155
+ | `tooling forgejo:create-release` | Create a Forgejo release from a tag. |
156
+ | `tooling changesets:merge` | Merge a changesets version PR. Gated on a live read of the PR; refuses an empty version PR. On Forgejo, a first `mergeable: false` re-reads once after a short delay before refusing, since the forge reports `false` both for a conflict and while still checking. |
157
+ | `tooling webhook:send <url>` | POST a structured JSON release payload with HMAC-SHA256 auth. Flags: `--tag`, `--repository`, `--server-url`, `--actor`, `--prerelease`. Secret from `WEBHOOK_SECRET` env var. |
146
158
 
147
159
  #### `release:simple`
148
160
 
@@ -267,7 +279,7 @@ pnpm test:coverage -> coverage:summary -> coverage:record -> coverage:stat
267
279
 
268
280
  so every CI log ends with the trend table for the last 10 records.
269
281
 
270
- The history file (`coverage-history.jsonl`) lives on a dedicated `bst/coverage-metrics` orphan branch -- never on `main`. This sidesteps branch protection (no privileged token needed beyond `contents: write`) and keeps `git log main` clean. `coverage:record` uses pure git plumbing (`hash-object` -> `mktree` -> `commit-tree` -> `push --force-with-lease`) so the working tree is never touched, and concurrent CI runs are handled safely via compare-and-swap with retry.
282
+ The history file (`coverage-history.jsonl`) lives on a dedicated `bst/coverage-metrics` orphan branch -- never on `main`. This sidesteps branch protection (no privileged token needed beyond `contents: write`) and keeps `git log main` clean. The `contents: write` workflow permission is a GitHub concern: on Forgejo the generated workflow declares no `permissions` block at all, and the push is authorized by the runner token's default access (or an Authorized Integration, where an instance restricts it). `coverage:record` uses pure git plumbing (`hash-object` -> `mktree` -> `commit-tree` -> `push --force-with-lease`) so the working tree is never touched, and concurrent CI runs are handled safely via compare-and-swap with retry.
271
283
 
272
284
  #### The local ratchet
273
285
 
@@ -419,7 +431,7 @@ Each package is tagged independently using its own version, so packages in a mon
419
431
 
420
432
  | Host | Username secret | Password secret |
421
433
  | ------------------------- | --------------------------------- | --------------------------------- |
422
- | `code.orangebikelabs.com` | `FORGEJO_REGISTRY_USERNAME` | `FORGEJO_REGISTRY_PASSWORD` |
434
+ | `code.orangebikelabs.com` | `OBL_REGISTRY_USERNAME` | `OBL_REGISTRY_PASSWORD` |
423
435
  | `docker.io` | `DOCKERHUB_USERNAME` | `DOCKERHUB_PASSWORD` |
424
436
  | `quay.io` | `QUAY_USERNAME` | `QUAY_PASSWORD` |
425
437
  | `ghcr.io` | `GHCR_USERNAME` | `GHCR_PASSWORD` |
@@ -427,7 +439,9 @@ Each package is tagged independently using its own version, so packages in a mon
427
439
 
428
440
  When `package.json#repository` and `.tooling.json#registryHost` are both absent the generator falls back to neutral `REGISTRY_USERNAME` / `REGISTRY_PASSWORD`.
429
441
 
430
- This rename exists because Forgejo's [act fork auto-injects](https://code.forgejo.org/forgejo/runner/src/branch/main/act/runner/run_context.go#L1437) `secrets.DOCKER_USERNAME` / `secrets.DOCKER_PASSWORD` into every `container:` image pull regardless of the target registry, so reusing those generic names across multiple registries causes confusing "denied" / "incorrect username or password" errors. The registry-specific names defeat the auto-injection trigger.
442
+ Registry-specific names keep one credential per registry, so a repo publishing to more than one never has to decide which registry the generic pair belongs to.
443
+
444
+ **Names a forge will not accept.** A secret name must match `[A-Za-z_][A-Za-z0-9_]*` and must not start with `FORGEJO_`, `GITEA_`, `GITHUB_`, or a digit -- Forgejo answers `invalid secret name` and refuses to create it. The generator sanitizes every derived name against those rules, which is why `code.orangebikelabs.com` is `OBL_REGISTRY_*` rather than the `FORGEJO_*` the hostname would suggest, and why a host like `10.0.0.5:5000` or `forgejo.example.com` derives a `REGISTRY_`-prefixed name. `bst forgejo:secrets set` refuses an invalid name up front rather than passing it to the API.
431
445
 
432
446
  **Forgejo setup:** On Forgejo, the username secret is your Forgejo account username, and the password secret can reuse the same token as `RELEASE_TOKEN`. The token needs write permissions on the org, package, and repository scopes. These permissions should be set for the **user** if the package is under a user namespace (e.g. `bensandee`), or the **organization** if it's under an org namespace (e.g. `orangebikelabs`). After migrating, the old `DOCKER_USERNAME` / `DOCKER_PASSWORD` user/org-level secrets can be deleted once every consumer has been regenerated.
433
447