@mh-alikhani/bunready 0.1.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/CHANGELOG.md +140 -0
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/action.yml +89 -0
- package/docs/CONFIGURATION.md +44 -0
- package/docs/JSON-OUTPUT.md +50 -0
- package/docs/RELEASING.md +65 -0
- package/docs/adr/0001-data-source-policy.md +36 -0
- package/docs/adr/0002-rule-severity-model.md +42 -0
- package/docs/adr/0003-release-pipeline.md +51 -0
- package/docs/brand/favicon.svg +8 -0
- package/docs/brand/guidelines.md +70 -0
- package/docs/brand/logo-dark.svg +11 -0
- package/docs/brand/logo-mono.svg +11 -0
- package/docs/brand/logo.svg +11 -0
- package/docs/brand/mark.svg +8 -0
- package/docs/brand/tokens.json +74 -0
- package/docs/demo.md +37 -0
- package/package.json +71 -0
- package/src/cli/args.ts +177 -0
- package/src/cli/copy.ts +76 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/io.ts +20 -0
- package/src/cli/run.ts +98 -0
- package/src/cli/theme.ts +59 -0
- package/src/config/baseline.ts +116 -0
- package/src/config/config.ts +113 -0
- package/src/core/errors.ts +59 -0
- package/src/core/fs.ts +72 -0
- package/src/core/version.ts +9 -0
- package/src/report/human.ts +100 -0
- package/src/report/json.ts +11 -0
- package/src/report/sarif.ts +73 -0
- package/src/report/types.ts +114 -0
- package/src/rules/data/native-packages.json +81 -0
- package/src/rules/data/node-runtime.json +6 -0
- package/src/rules/install/engines.ts +74 -0
- package/src/rules/install/index.ts +27 -0
- package/src/rules/install/lifecycle-scripts.ts +70 -0
- package/src/rules/install/lockfile-presence.ts +68 -0
- package/src/rules/install/native-addon.ts +126 -0
- package/src/rules/run/index.ts +114 -0
- package/src/rules/runtime/builtins.ts +148 -0
- package/src/rules/runtime/index.ts +18 -0
- package/src/rules/severity.ts +46 -0
- package/src/scanner/execute.ts +301 -0
- package/src/scanner/graph.ts +77 -0
- package/src/scanner/lockfile.ts +545 -0
- package/src/scanner/manifest.ts +109 -0
- package/src/scanner/scan.ts +322 -0
- package/src/scanner/semver.ts +227 -0
- package/src/scanner/sources.ts +355 -0
- package/src/scanner/target.ts +224 -0
- package/src/scanner/workspaces.ts +170 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
Nothing yet.
|
|
11
|
+
|
|
12
|
+
## [0.1.0] - 2026-09-09
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Repository hygiene baseline: `.gitignore`, `.gitattributes`, `.editorconfig`,
|
|
17
|
+
`LICENSE` (MIT), `README.md`, `CONTRIBUTING.md`, `SECURITY.md`,
|
|
18
|
+
`CODE_OF_CONDUCT.md`.
|
|
19
|
+
- Git hooks via `simple-git-hooks`: `pre-commit` (Biome on staged files +
|
|
20
|
+
project typecheck) and `commit-msg` (commitlint, Conventional Commits).
|
|
21
|
+
- Brand system under `docs/brand/`: design tokens, logo variants, favicon,
|
|
22
|
+
guidelines, and voice/copy reference.
|
|
23
|
+
- Project scaffold: strict ESM `tsconfig.json`, Biome 2.x config, `bun:test`
|
|
24
|
+
suite, and the `src/{cli,core,rules,report}` layout.
|
|
25
|
+
- CLI skeleton (`src/cli/`): argument parsing, `--help`, `--version`, `--json`,
|
|
26
|
+
`NO_COLOR` handling, and the documented exit-code contract
|
|
27
|
+
(`0` clean, `1` blockers, `2` usage error).
|
|
28
|
+
- `src/core/errors.ts`: a `Result` type and an actionable error shape with a
|
|
29
|
+
`hint` field, so failures cross module boundaries as values rather than
|
|
30
|
+
thrown exceptions.
|
|
31
|
+
- Architecture decision records `0001` (data source policy) and `0002` (rule
|
|
32
|
+
severity model).
|
|
33
|
+
- **Scanning.** `bunready <path>` reads `package.json` and the lockfile and
|
|
34
|
+
prints a real report. The dependency graph is built from `bun.lock`,
|
|
35
|
+
`package-lock.json`, `yarn.lock` or `pnpm-lock.yaml`.
|
|
36
|
+
- Hand-written lockfile parsers (`src/scanner/lockfile.ts`), covering Bun's JSONC
|
|
37
|
+
text lockfile, npm v1/v2/v3, Yarn v1 and Berry, and pnpm. A lockfile that
|
|
38
|
+
cannot be parsed is reported as a finding instead of silently yielding an
|
|
39
|
+
empty graph.
|
|
40
|
+
- Install-phase rules (`src/rules/install/`): blocked lifecycle scripts
|
|
41
|
+
(`blocker`), native addons (`risk`), `engines` conflicts, and evidence gaps
|
|
42
|
+
such as a binary `bun.lockb` or a missing lockfile.
|
|
43
|
+
- Vendored native-package dataset (`src/rules/data/native-packages.json`); every
|
|
44
|
+
entry carries a source link, and entries assert a property of the package
|
|
45
|
+
rather than a Bun compatibility claim.
|
|
46
|
+
- Minimal semver range evaluation (`src/scanner/semver.ts`) for `engines`, which
|
|
47
|
+
reports "could not evaluate" instead of guessing.
|
|
48
|
+
- Report renderers: a human report (`src/report/human.ts`) and `--json`
|
|
49
|
+
(`src/report/json.ts`), with `ScanReport.stats` for context behind a verdict.
|
|
50
|
+
- Runtime-phase rule (`src/rules/runtime/`): the Node built-in modules the
|
|
51
|
+
repository's own code imports, reported as an inventory with a link to Bun's
|
|
52
|
+
compatibility table rather than as a verdict bunready cannot source.
|
|
53
|
+
- Import scanning (`src/scanner/sources.ts`): a regex-based extractor that masks
|
|
54
|
+
strings and comments first, so a fixture containing import-shaped text is not
|
|
55
|
+
counted as an import. The walk skips `node_modules` and build output, and
|
|
56
|
+
reports when it hits its file cap.
|
|
57
|
+
- `ScanReport.stats` now also carries the source-file count and the number of
|
|
58
|
+
Node built-ins found.
|
|
59
|
+
- Continuous integration: `.github/workflows/ci.yml` runs lint, typecheck, tests
|
|
60
|
+
with coverage and a build on Ubuntu, macOS and Windows, against both the
|
|
61
|
+
current Bun release and the exact floor `engines.bun` claims.
|
|
62
|
+
- `.github/workflows/security.yml`: gitleaks secret scanning, CodeQL code
|
|
63
|
+
scanning (`security-and-quality`) and `bun audit`, on pushes, pull requests and
|
|
64
|
+
a weekly schedule.
|
|
65
|
+
- `.github/dependabot.yml`: weekly updates for dev dependencies and for GitHub
|
|
66
|
+
Actions, so the SHA pins stay current.
|
|
67
|
+
- Live CI and security badges in the README.
|
|
68
|
+
- Release pipeline (`.github/workflows/release.yml`): a `v*` tag asserts CI is
|
|
69
|
+
green on that exact commit, compiles and smoke-tests Linux, macOS and Windows
|
|
70
|
+
binaries, publishes to npm with provenance over OIDC, and attaches the
|
|
71
|
+
binaries, `SHA256SUMS` and a CycloneDX SBOM to the GitHub release.
|
|
72
|
+
- `scripts/generate-sbom.ts` and `scripts/checksums.ts`, with the pure logic in
|
|
73
|
+
`scripts/lib/` so the artifact formats are tested rather than trusted.
|
|
74
|
+
- `docs/RELEASING.md` (process and prerequisites) and
|
|
75
|
+
`docs/adr/0003-release-pipeline.md` (why OIDC, binaries and a tag gate).
|
|
76
|
+
- `--run`: the target is copied to a temporary directory, dependencies are
|
|
77
|
+
installed there and its `start` (or `test`) script is booted with `bun run`.
|
|
78
|
+
The first real failure is captured with its stack frames. Nothing is executed
|
|
79
|
+
in place, every command is timed, and the copy is removed even when the run
|
|
80
|
+
fails.
|
|
81
|
+
- `bunready.config.json` (`src/config/`): ignore findings by rule id or package,
|
|
82
|
+
allowlist native addons, exclude paths, raise or lower `failOn`, and choose the
|
|
83
|
+
script and copy limit `--run` uses. `--config` points elsewhere; a missing file
|
|
84
|
+
is an error rather than a silent default. See `docs/CONFIGURATION.md`.
|
|
85
|
+
- `--json` now carries `schemaVersion` and `failOn`, so a CI job can pin the
|
|
86
|
+
contract and see which threshold produced the exit code.
|
|
87
|
+
See `docs/JSON-OUTPUT.md`.
|
|
88
|
+
- `--sarif` writes SARIF 2.1.0 for code-scanning upload, generated from the same
|
|
89
|
+
findings as every other renderer.
|
|
90
|
+
- `--run-script <name>` picks the script to boot, and `--run` refuses a target
|
|
91
|
+
larger than `run.maxCopyMegabytes` instead of silently skipping it.
|
|
92
|
+
- Workspace support: `workspaces` in package.json or a `pnpm-workspace.yaml` is
|
|
93
|
+
detected, every package is scanned, the report aggregates them with a `targets`
|
|
94
|
+
list and a `path` on each finding, and `--scope` narrows a scan to the matching
|
|
95
|
+
packages. Globs are expanded with a small, documented matcher.
|
|
96
|
+
- Baseline and regression detection: `--write-baseline` records the findings you
|
|
97
|
+
have accepted, `--baseline` compares against them, marks the rest `new` and
|
|
98
|
+
fails the run only for those. The fingerprint is rule + package + path.
|
|
99
|
+
- `action.yml`: a composite GitHub Action that scans a repository, uploads the
|
|
100
|
+
SARIF report to code scanning and fails the step on findings at or above
|
|
101
|
+
`failOn`. `version: local` runs the action from source, which is how CI tests
|
|
102
|
+
it before the first publish.
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
|
|
106
|
+
- `.github/workflows/release.yml` was invalid YAML: a secrets expression had been
|
|
107
|
+
written as a literal `*` alias, and GitHub does not fail loudly for that - it
|
|
108
|
+
simply refuses to run the workflow. `ci.yml` now parses every workflow file on
|
|
109
|
+
each pull request so the class of break cannot reach `main` again.
|
|
110
|
+
- `actions/checkout` to v7, `gitleaks/gitleaks-action` to v3, `github/codeql-action`
|
|
111
|
+
init and analyze to v4 (they must move together), `@commitlint/*` to 21 and
|
|
112
|
+
TypeScript to 7, with a regenerated lockfile.
|
|
113
|
+
- `engines.bun` now declares `>=1.4.0`. The previous `>=1.2.0` claim was wrong:
|
|
114
|
+
the committed `bun.lock` is text lockfile format version 2, which Bun 1.2 and
|
|
115
|
+
1.3 reject with an unknown-lockfile-version error. The CI matrix caught it on
|
|
116
|
+
its first run.
|
|
117
|
+
- Scanning bunready itself now exits `0`. The `install/lifecycle-script` finding
|
|
118
|
+
about `simple-git-hooks` was true, so the fix was to list it in
|
|
119
|
+
`trustedDependencies` rather than to weaken the rule.
|
|
120
|
+
- Workspace glob expansion: `packages/*` and `packages/**` are expanded against
|
|
121
|
+
the correct parent directory, bounded in depth, and never walk `node_modules`.
|
|
122
|
+
`finding.path` is also normalised to forward slashes on every platform.
|
|
123
|
+
|
|
124
|
+
### Changed
|
|
125
|
+
|
|
126
|
+
- A scan now produces a verdict and an exit code.
|
|
127
|
+
- `--run` executes the target's own script in a temporary copy instead of
|
|
128
|
+
reporting itself as unimplemented.
|
|
129
|
+
- Paths in findings print with forward slashes, so output is identical on every
|
|
130
|
+
operating system.
|
|
131
|
+
|
|
132
|
+
### Notes
|
|
133
|
+
|
|
134
|
+
- `src/rules/data/node-runtime.json` ships an empty `gaps` list on purpose: each
|
|
135
|
+
entry would assert that a specific Node built-in is partial or missing in Bun,
|
|
136
|
+
and that claim needs a primary source. Until then the rule reports what the
|
|
137
|
+
repository imports and cites the compatibility table.
|
|
138
|
+
|
|
139
|
+
[Unreleased]: https://github.com/MHAlikhani/bunready/compare/v0.1.0...HEAD
|
|
140
|
+
[0.1.0]: https://github.com/MHAlikhani/bunready/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohammad Hosein Alikhani
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="docs/brand/logo.svg" alt="bunready" width="360">
|
|
4
|
+
|
|
5
|
+
**One command that tells you what will break before you move a Node/TS repo to Bun — and gives you one clear verdict.**
|
|
6
|
+
|
|
7
|
+
[](https://github.com/MHAlikhani/bunready/actions/workflows/ci.yml)
|
|
8
|
+
[](https://github.com/MHAlikhani/bunready/actions/workflows/security.yml)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://bun.sh)
|
|
11
|
+
[](tsconfig.json)
|
|
12
|
+
[](#status)
|
|
13
|
+
|
|
14
|
+
[](https://www.npmjs.com/package/@mh-alikhani/bunready)
|
|
15
|
+
-->
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Status
|
|
22
|
+
|
|
23
|
+
Pre-alpha, and precise about it. `bunready <path>` reads `package.json`, any
|
|
24
|
+
lockfile, and the repository's own imports, then prints a report with a verdict
|
|
25
|
+
and an exit code CI can gate on.
|
|
26
|
+
|
|
27
|
+
What it does not do yet: `--run` (executing the target's scripts under Bun) is
|
|
28
|
+
accepted and reported as unimplemented, and no Node-runtime gap is claimed
|
|
29
|
+
without a source. See [STATE.md](STATE.md) for what exists and what does not.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
bunx @mh-alikhani/bunready . # run without installing
|
|
35
|
+
bun add --global @mh-alikhani/bunready
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or from a checkout:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
git clone https://github.com/MHAlikhani/bunready.git
|
|
42
|
+
cd bunready
|
|
43
|
+
bun install
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
bunx @mh-alikhani/bunready --help
|
|
50
|
+
|
|
51
|
+
# against a target repository
|
|
52
|
+
bunx @mh-alikhani/bunready /path/to/node-project
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| Flag | Meaning |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `--help` | Print usage and exit. |
|
|
58
|
+
| `--version` | Print the CLI version and exit. |
|
|
59
|
+
| `--json` | Emit machine-readable JSON instead of the terminal report. |
|
|
60
|
+
| `--run` | Planned: run the target's own scripts under Bun in a temporary copy. Not implemented yet; the flag says so before scanning. |
|
|
61
|
+
| `NO_COLOR` | Environment variable: disable ANSI color when set. |
|
|
62
|
+
|
|
63
|
+
Exit codes: `0` no blockers, `1` blockers found, `2` usage error.
|
|
64
|
+
|
|
65
|
+
## Monorepos
|
|
66
|
+
|
|
67
|
+
A `workspaces` field in `package.json`, or a `pnpm-workspace.yaml`, is detected
|
|
68
|
+
automatically: the root and every package are scanned, and the report aggregates
|
|
69
|
+
them with a `targets` list and a `path` on each finding. `--scope packages/api`
|
|
70
|
+
narrows a scan to the matching packages.
|
|
71
|
+
|
|
72
|
+
## Baselines
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
bunready . --write-baseline bunready.baseline.json # accept today's findings
|
|
76
|
+
bunready . --baseline bunready.baseline.json # fail only on new ones
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A baseline records rule, package and path - not the message - so rewording a
|
|
80
|
+
finding does not resurrect it.
|
|
81
|
+
|
|
82
|
+
## In CI
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
permissions:
|
|
86
|
+
contents: read
|
|
87
|
+
security-events: write # required for the SARIF upload
|
|
88
|
+
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v7
|
|
91
|
+
- uses: MHAlikhani/bunready@v0.1.0
|
|
92
|
+
with:
|
|
93
|
+
path: .
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The action writes a JSON report, uploads the SARIF report to code scanning and
|
|
97
|
+
fails the step when findings at or above `failOn` exist. Inputs: `path`,
|
|
98
|
+
`version` (`latest` or `local`), `sarif-file`, `json-file`, `upload`.
|
|
99
|
+
|
|
100
|
+
## Why
|
|
101
|
+
|
|
102
|
+
Moving a repo to Bun is usually a pile of small unknowns: which npm lifecycle
|
|
103
|
+
scripts actually run, which APIs are partial, which packages are native, which
|
|
104
|
+
test runner behaviours differ. bunready answers that with evidence and a single
|
|
105
|
+
verdict instead of a checklist you have to interpret yourself.
|
|
106
|
+
|
|
107
|
+
## Compatibility data policy
|
|
108
|
+
|
|
109
|
+
Compatibility claims come only from public Bun documentation and issue tracker
|
|
110
|
+
entries, shipped as versioned JSON with source links. We never invent
|
|
111
|
+
compatibility facts. See [ADR 0001](docs/adr/0001-data-source-policy.md).
|
|
112
|
+
|
|
113
|
+
## Contributing
|
|
114
|
+
|
|
115
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) and the
|
|
116
|
+
[Code of Conduct](CODE_OF_CONDUCT.md). Security reports go through
|
|
117
|
+
[SECURITY.md](SECURITY.md).
|
|
118
|
+
|
|
119
|
+
## Disclaimer
|
|
120
|
+
|
|
121
|
+
bunready is an independent, community project. It is **not affiliated with,
|
|
122
|
+
endorsed by, or sponsored by the Bun project, Oven**. "Bun" and
|
|
123
|
+
the Bun logo are trademarks of their respective owners and are used here only
|
|
124
|
+
for descriptive, nominative purposes. bunready ships no Bun code and no Bun
|
|
125
|
+
branding.
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
[MIT](LICENSE) © Mohammad Hosein Alikhani
|
package/action.yml
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
name: bunready
|
|
2
|
+
description: Report what will break when a Node or TypeScript repository moves to Bun, and upload the result to code scanning.
|
|
3
|
+
author: MHAlikhani
|
|
4
|
+
branding:
|
|
5
|
+
icon: check-circle
|
|
6
|
+
color: orange
|
|
7
|
+
|
|
8
|
+
inputs:
|
|
9
|
+
path:
|
|
10
|
+
description: Repository to scan.
|
|
11
|
+
required: false
|
|
12
|
+
default: "."
|
|
13
|
+
version:
|
|
14
|
+
description: bunready version to run from npm, or "local" to use the source inside this action.
|
|
15
|
+
required: false
|
|
16
|
+
default: latest
|
|
17
|
+
sarif-file:
|
|
18
|
+
description: Where to write the SARIF report.
|
|
19
|
+
required: false
|
|
20
|
+
default: bunready.sarif
|
|
21
|
+
json-file:
|
|
22
|
+
description: Where to write the JSON report.
|
|
23
|
+
required: false
|
|
24
|
+
default: bunready.json
|
|
25
|
+
upload:
|
|
26
|
+
description: Upload the SARIF report to GitHub code scanning.
|
|
27
|
+
required: false
|
|
28
|
+
default: "true"
|
|
29
|
+
|
|
30
|
+
outputs:
|
|
31
|
+
exit-code:
|
|
32
|
+
description: 0 when nothing at or above failOn was found, 1 otherwise.
|
|
33
|
+
value: ${{ steps.scan.outputs.exit-code }}
|
|
34
|
+
verdict:
|
|
35
|
+
description: ready, risky or blocked.
|
|
36
|
+
value: ${{ steps.scan.outputs.verdict }}
|
|
37
|
+
|
|
38
|
+
runs:
|
|
39
|
+
using: composite
|
|
40
|
+
steps:
|
|
41
|
+
- name: Set up Bun
|
|
42
|
+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
43
|
+
with:
|
|
44
|
+
bun-version: latest
|
|
45
|
+
|
|
46
|
+
# The scan itself never fails the step: the SARIF report has to be uploaded
|
|
47
|
+
# before the exit code is honoured, or code scanning would never see the
|
|
48
|
+
# findings of a failing run.
|
|
49
|
+
- name: Scan
|
|
50
|
+
id: scan
|
|
51
|
+
shell: bash
|
|
52
|
+
env:
|
|
53
|
+
INPUT_VERSION: ${{ inputs.version }}
|
|
54
|
+
INPUT_PATH: ${{ inputs.path }}
|
|
55
|
+
INPUT_SARIF: ${{ inputs.sarif-file }}
|
|
56
|
+
INPUT_JSON: ${{ inputs.json-file }}
|
|
57
|
+
run: |
|
|
58
|
+
set -euo pipefail
|
|
59
|
+
|
|
60
|
+
if [ "${INPUT_VERSION}" = "local" ]; then
|
|
61
|
+
scan() { bun run "${GITHUB_ACTION_PATH}/src/cli/index.ts" "$@"; }
|
|
62
|
+
else
|
|
63
|
+
scan() { bunx --bun "@mh-alikhani/bunready@${INPUT_VERSION}" "$@"; }
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
set +e
|
|
67
|
+
scan "${INPUT_PATH}" --json > "${INPUT_JSON}"
|
|
68
|
+
status=$?
|
|
69
|
+
set -e
|
|
70
|
+
|
|
71
|
+
verdict=$(node -e 'const fs=require("node:fs");process.stdout.write(JSON.parse(fs.readFileSync(process.argv[1],"utf8")).verdict)' "${INPUT_JSON}")
|
|
72
|
+
|
|
73
|
+
printf 'exit-code=%s\n' "${status}" >> "${GITHUB_OUTPUT}"
|
|
74
|
+
printf 'verdict=%s\n' "${verdict}" >> "${GITHUB_OUTPUT}"
|
|
75
|
+
printf 'bunready: %s (exit %s)\n' "${verdict}" "${status}" >> "${GITHUB_STEP_SUMMARY}"
|
|
76
|
+
|
|
77
|
+
scan "${INPUT_PATH}" --sarif > "${INPUT_SARIF}"
|
|
78
|
+
|
|
79
|
+
- name: Upload SARIF
|
|
80
|
+
if: ${{ inputs.upload == 'true' }}
|
|
81
|
+
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4
|
|
82
|
+
with:
|
|
83
|
+
sarif_file: ${{ inputs.sarif-file }}
|
|
84
|
+
category: bunready
|
|
85
|
+
|
|
86
|
+
- name: Fail when bunready reported findings at or above failOn
|
|
87
|
+
if: ${{ steps.scan.outputs.exit-code != '0' }}
|
|
88
|
+
shell: bash
|
|
89
|
+
run: exit 1
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
`bunready.config.json` in the scanned repository root. Every key is optional; the
|
|
4
|
+
defaults are what you get when the file is absent. `--config <path>` points at a
|
|
5
|
+
different file, and a path that does not exist is an error rather than a silent
|
|
6
|
+
fallback.
|
|
7
|
+
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"ignore": ["install/no-lockfile"],
|
|
11
|
+
"ignorePackages": ["fsevents"],
|
|
12
|
+
"nativeAllowlist": ["sharp"],
|
|
13
|
+
"excludePaths": ["fixtures/"],
|
|
14
|
+
"failOn": "risk",
|
|
15
|
+
"run": { "script": "test", "maxCopyMegabytes": 100 }
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| Key | Type | Default | Effect |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| `ignore` | string[] | `[]` | Drops findings by rule id, e.g. `install/no-lockfile`. |
|
|
22
|
+
| `ignorePackages` | string[] | `[]` | Drops findings whose `package` field names one of these dependencies. |
|
|
23
|
+
| `nativeAllowlist` | string[] | `[]` | Packages the native-addon rule never reports. |
|
|
24
|
+
| `excludePaths` | string[] | `[]` | Substring match against source paths; a match skips the file in the import scan. |
|
|
25
|
+
| `failOn` | `blocker` \| `risk` \| `info` | `blocker` | Lowest severity that makes the process exit `1`. |
|
|
26
|
+
| `run.script` | string | first of `start`, `test` | Script booted by `--run`. |
|
|
27
|
+
| `run.maxCopyMegabytes` | number | `250` | Refuses to copy a repository larger than this. Reported as a `risk`, never a silent skip. |
|
|
28
|
+
|
|
29
|
+
## What configuration cannot do
|
|
30
|
+
|
|
31
|
+
It cannot change what a rule *claims*. `ignore` and `ignorePackages` suppress a
|
|
32
|
+
finding you have decided is acceptable; they do not rewrite the evidence, and the
|
|
33
|
+
report still exits `0` only because you said so. That is the intended trade: the
|
|
34
|
+
tool stays honest about the repository, and the repository stays honest about
|
|
35
|
+
what it has accepted.
|
|
36
|
+
|
|
37
|
+
`failOn` is echoed in the `--json` report as `failOn`, so a CI log always shows
|
|
38
|
+
which threshold produced the exit code.
|
|
39
|
+
|
|
40
|
+
## Precedence
|
|
41
|
+
|
|
42
|
+
1. CLI flags (`--run-script`, `--config`)
|
|
43
|
+
2. `bunready.config.json` in the target
|
|
44
|
+
3. Built-in defaults
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# JSON output
|
|
2
|
+
|
|
3
|
+
`bunready <path> --json` writes a `ScanReport` to stdout and nothing else, so it
|
|
4
|
+
can be piped straight into `jq` or a CI step.
|
|
5
|
+
|
|
6
|
+
```jsonc
|
|
7
|
+
{
|
|
8
|
+
"schemaVersion": 1, // bumped only for a breaking change
|
|
9
|
+
"failOn": "blocker", // the threshold that decides the exit code
|
|
10
|
+
"tool": "bunready",
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"target": "/path/to/repo",
|
|
13
|
+
"verdict": "blocked", // ready | risky | blocked
|
|
14
|
+
"counts": { "blocker": 1, "risk": 0, "info": 1 },
|
|
15
|
+
"findings": [
|
|
16
|
+
{
|
|
17
|
+
"id": "install/lifecycle-script", // stable rule id
|
|
18
|
+
"severity": "blocker", // blocker | risk | info
|
|
19
|
+
"title": "…",
|
|
20
|
+
"detail": "…",
|
|
21
|
+
"package": "sharp", // present when the finding is about one dependency
|
|
22
|
+
"evidence": "…", // what was observed, never a guess
|
|
23
|
+
"hint": "…", // the next step
|
|
24
|
+
"source": "https://bun.com/docs/pm/lifecycle" // required for compat claims
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"stats": { "…": "what the scan looked at" },
|
|
28
|
+
"run": { "script": "test", "exitCode": 0 } // only with --run
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Compatibility
|
|
33
|
+
|
|
34
|
+
`schemaVersion` is the contract. A patch or minor release of bunready never
|
|
35
|
+
changes the meaning of an existing field; a field may be added, and consumers
|
|
36
|
+
should ignore unknown fields. A rename or removal bumps `schemaVersion`.
|
|
37
|
+
|
|
38
|
+
## Exit codes
|
|
39
|
+
|
|
40
|
+
| Code | Meaning |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `0` | Nothing at or above `failOn`. |
|
|
43
|
+
| `1` | At least one finding at or above `failOn`. |
|
|
44
|
+
| `2` | Usage error, or the scan could not complete (no `--json` is written in this case). |
|
|
45
|
+
|
|
46
|
+
## Machine-readable variants
|
|
47
|
+
|
|
48
|
+
- `--json` - this document.
|
|
49
|
+
- `--sarif` - SARIF 2.1.0 for code-scanning upload. The two are mutually
|
|
50
|
+
exclusive; passing both is a usage error rather than an ambiguous stream.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
Two things must be true before a release can happen, and one of them is not in
|
|
4
|
+
this repository.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
1. **CI is green on the commit you are tagging.** The release workflow checks
|
|
9
|
+
this itself and fails closed: it queries the `ci` workflow for a successful
|
|
10
|
+
run on exactly that SHA. If CI is red or has not run, there is no release.
|
|
11
|
+
2. **Trusted publishing is configured on npmjs.com** (owner action, done once).
|
|
12
|
+
Open the `@mh-alikhani/bunready` package settings on npmjs.com, add a trusted publisher
|
|
13
|
+
pointing at this repository, the `release.yml` workflow file, and the `release`
|
|
14
|
+
environment if you use one. Without this, the publish job fails with an
|
|
15
|
+
authentication error — by design, because the alternative would be a
|
|
16
|
+
long-lived token in the repository.
|
|
17
|
+
|
|
18
|
+
No `NPM_TOKEN` secret exists or is needed. Authentication is OIDC.
|
|
19
|
+
|
|
20
|
+
## Cutting a release
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
# 1. Make sure main is where you want it, and CI is green
|
|
24
|
+
gh run list --workflow ci.yml --limit 3
|
|
25
|
+
|
|
26
|
+
# 2. Bump the version and move the changelog entry out of Unreleased
|
|
27
|
+
# (package.json + CHANGELOG.md), then land it through a pull request
|
|
28
|
+
|
|
29
|
+
# 3. Tag the release commit and push the tag
|
|
30
|
+
git tag -a v0.1.0 -m "bunready v0.1.0"
|
|
31
|
+
git push origin v0.1.0
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The tag is the release. `release.yml` then:
|
|
35
|
+
|
|
36
|
+
| Step | What it does |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `verify` | Refuses to continue unless `ci` succeeded on this SHA. |
|
|
39
|
+
| `binaries` | Compiles and smoke-tests `bunready-linux-x64`, `bunready-darwin-arm64`, `bunready-windows-x64.exe`. |
|
|
40
|
+
| `publish` | `npm publish --provenance --access public` over OIDC. |
|
|
41
|
+
| `release` | Generates `bom.json` (CycloneDX) and `SHA256SUMS`, then attaches all five files to the GitHub release with generated notes. |
|
|
42
|
+
|
|
43
|
+
## Verifying a release as a user
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
gh release download v0.1.0 --repo MHAlikhani/bunready
|
|
47
|
+
sha256sum -c SHA256SUMS
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`bom.json` lists every locked package with a `purl`, so the dependency set behind
|
|
51
|
+
a published version can be read without cloning anything.
|
|
52
|
+
|
|
53
|
+
## Versioning
|
|
54
|
+
|
|
55
|
+
Semantic versioning. Until `1.0.0`, a minor bump means new rules, a patch bump
|
|
56
|
+
means fixes to existing behaviour, and any change to the CLI contract (exit
|
|
57
|
+
codes, `--json` fields) gets called out under `### Changed`.
|
|
58
|
+
|
|
59
|
+
## What is deliberately missing
|
|
60
|
+
|
|
61
|
+
- **Code signing.** macOS and Windows binaries are unsigned for `v0.1.0`; users
|
|
62
|
+
will see Gatekeeper and SmartScreen warnings. Signing needs certificates and a
|
|
63
|
+
budget decision, not just code.
|
|
64
|
+
- **A coverage badge.** Auto-committing one would require write access to
|
|
65
|
+
protected `main`; see D23 in STATE.md.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ADR 0001 - Compatibility data source policy
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-15
|
|
5
|
+
- **Context:** `bunready`'s only value is that its verdict can be trusted.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
Compatibility claims ship as versioned JSON in `src/rules/data/`, and every
|
|
10
|
+
entry carries `source`, a link to the public Bun documentation or issue that
|
|
11
|
+
establishes the claim. The dataset records the Bun version range it was
|
|
12
|
+
validated against.
|
|
13
|
+
|
|
14
|
+
Rules may also report **observed facts about the scanned repository** (a native
|
|
15
|
+
addon is present, a lifecycle script exists, an import resolves to a Node
|
|
16
|
+
built-in). Those need no external source: the evidence is the repo itself and is
|
|
17
|
+
printed with the finding.
|
|
18
|
+
|
|
19
|
+
If a claim has neither a source link nor repo evidence, it does not ship.
|
|
20
|
+
|
|
21
|
+
## Consequences
|
|
22
|
+
|
|
23
|
+
- The dataset is reviewable: a reviewer can open every link.
|
|
24
|
+
- Adding a rule requires a source, which keeps rule authorship slow on purpose.
|
|
25
|
+
- The dataset is versioned with the code; the report states which dataset
|
|
26
|
+
version produced the verdict, so an old verdict stays explainable.
|
|
27
|
+
|
|
28
|
+
## Rejected alternatives
|
|
29
|
+
|
|
30
|
+
- **Bundling an upstream compatibility list without links.** Unverifiable, ages
|
|
31
|
+
badly, and makes us the authority on someone else's runtime.
|
|
32
|
+
- **Asking the network at scan time.** `bunready` must work offline and must
|
|
33
|
+
never phone home. Data is vendored, not fetched.
|
|
34
|
+
- **Inferring compatibility from package metadata alone.** Would produce
|
|
35
|
+
confident-sounding guesses; the guardrail is "never invent compatibility
|
|
36
|
+
facts".
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# ADR 0002 - Rule severity model
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-15
|
|
5
|
+
- **Context:** The report has to be actionable, and CI has to be able to gate on
|
|
6
|
+
it, without turning a migration into a wall of red.
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
Every finding carries exactly one severity:
|
|
11
|
+
|
|
12
|
+
| Severity | Meaning | Exit code effect |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| `blocker` | The target repo cannot run correctly on Bun until this is fixed. | Process exits `1`. |
|
|
15
|
+
| `risk` | A real hazard that needs a human judgement call; may still be fine. | No effect. |
|
|
16
|
+
| `info` | Context that helps the reader decide. | No effect. |
|
|
17
|
+
|
|
18
|
+
Exit codes are fixed: `0` no blockers, `1` blockers found, `2` usage error or
|
|
19
|
+
incomplete scan.
|
|
20
|
+
|
|
21
|
+
Severity is decided by one question: **if the user does nothing, does this break
|
|
22
|
+
at runtime or at install time?** Yes, and unavoidable -> `blocker`. Yes, but
|
|
23
|
+
conditional on how the code is used -> `risk`. No -> `info`.
|
|
24
|
+
|
|
25
|
+
Colour is presentation only. Every severity prints as a word, so `NO_COLOR`,
|
|
26
|
+
monochrome terminals, and log files keep the full meaning.
|
|
27
|
+
|
|
28
|
+
## Consequences
|
|
29
|
+
|
|
30
|
+
- CI gating is a one-line rule (`exit != 0`) and cannot be accidentally
|
|
31
|
+
configured into a permanent failure by a `risk`.
|
|
32
|
+
- `blocker` is a strong claim, so the bar for assigning it is deliberately high.
|
|
33
|
+
- The severity of every rule must be justified against the single question above
|
|
34
|
+
in review.
|
|
35
|
+
|
|
36
|
+
## Rejected alternatives
|
|
37
|
+
|
|
38
|
+
- **A numeric score.** Implies precision the model does not have, and invites
|
|
39
|
+
threshold arguments instead of reading the findings.
|
|
40
|
+
- **`error` / `warning`.** Borrows linter vocabulary without the run-time
|
|
41
|
+
meaning this tool is about.
|
|
42
|
+
- **Letting `risk` fail CI.** Would make the tool unusable on any real repo.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# ADR 0003 - Release pipeline
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-08-26
|
|
5
|
+
- **Context:** bunready is a CLI that people will run on their own machines, so
|
|
6
|
+
"trust the release" has to be something a stranger can verify, not a promise.
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
A release is a tag. Pushing `v*` starts `.github/workflows/release.yml`, which:
|
|
11
|
+
|
|
12
|
+
1. **Asserts CI is green on the tagged commit.** A tag can point at anything, so
|
|
13
|
+
the pipeline queries the `ci` workflow for a successful run on exactly that
|
|
14
|
+
SHA and refuses to continue otherwise.
|
|
15
|
+
2. **Publishes to npm with provenance** using OIDC (`id-token: write`) instead of
|
|
16
|
+
a long-lived `NPM_TOKEN`. npm signs a statement that the published bytes came
|
|
17
|
+
from this repository at this commit, verifiable on the registry.
|
|
18
|
+
3. **Builds a compiled binary per platform** (`bun build --compile` for
|
|
19
|
+
linux-x64, darwin-arm64, windows-x64) and smoke-tests each one by running
|
|
20
|
+
`--version` before it is allowed to become an artifact. An unsigned binary
|
|
21
|
+
that does not run is not a release artifact.
|
|
22
|
+
4. **Publishes an SBOM and checksums.** `dist/bom.json` is CycloneDX 1.5,
|
|
23
|
+
generated from `bun.lock` with bunready's own parser; `dist/SHA256SUMS` uses
|
|
24
|
+
the `sha256sum` format so verification needs no special tooling.
|
|
25
|
+
|
|
26
|
+
Every action is pinned to a commit SHA. The only job with write access to the
|
|
27
|
+
repository is the one attaching files to the release; only the publish job can
|
|
28
|
+
mint an OIDC token.
|
|
29
|
+
|
|
30
|
+
## Consequences
|
|
31
|
+
|
|
32
|
+
- There is no npm token in this repository to leak or rotate.
|
|
33
|
+
- The first release requires one manual step outside the repository: registering
|
|
34
|
+
the trusted publisher on npmjs.com (recorded as O10 in STATE.md). Until that is
|
|
35
|
+
done, the publish job fails closed rather than publishing anonymously.
|
|
36
|
+
- Binaries are unsigned. Users on macOS and Windows will see the usual warnings;
|
|
37
|
+
code signing is deliberately out of scope for `v0.1.0`.
|
|
38
|
+
- The SBOM inherits the lockfile parser's correctness. That is intentional: the
|
|
39
|
+
same parser produces the scanner's dependency graph, so a parser bug shows up
|
|
40
|
+
in both places instead of hiding in one.
|
|
41
|
+
|
|
42
|
+
## Rejected alternatives
|
|
43
|
+
|
|
44
|
+
- **Publish on every push to main.** No review point, and a bad commit becomes a
|
|
45
|
+
bad published version with no way back.
|
|
46
|
+
- **A long-lived `NPM_TOKEN` secret.** Simplest to set up and the most valuable
|
|
47
|
+
thing in the repository to steal.
|
|
48
|
+
- **Ship TypeScript only.** The `bin` needs a runtime with TypeScript support;
|
|
49
|
+
compiled binaries are what a user with no Bun installed can actually try.
|
|
50
|
+
- **A hand-written Release notes file.** `--generate-notes` keeps the changelog
|
|
51
|
+
in the commits, where CI already enforces its shape.
|