react-native-doctor-ci 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amrith Vengalath
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,196 @@
1
+ # react-native-doctor-ci
2
+
3
+ Fail pull requests that add abandoned, non-New-Architecture, or npm-deprecated React Native dependencies — with inline annotations and a policy-as-code allowlist.
4
+
5
+ ![PR annotation: rn-doctor fails a pull request on the exact package.json line that adds a dying dependency](https://raw.githubusercontent.com/AmrithVengalath/react-native-doctor-ci/main/docs/assets/pr-annotation.png)
6
+
7
+ [![npm version](https://img.shields.io/npm/v/react-native-doctor-ci)](https://www.npmjs.com/package/react-native-doctor-ci)
8
+ [![CI](https://github.com/AmrithVengalath/react-native-doctor-ci/actions/workflows/ci.yml/badge.svg)](https://github.com/AmrithVengalath/react-native-doctor-ci/actions/workflows/ci.yml)
9
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
+ ![node >= 20](https://img.shields.io/badge/node-%3E%3D20-brightgreen)
11
+
12
+ ## Why
13
+
14
+ Unmaintained dependencies are a perennial top-5 pain in React Native surveys, and the cost keeps climbing now that the New Architecture is the default: a package that looked fine when it was added quietly stops getting releases, never gains New Architecture support, or gets deprecated on npm — and you find out during an upgrade, months after the PR that introduced it merged.
15
+
16
+ Lookup tools exist (the React Native Directory tells you a package's status if you go ask). What's been missing is **enforcement**: the check that runs on every PR, fails when someone adds a dying dependency, and points at the exact line — so the conversation happens at review time, when swapping the package costs five minutes instead of a quarter.
17
+
18
+ `rn-doctor` is that check. Pure TypeScript, zero native code, one command.
19
+
20
+ ## Quickstart
21
+
22
+ ### GitHub Action (recommended)
23
+
24
+ Create `.github/workflows/rn-doctor.yml`:
25
+
26
+ ```yaml
27
+ name: Dependency health
28
+
29
+ on:
30
+ pull_request:
31
+
32
+ permissions:
33
+ contents: read
34
+
35
+ jobs:
36
+ rn-doctor:
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@v7
40
+ with:
41
+ fetch-depth: 0 # required for changed-only (merge-base diff)
42
+
43
+ - uses: AmrithVengalath/react-native-doctor-ci/action@v0.1.0
44
+ with:
45
+ changed-only: "true"
46
+ base: origin/${{ github.base_ref }}
47
+ ```
48
+
49
+ That's the whole setup. PRs that add or upgrade a dependency failing your policy get a red check with an inline annotation on the offending `package.json` line. A ready-to-copy workflow and a fully commented policy file live in [`example/`](example/).
50
+
51
+ ### CLI
52
+
53
+ Run it directly in any project with a `package.json`:
54
+
55
+ ```sh
56
+ npx --yes --package react-native-doctor-ci rn-doctor
57
+ ```
58
+
59
+ (The package name and the binary name differ, hence `--package`. Once installed as a devDependency, it's just `rn-doctor`.)
60
+
61
+ ## GitHub Action reference
62
+
63
+ ```yaml
64
+ - uses: AmrithVengalath/react-native-doctor-ci/action@v0.1.0
65
+ with:
66
+ version: "0.1.0" # npm version/dist-tag to run (default: latest)
67
+ policy: ".rn-doctor.yml" # policy file path (default: auto-detect)
68
+ changed-only: "true" # only deps added/changed vs base (default: "false")
69
+ base: origin/main # base ref for changed-only (default: origin/main)
70
+ workspaces: "false" # also check workspace package.jsons
71
+ annotations: "true" # inline GitHub annotations (default: "true")
72
+ token: ${{ github.token }} # for GitHub repo enrichment (default: workflow token)
73
+ working-directory: "." # where package.json lives
74
+ node-version: "22" # Node to set up (rn-doctor needs >= 20)
75
+ ```
76
+
77
+ Notes:
78
+
79
+ - `changed-only` requires `actions/checkout` with `fetch-depth: 0` — the diff is computed against the merge-base of the PR branch and `base`, same as GitHub's three-dot compare. Without history the run fails with an actionable message telling you exactly that.
80
+ - The default `token` (the workflow's own `GITHUB_TOKEN`) is enough; it's only used to read public repo metadata (archived state, last push). Without any token those fields degrade to `unknown` with a warning — the run never fails because of rate limits.
81
+ - Pin `version` for reproducible CI; `latest` is convenient but floats.
82
+
83
+ ## CLI reference
84
+
85
+ ```
86
+ rn-doctor [options]
87
+
88
+ --json Machine-readable JSON report (stable-ordered)
89
+ --sarif SARIF 2.1.0 report (for code-scanning upload)
90
+ --policy <path> Policy file path (default: .rn-doctor.yml if present)
91
+ --changed-only Only check deps added or changed vs the base ref
92
+ --base <ref> Base ref for --changed-only (default: origin/main)
93
+ --workspaces Also check every workspace package.json
94
+ --no-cache Bypass the enrichment cache (read and write)
95
+ --annotations Force GitHub annotations on (auto in GitHub Actions)
96
+ --no-annotations Force GitHub annotations off
97
+ -v, --version Print the version
98
+ -h, --help Show help
99
+ ```
100
+
101
+ **Exit codes** (stable contract — CI depends on it):
102
+
103
+ | Code | Meaning |
104
+ | ---- | ------- |
105
+ | `0` | Clean. Warnings, notes, and allowlisted findings are OK. |
106
+ | `1` | Policy errors found. |
107
+ | `2` | Tool failure: bad flags, unreadable `package.json`, invalid policy file, git failure under `--changed-only`. |
108
+
109
+ **Environment**: `GITHUB_TOKEN` (optional, enables GitHub repo enrichment), `NO_COLOR` (disables ANSI color), `GITHUB_ACTIONS=true` (auto-enables annotations).
110
+
111
+ Only `dependencies` are checked — `devDependencies` never ship in your app.
112
+
113
+ ## Policy: `.rn-doctor.yml`
114
+
115
+ Everything is optional; omitted keys use the defaults shown. Unknown keys are rejected loudly, so a typo can't silently weaken the policy.
116
+
117
+ ```yaml
118
+ rules:
119
+ newArchitecture: error # directory says "not New Arch supported"
120
+ newArchUnknown: warn # New Arch support unknown (missing data -> warn, not error)
121
+ lastPublish: # staleness of the latest npm publish
122
+ warnMonths: 12
123
+ errorMonths: 24 # or the string "off"
124
+ githubArchived: error # GitHub repository is archived
125
+ npmDeprecated: error # latest version deprecated on npm
126
+ directoryUnmaintained: warn # RN Directory "unmaintained" flag
127
+
128
+ scope: rn-native-only # or all-deps
129
+
130
+ allow: [] # see "Unblocking a PR" below
131
+ ```
132
+
133
+ Each rule takes `error | warn | off` (except `lastPublish`, which takes thresholds or `"off"`).
134
+
135
+ **How the data is gathered.** Each dependency is enriched in parallel from the npm registry (publish time, deprecation, `codegenConfig`), the [React Native Directory](https://reactnative.directory) (New Architecture support, unmaintained flag, GitHub URL), and the GitHub API (archived, last push — token optional). Results are cached in `.rn-doctor-cache.json` for 24 hours (add it to `.gitignore`; `--no-cache` bypasses).
136
+
137
+ **New Architecture verdicts** are tiered honestly: directory says supported → pass; directory says unsupported → `newArchitecture` fires; unknown but the package ships `codegenConfig` → pass with an informational note; unknown otherwise → `newArchUnknown` fires (a warning by default — missing data is not the same as a dead package).
138
+
139
+ **Scope.** `rn-native-only` (default) checks packages that actually couple to React Native: listed in the RN Directory, peer-depend on `react-native`, or ship `android`/`ios` directories. `all-deps` checks everything under `dependencies`.
140
+
141
+ ## Unblocking a PR (allowlist)
142
+
143
+ Sometimes you ship with a flagged package on purpose. One YAML entry unblocks the PR while keeping the finding visible in every report:
144
+
145
+ ```yaml
146
+ allow:
147
+ - package: react-native-legacy-thing
148
+ reason: "Replacement planned for Q4; tracked in TICKET-123"
149
+ expires: 2027-01-31
150
+ ```
151
+
152
+ - Suppressed findings still appear in output (marked as allowed) — nothing disappears silently.
153
+ - `expires` is optional but recommended: past that date the allow stops suppressing and findings **escalate to errors**, so exceptions can't quietly outlive their justification.
154
+ - Every finding's message suggests the exact allowlist entry to add, so unblocking is copy-paste.
155
+
156
+ ## Output formats
157
+
158
+ - **Pretty** (default): one block per finding — severity badge, message, allow-reason if suppressed, and an evidence link (npm/directory/GitHub) so you can verify every claim.
159
+ - **`--json`**: stable-ordered, timestamp-free (`version: 1`) — safe to snapshot or post-process.
160
+ - **`--sarif`**: SARIF 2.1.0, validates against the official schema. Upload to GitHub code scanning:
161
+
162
+ ```yaml
163
+ - run: npx --yes --package react-native-doctor-ci rn-doctor --sarif > rn-doctor.sarif
164
+ continue-on-error: true # let the SARIF upload happen; the gate is separate
165
+ - uses: github/codeql-action/upload-sarif@v3
166
+ with:
167
+ sarif_file: rn-doctor.sarif
168
+ ```
169
+
170
+ - **GitHub annotations**: emitted automatically inside GitHub Actions, resolved to the dependency's real line in `package.json` (string-escape-aware — a same-named key under `scripts` can't false-match).
171
+
172
+ ## Changed-only and monorepos
173
+
174
+ `--changed-only` is the flagship PR mode: it diffs `dependencies` against the merge-base with `--base` and checks only additions and spec changes (downgrades and protocol changes count too — they're re-checked). Fast, and zero noise from pre-existing debt: adopting rn-doctor on a 5-year-old app doesn't mean fixing 30 findings before the check goes green.
175
+
176
+ `--workspaces` walks npm/yarn `workspaces` globs or `pnpm-workspace.yaml` and checks every workspace manifest, with findings grouped per manifest and annotations pointing at the right file. Composes with `--changed-only`.
177
+
178
+ ## FAQ
179
+
180
+ **Does it check devDependencies?** No — they don't ship in your app. Scope decisions live in the policy file, not flags.
181
+
182
+ **Is it a security scanner?** No. CVE auditing is `npm audit` / OSV territory. rn-doctor answers a different question: *is this dependency alive and does it have a future on the New Architecture?*
183
+
184
+ **Does it fix anything?** No — it tells you what's wrong and what to do (allowlist entry, `npx expo install --fix`, Renovate), then gets out of the way.
185
+
186
+ **What if the RN Directory is wrong about a package?** Every finding carries an evidence link so you can check, and a false positive costs one allowlist line. File a correction with the directory — everyone benefits.
187
+
188
+ **Why is `yaml` the only runtime dependency?** The policy file and `pnpm-workspace.yaml` are YAML, and a spec-compliant YAML parser is not a weekend project — this one is worth its weight. Everything else (HTTP, caching, git diffing, glob matching, SARIF, annotations) is hand-rolled on Node built-ins; less supply chain in a tool whose whole job is judging dependencies.
189
+
190
+ ## Contributing
191
+
192
+ Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). The project follows a phased roadmap; out-of-scope items (security auditing, auto-fixing, a hosted service) are listed there.
193
+
194
+ ## License
195
+
196
+ [MIT](LICENSE) © Amrith Vengalath