@rainy-updates/cli 0.4.0 → 0.5.0-rc.1
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 +53 -1
- package/CODE_OF_CONDUCT.md +25 -0
- package/README.md +150 -82
- package/SECURITY.md +18 -0
- package/dist/bin/cli.js +73 -10
- package/dist/cache/cache.d.ts +1 -1
- package/dist/cache/cache.js +60 -15
- package/dist/config/loader.d.ts +3 -1
- package/dist/core/baseline.d.ts +23 -0
- package/dist/core/baseline.js +72 -0
- package/dist/core/check.js +24 -12
- package/dist/core/init-ci.d.ts +7 -1
- package/dist/core/init-ci.js +46 -7
- package/dist/core/options.d.ts +11 -2
- package/dist/core/options.js +163 -2
- package/dist/core/warm-cache.js +4 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/output/sarif.js +21 -1
- package/dist/registry/npm.d.ts +13 -2
- package/dist/registry/npm.js +29 -12
- package/dist/types/index.d.ts +11 -0
- package/dist/utils/semver.d.ts +1 -0
- package/dist/utils/semver.js +24 -0
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.5.0-rc.1] - 2026-02-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- New CI rollout controls:
|
|
10
|
+
- `--fail-on none|patch|minor|major|any`
|
|
11
|
+
- `--max-updates <n>`
|
|
12
|
+
- New baseline workflow command:
|
|
13
|
+
- `baseline --save --file <path>` to snapshot dependency state
|
|
14
|
+
- `baseline --check --file <path>` to detect dependency drift
|
|
15
|
+
- New `init-ci --mode enterprise` template:
|
|
16
|
+
- Node runtime matrix (`20`, `22`)
|
|
17
|
+
- stricter default permissions
|
|
18
|
+
- artifact retention policy
|
|
19
|
+
- built-in rollout gate flags (`--fail-on`, `--max-updates`)
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Dependency target selection now evaluates available package versions from registry metadata, improving `patch|minor|major` accuracy.
|
|
24
|
+
- CLI parser now rejects unknown options and missing option values with explicit errors (safer CI behavior).
|
|
25
|
+
- SARIF output now reports the actual package version dynamically.
|
|
26
|
+
|
|
27
|
+
### Tests
|
|
28
|
+
|
|
29
|
+
- Added baseline snapshot/diff tests.
|
|
30
|
+
- Added enterprise workflow generation tests.
|
|
31
|
+
- Added semver target selection tests using available version sets.
|
|
32
|
+
- Added parser tests for baseline command, rollout flags, and unknown option rejection.
|
|
33
|
+
|
|
34
|
+
## [0.4.4] - 2026-02-27
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Version bump to `0.4.4` for production stabilization.
|
|
39
|
+
- Simplified public documentation to focus on end-user CLI usage.
|
|
40
|
+
- Removed user-facing instructions for GitHub Actions configuration from README.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- Removed optional `better-sqlite3` dependency to avoid deprecated native install warnings (`prebuild-install`).
|
|
45
|
+
- Cache backend now uses `bun:sqlite` when available and falls back cleanly to file-based cache without native Node addons.
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- `SECURITY.md` with vulnerability disclosure guidance.
|
|
50
|
+
- `CODE_OF_CONDUCT.md` for OSS community standards.
|
|
51
|
+
- Automatic CI bootstrap improvements in `init-ci`:
|
|
52
|
+
- `--mode minimal|strict`
|
|
53
|
+
- `--schedule weekly|daily|off`
|
|
54
|
+
- package-manager-aware install step generation (npm/pnpm)
|
|
55
|
+
|
|
56
|
+
|
|
5
57
|
## [0.4.0] - 2026-02-27
|
|
6
58
|
|
|
7
59
|
### Added
|
|
@@ -126,7 +178,7 @@ All notable changes to this project are documented in this file.
|
|
|
126
178
|
- `--dep-kinds deps,dev,optional,peer`
|
|
127
179
|
- Runtime controls:
|
|
128
180
|
- `--concurrency` for parallel dependency checks.
|
|
129
|
-
|
|
181
|
+
- `--cache-ttl` for cache freshness tuning.
|
|
130
182
|
- Cache layer improvements:
|
|
131
183
|
- SQLite-first cache backend when available.
|
|
132
184
|
- JSON fallback cache backend.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Standards
|
|
4
|
+
|
|
5
|
+
We are committed to a respectful, inclusive, and harassment-free community.
|
|
6
|
+
|
|
7
|
+
Expected behavior:
|
|
8
|
+
|
|
9
|
+
- be respectful and constructive
|
|
10
|
+
- focus on technical issues, not personal attacks
|
|
11
|
+
- welcome feedback and different viewpoints
|
|
12
|
+
|
|
13
|
+
Unacceptable behavior:
|
|
14
|
+
|
|
15
|
+
- harassment, discrimination, or abusive language
|
|
16
|
+
- doxxing or threats
|
|
17
|
+
- trolling and persistent disruption
|
|
18
|
+
|
|
19
|
+
## Enforcement
|
|
20
|
+
|
|
21
|
+
Project maintainers are responsible for clarifying and enforcing this code of conduct.
|
|
22
|
+
|
|
23
|
+
## Reporting
|
|
24
|
+
|
|
25
|
+
Report unacceptable behavior through private communication with maintainers or GitHub moderation tools.
|
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# @rainy-updates/cli
|
|
2
2
|
|
|
3
|
-
Agentic
|
|
3
|
+
Agentic CLI to detect, control, and apply dependency updates across npm/pnpm projects and monorepos.
|
|
4
|
+
|
|
5
|
+
`@rainy-updates/cli` is built for teams that need fast dependency intelligence, policy-aware upgrades, and automation-ready output for CI/CD and pull request workflows.
|
|
6
|
+
|
|
7
|
+
## Why this package
|
|
8
|
+
|
|
9
|
+
- Detects updates quickly across single-package repos and workspaces.
|
|
10
|
+
- Applies updates safely with configurable targets (`patch`, `minor`, `major`, `latest`).
|
|
11
|
+
- Enforces policy rules per package (ignore rules and max upgrade level).
|
|
12
|
+
- Supports offline and cache-warmed execution for deterministic CI runs.
|
|
13
|
+
- Produces machine-readable artifacts (JSON, SARIF, GitHub outputs, PR markdown report).
|
|
4
14
|
|
|
5
15
|
## Install
|
|
6
16
|
|
|
@@ -10,64 +20,58 @@ npm i -D @rainy-updates/cli
|
|
|
10
20
|
pnpm add -D @rainy-updates/cli
|
|
11
21
|
```
|
|
12
22
|
|
|
13
|
-
##
|
|
23
|
+
## Core commands
|
|
14
24
|
|
|
15
|
-
- `check`:
|
|
16
|
-
- `upgrade`: rewrite dependency ranges
|
|
17
|
-
- `warm-cache`:
|
|
18
|
-
- `
|
|
25
|
+
- `check`: analyze dependencies and report available updates.
|
|
26
|
+
- `upgrade`: rewrite dependency ranges in manifests, optionally install lockfile updates.
|
|
27
|
+
- `warm-cache`: prefetch package metadata for fast and offline checks.
|
|
28
|
+
- `baseline`: save and compare dependency baseline snapshots.
|
|
19
29
|
|
|
20
|
-
## Quick
|
|
30
|
+
## Quick usage
|
|
21
31
|
|
|
22
32
|
```bash
|
|
23
|
-
#
|
|
24
|
-
npx @rainy-updates/cli check --
|
|
33
|
+
# 1) Detect updates
|
|
34
|
+
npx @rainy-updates/cli check --format table
|
|
25
35
|
|
|
26
|
-
#
|
|
27
|
-
npx @rainy-updates/cli
|
|
28
|
-
npx @rainy-updates/cli check --workspace --offline --ci
|
|
36
|
+
# 2) Strict CI mode (non-zero when updates exist)
|
|
37
|
+
npx @rainy-updates/cli check --workspace --ci --format json --json-file .artifacts/updates.json
|
|
29
38
|
|
|
30
|
-
#
|
|
39
|
+
# 3) Apply upgrades with workspace sync
|
|
31
40
|
npx @rainy-updates/cli upgrade --target latest --workspace --sync --install
|
|
32
41
|
|
|
33
|
-
#
|
|
34
|
-
npx @rainy-updates/cli
|
|
42
|
+
# 4) Warm cache for deterministic offline checks
|
|
43
|
+
npx @rainy-updates/cli warm-cache --workspace --concurrency 32
|
|
44
|
+
npx @rainy-updates/cli check --workspace --offline --ci
|
|
45
|
+
|
|
46
|
+
# 5) Save and compare baseline drift in CI
|
|
47
|
+
npx @rainy-updates/cli baseline --save --file .artifacts/deps-baseline.json --workspace
|
|
48
|
+
npx @rainy-updates/cli baseline --check --file .artifacts/deps-baseline.json --workspace --ci
|
|
35
49
|
```
|
|
36
50
|
|
|
37
|
-
##
|
|
51
|
+
## What it does in production
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
- `--filter <pattern>`
|
|
41
|
-
- `--reject <pattern>`
|
|
42
|
-
- `--dep-kinds deps,dev,optional,peer`
|
|
43
|
-
- `--workspace`
|
|
44
|
-
- `--concurrency <n>`
|
|
45
|
-
- `--cache-ttl <seconds>`
|
|
46
|
-
- `--offline` (cache-only mode)
|
|
47
|
-
- `--cwd <path>`
|
|
53
|
+
### Update detection engine
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
- Scans dependency groups: `dependencies`, `devDependencies`, `optionalDependencies`, `peerDependencies`.
|
|
56
|
+
- Resolves versions per unique package to reduce duplicate network requests.
|
|
57
|
+
- Uses network concurrency controls and resilient retries.
|
|
58
|
+
- Supports stale-cache fallback when registry calls fail.
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
- `--json-file <path>`
|
|
53
|
-
- `--github-output <path>`
|
|
54
|
-
- `--sarif-file <path>`
|
|
55
|
-
- `--pr-report-file <path>` (generates markdown report for PR comments)
|
|
60
|
+
### Workspace support
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
- Detects package workspaces from:
|
|
63
|
+
- `package.json` workspaces
|
|
64
|
+
- `pnpm-workspace.yaml`
|
|
65
|
+
- Handles multi-manifest upgrade flows.
|
|
66
|
+
- Graph-aware sync mode (`--sync`) avoids breaking `workspace:*` references.
|
|
58
67
|
|
|
59
|
-
-
|
|
60
|
-
- `--pm auto|npm|pnpm`
|
|
61
|
-
- `--sync` (graph-aware version alignment across workspace packages)
|
|
62
|
-
|
|
63
|
-
## Policy controls
|
|
68
|
+
### Policy-aware control
|
|
64
69
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
|
|
68
|
-
- `rainy-updates.policy.json`
|
|
70
|
+
- Apply global ignore patterns.
|
|
71
|
+
- Apply package-specific rules.
|
|
72
|
+
- Enforce max upgrade target per package (for safer rollout).
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
Example policy file:
|
|
71
75
|
|
|
72
76
|
```json
|
|
73
77
|
{
|
|
@@ -79,63 +83,127 @@ Policy example:
|
|
|
79
83
|
}
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
Use it with:
|
|
83
87
|
|
|
84
88
|
```bash
|
|
85
|
-
rainy-updates --
|
|
86
|
-
rainy-updates <command> --help
|
|
87
|
-
rainy-updates --version
|
|
89
|
+
npx @rainy-updates/cli check --policy-file .rainyupdates-policy.json
|
|
88
90
|
```
|
|
89
91
|
|
|
90
|
-
##
|
|
92
|
+
## Output and reporting
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
- returns exit code `2` for operational errors (registry/IO/runtime failures).
|
|
94
|
+
### Human output
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
- `--format table`
|
|
97
|
+
- `--format minimal`
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
### Automation output
|
|
98
100
|
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
101
|
+
- `--format json`
|
|
102
|
+
- `--json-file <path>`
|
|
103
|
+
- `--sarif-file <path>`
|
|
104
|
+
- `--github-output <path>`
|
|
105
|
+
- `--pr-report-file <path>`
|
|
102
106
|
|
|
103
|
-
|
|
107
|
+
These outputs are designed for CI pipelines, security tooling, and PR review automation.
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"prReportFile": ".artifacts/deps.md",
|
|
117
|
-
"policyFile": ".rainyupdates-policy.json"
|
|
118
|
-
}
|
|
119
|
-
}
|
|
109
|
+
|
|
110
|
+
## Automatic CI bootstrap
|
|
111
|
+
|
|
112
|
+
Generate a workflow in the target project automatically:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# strict mode (recommended)
|
|
116
|
+
npx @rainy-updates/cli init-ci --mode enterprise --schedule weekly
|
|
117
|
+
|
|
118
|
+
# lightweight mode
|
|
119
|
+
npx @rainy-updates/cli init-ci --mode minimal --schedule daily
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Generated file:
|
|
123
|
+
|
|
124
|
+
- `.github/workflows/rainy-updates.yml`
|
|
125
|
+
|
|
126
|
+
Modes:
|
|
127
|
+
|
|
128
|
+
- `strict`: warm-cache + offline check + artifacts + SARIF upload.
|
|
129
|
+
- `enterprise`: strict checks + runtime matrix + retention policy + rollout gates.
|
|
130
|
+
- `minimal`: fast check-only workflow for quick adoption.
|
|
131
|
+
|
|
132
|
+
Schedule:
|
|
133
|
+
|
|
134
|
+
- `weekly`, `daily`, or `off` (manual dispatch only).
|
|
135
|
+
|
|
136
|
+
## Command options
|
|
137
|
+
|
|
138
|
+
### Global
|
|
139
|
+
|
|
140
|
+
- `--cwd <path>`
|
|
141
|
+
- `--workspace`
|
|
142
|
+
- `--target patch|minor|major|latest`
|
|
143
|
+
- `--filter <pattern>`
|
|
144
|
+
- `--reject <pattern>`
|
|
145
|
+
- `--dep-kinds deps,dev,optional,peer`
|
|
146
|
+
- `--concurrency <n>`
|
|
147
|
+
- `--cache-ttl <seconds>`
|
|
148
|
+
- `--offline`
|
|
149
|
+
- `--fail-on none|patch|minor|major|any`
|
|
150
|
+
- `--max-updates <n>`
|
|
151
|
+
- `--policy-file <path>`
|
|
152
|
+
- `--format table|json|minimal|github`
|
|
153
|
+
- `--json-file <path>`
|
|
154
|
+
- `--github-output <path>`
|
|
155
|
+
- `--sarif-file <path>`
|
|
156
|
+
- `--pr-report-file <path>`
|
|
157
|
+
- `--ci`
|
|
158
|
+
|
|
159
|
+
### Upgrade-only
|
|
160
|
+
|
|
161
|
+
- `--install`
|
|
162
|
+
- `--pm auto|npm|pnpm`
|
|
163
|
+
- `--sync`
|
|
164
|
+
|
|
165
|
+
### Baseline-only
|
|
166
|
+
|
|
167
|
+
- `--save`
|
|
168
|
+
- `--check`
|
|
169
|
+
- `--file <path>`
|
|
170
|
+
|
|
171
|
+
## Config support
|
|
172
|
+
|
|
173
|
+
Configuration can be loaded from:
|
|
174
|
+
|
|
175
|
+
- `.rainyupdatesrc`
|
|
176
|
+
- `.rainyupdatesrc.json`
|
|
177
|
+
- `package.json` field: `rainyUpdates`
|
|
178
|
+
|
|
179
|
+
## CLI help
|
|
123
180
|
|
|
124
181
|
```bash
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
182
|
+
rainy-updates --help
|
|
183
|
+
rainy-updates <command> --help
|
|
184
|
+
rainy-updates --version
|
|
128
185
|
```
|
|
129
186
|
|
|
130
|
-
|
|
187
|
+
## Reliability characteristics
|
|
188
|
+
|
|
189
|
+
- Node.js 20+ runtime.
|
|
190
|
+
- Works with npm and pnpm workflows.
|
|
191
|
+
- Uses optional `undici` pool path for high-throughput HTTP.
|
|
192
|
+
- Cache-first architecture for speed and resilience.
|
|
193
|
+
|
|
194
|
+
## CI/CD included
|
|
195
|
+
|
|
196
|
+
This package ships with production CI/CD pipelines in the repository:
|
|
197
|
+
|
|
198
|
+
- Continuous integration pipeline for typecheck, tests, build, and production smoke checks.
|
|
199
|
+
- Tag-driven release pipeline for npm publishing with provenance.
|
|
200
|
+
- Release preflight validation for npm auth/scope checks before publishing.
|
|
201
|
+
|
|
131
202
|
|
|
132
|
-
|
|
203
|
+
## Product roadmap
|
|
133
204
|
|
|
134
|
-
-
|
|
135
|
-
- `.github/workflows/release.yml` for tag-driven npm publishing.
|
|
205
|
+
The long-term roadmap is maintained in [`ROADMAP.md`](./ROADMAP.md).
|
|
136
206
|
|
|
137
|
-
##
|
|
207
|
+
## License
|
|
138
208
|
|
|
139
|
-
|
|
140
|
-
- Uses `undici` pool with HTTP/2 when available; falls back to native `fetch` automatically.
|
|
141
|
-
- Uses layered cache with stale fallback for resilient CI runs.
|
|
209
|
+
MIT
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are applied to the latest released version.
|
|
6
|
+
|
|
7
|
+
## Reporting vulnerabilities
|
|
8
|
+
|
|
9
|
+
Report vulnerabilities privately through GitHub Security Advisories.
|
|
10
|
+
|
|
11
|
+
Include:
|
|
12
|
+
|
|
13
|
+
- affected version
|
|
14
|
+
- reproduction steps
|
|
15
|
+
- impact assessment
|
|
16
|
+
- proof-of-concept if available
|
|
17
|
+
|
|
18
|
+
Do not open public issues for unpatched security vulnerabilities.
|
package/dist/bin/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { check } from "../core/check.js";
|
|
|
8
8
|
import { upgrade } from "../core/upgrade.js";
|
|
9
9
|
import { warmCache } from "../core/warm-cache.js";
|
|
10
10
|
import { initCiWorkflow } from "../core/init-ci.js";
|
|
11
|
+
import { diffBaseline, saveBaseline } from "../core/baseline.js";
|
|
11
12
|
import { renderResult } from "../output/format.js";
|
|
12
13
|
import { writeGitHubOutput } from "../output/github.js";
|
|
13
14
|
import { createSarifReport } from "../output/sarif.js";
|
|
@@ -25,12 +26,40 @@ async function main() {
|
|
|
25
26
|
}
|
|
26
27
|
const parsed = await parseCliArgs(argv);
|
|
27
28
|
if (parsed.command === "init-ci") {
|
|
28
|
-
const workflow = await initCiWorkflow(parsed.options.cwd, parsed.options.force
|
|
29
|
+
const workflow = await initCiWorkflow(parsed.options.cwd, parsed.options.force, {
|
|
30
|
+
mode: parsed.options.mode,
|
|
31
|
+
schedule: parsed.options.schedule,
|
|
32
|
+
});
|
|
29
33
|
process.stdout.write(workflow.created
|
|
30
34
|
? `Created CI workflow at ${workflow.path}\n`
|
|
31
35
|
: `CI workflow already exists at ${workflow.path}. Use --force to overwrite.\n`);
|
|
32
36
|
return;
|
|
33
37
|
}
|
|
38
|
+
if (parsed.command === "baseline") {
|
|
39
|
+
if (parsed.options.action === "save") {
|
|
40
|
+
const saved = await saveBaseline(parsed.options);
|
|
41
|
+
process.stdout.write(`Saved baseline at ${saved.filePath} (${saved.entries} entries)\n`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const diff = await diffBaseline(parsed.options);
|
|
45
|
+
const changes = diff.added.length + diff.removed.length + diff.changed.length;
|
|
46
|
+
if (changes === 0) {
|
|
47
|
+
process.stdout.write(`No baseline drift detected (${diff.filePath}).\n`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
process.stdout.write(`Baseline drift detected (${diff.filePath}).\n`);
|
|
51
|
+
if (diff.added.length > 0) {
|
|
52
|
+
process.stdout.write(`Added: ${diff.added.length}\n`);
|
|
53
|
+
}
|
|
54
|
+
if (diff.removed.length > 0) {
|
|
55
|
+
process.stdout.write(`Removed: ${diff.removed.length}\n`);
|
|
56
|
+
}
|
|
57
|
+
if (diff.changed.length > 0) {
|
|
58
|
+
process.stdout.write(`Changed: ${diff.changed.length}\n`);
|
|
59
|
+
}
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
34
63
|
const result = parsed.command === "upgrade"
|
|
35
64
|
? await upgrade(parsed.options)
|
|
36
65
|
: parsed.command === "warm-cache"
|
|
@@ -55,13 +84,7 @@ async function main() {
|
|
|
55
84
|
await fs.mkdir(path.dirname(parsed.options.sarifFile), { recursive: true });
|
|
56
85
|
await fs.writeFile(parsed.options.sarifFile, JSON.stringify(sarif, null, 2) + "\n", "utf8");
|
|
57
86
|
}
|
|
58
|
-
|
|
59
|
-
process.exitCode = 1;
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (result.errors.length > 0) {
|
|
63
|
-
process.exitCode = 2;
|
|
64
|
-
}
|
|
87
|
+
process.exitCode = resolveExitCode(result, parsed.options.failOn, parsed.options.maxUpdates, parsed.options.ci);
|
|
65
88
|
}
|
|
66
89
|
catch (error) {
|
|
67
90
|
process.stderr.write(`rainy-updates: ${String(error)}\n`);
|
|
@@ -107,10 +130,28 @@ Options:
|
|
|
107
130
|
--pr-report-file <path>`;
|
|
108
131
|
}
|
|
109
132
|
if (isCommand && command === "init-ci") {
|
|
110
|
-
return `rainy-updates init-ci [
|
|
133
|
+
return `rainy-updates init-ci [options]
|
|
111
134
|
|
|
112
135
|
Create a GitHub Actions workflow template at:
|
|
113
|
-
.github/workflows/rainy-updates.yml
|
|
136
|
+
.github/workflows/rainy-updates.yml
|
|
137
|
+
|
|
138
|
+
Options:
|
|
139
|
+
--force
|
|
140
|
+
--mode minimal|strict|enterprise
|
|
141
|
+
--schedule weekly|daily|off`;
|
|
142
|
+
}
|
|
143
|
+
if (isCommand && command === "baseline") {
|
|
144
|
+
return `rainy-updates baseline [options]
|
|
145
|
+
|
|
146
|
+
Save or compare dependency baseline snapshots.
|
|
147
|
+
|
|
148
|
+
Options:
|
|
149
|
+
--save
|
|
150
|
+
--check
|
|
151
|
+
--file <path>
|
|
152
|
+
--workspace
|
|
153
|
+
--dep-kinds deps,dev,optional,peer
|
|
154
|
+
--ci`;
|
|
114
155
|
}
|
|
115
156
|
return `rainy-updates <command> [options]
|
|
116
157
|
|
|
@@ -119,6 +160,7 @@ Commands:
|
|
|
119
160
|
upgrade Apply updates to manifests
|
|
120
161
|
warm-cache Warm local cache for fast/offline checks
|
|
121
162
|
init-ci Scaffold GitHub Actions workflow
|
|
163
|
+
baseline Save/check dependency baseline snapshots
|
|
122
164
|
|
|
123
165
|
Global options:
|
|
124
166
|
--cwd <path>
|
|
@@ -130,6 +172,8 @@ Global options:
|
|
|
130
172
|
--sarif-file <path>
|
|
131
173
|
--pr-report-file <path>
|
|
132
174
|
--policy-file <path>
|
|
175
|
+
--fail-on none|patch|minor|major|any
|
|
176
|
+
--max-updates <n>
|
|
133
177
|
--concurrency <n>
|
|
134
178
|
--cache-ttl <seconds>
|
|
135
179
|
--offline
|
|
@@ -144,3 +188,22 @@ async function readPackageVersion() {
|
|
|
144
188
|
const parsed = JSON.parse(content);
|
|
145
189
|
return parsed.version ?? "0.0.0";
|
|
146
190
|
}
|
|
191
|
+
function resolveExitCode(result, failOn, maxUpdates, ciMode) {
|
|
192
|
+
if (result.errors.length > 0)
|
|
193
|
+
return 2;
|
|
194
|
+
if (typeof maxUpdates === "number" && result.updates.length > maxUpdates)
|
|
195
|
+
return 1;
|
|
196
|
+
const effectiveFailOn = failOn && failOn !== "none" ? failOn : ciMode ? "any" : "none";
|
|
197
|
+
if (!shouldFailForUpdates(result.updates, effectiveFailOn))
|
|
198
|
+
return 0;
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
function shouldFailForUpdates(updates, failOn) {
|
|
202
|
+
if (failOn === "none")
|
|
203
|
+
return false;
|
|
204
|
+
if (failOn === "any" || failOn === "patch")
|
|
205
|
+
return updates.length > 0;
|
|
206
|
+
if (failOn === "minor")
|
|
207
|
+
return updates.some((update) => update.diffType === "minor" || update.diffType === "major");
|
|
208
|
+
return updates.some((update) => update.diffType === "major");
|
|
209
|
+
}
|
package/dist/cache/cache.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ export declare class VersionCache {
|
|
|
5
5
|
static create(customPath?: string): Promise<VersionCache>;
|
|
6
6
|
getValid(packageName: string, target: TargetLevel): Promise<CachedVersion | null>;
|
|
7
7
|
getAny(packageName: string, target: TargetLevel): Promise<CachedVersion | null>;
|
|
8
|
-
set(packageName: string, target: TargetLevel, latestVersion: string, ttlSeconds: number): Promise<void>;
|
|
8
|
+
set(packageName: string, target: TargetLevel, latestVersion: string, availableVersions: string[], ttlSeconds: number): Promise<void>;
|
|
9
9
|
}
|
package/dist/cache/cache.js
CHANGED
|
@@ -9,7 +9,13 @@ class FileCacheStore {
|
|
|
9
9
|
async get(packageName, target) {
|
|
10
10
|
const entries = await this.readEntries();
|
|
11
11
|
const key = this.getKey(packageName, target);
|
|
12
|
-
|
|
12
|
+
const entry = entries[key];
|
|
13
|
+
if (!entry)
|
|
14
|
+
return null;
|
|
15
|
+
return {
|
|
16
|
+
...entry,
|
|
17
|
+
availableVersions: Array.isArray(entry.availableVersions) ? entry.availableVersions : [entry.latestVersion],
|
|
18
|
+
};
|
|
13
19
|
}
|
|
14
20
|
async set(entry) {
|
|
15
21
|
const entries = await this.readEntries();
|
|
@@ -39,31 +45,63 @@ class SqliteCacheStore {
|
|
|
39
45
|
package_name TEXT NOT NULL,
|
|
40
46
|
target TEXT NOT NULL,
|
|
41
47
|
latest_version TEXT NOT NULL,
|
|
48
|
+
available_versions TEXT NOT NULL,
|
|
42
49
|
fetched_at INTEGER NOT NULL,
|
|
43
50
|
ttl_seconds INTEGER NOT NULL,
|
|
44
51
|
PRIMARY KEY (package_name, target)
|
|
45
52
|
);
|
|
46
53
|
`);
|
|
54
|
+
this.ensureSchema();
|
|
47
55
|
}
|
|
48
56
|
async get(packageName, target) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.
|
|
57
|
+
let row;
|
|
58
|
+
try {
|
|
59
|
+
row = this.db
|
|
60
|
+
.prepare(`SELECT package_name, target, latest_version, available_versions, fetched_at, ttl_seconds FROM versions WHERE package_name = ? AND target = ?`)
|
|
61
|
+
.get(packageName, target);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
row = this.db
|
|
65
|
+
.prepare(`SELECT package_name, target, latest_version, fetched_at, ttl_seconds FROM versions WHERE package_name = ? AND target = ?`)
|
|
66
|
+
.get(packageName, target);
|
|
67
|
+
}
|
|
52
68
|
if (!row)
|
|
53
69
|
return null;
|
|
54
70
|
return {
|
|
55
71
|
packageName: row.package_name,
|
|
56
72
|
target: row.target,
|
|
57
73
|
latestVersion: row.latest_version,
|
|
74
|
+
availableVersions: parseJsonArray(row.available_versions ?? row.latest_version, row.latest_version),
|
|
58
75
|
fetchedAt: row.fetched_at,
|
|
59
76
|
ttlSeconds: row.ttl_seconds,
|
|
60
77
|
};
|
|
61
78
|
}
|
|
62
79
|
async set(entry) {
|
|
63
|
-
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
|
|
80
|
+
try {
|
|
81
|
+
this.db
|
|
82
|
+
.prepare(`INSERT OR REPLACE INTO versions (package_name, target, latest_version, available_versions, fetched_at, ttl_seconds)
|
|
83
|
+
VALUES (?, ?, ?, ?, ?, ?)`)
|
|
84
|
+
.run(entry.packageName, entry.target, entry.latestVersion, JSON.stringify(entry.availableVersions), entry.fetchedAt, entry.ttlSeconds);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
this.db
|
|
88
|
+
.prepare(`INSERT OR REPLACE INTO versions (package_name, target, latest_version, fetched_at, ttl_seconds)
|
|
89
|
+
VALUES (?, ?, ?, ?, ?)`)
|
|
90
|
+
.run(entry.packageName, entry.target, entry.latestVersion, entry.fetchedAt, entry.ttlSeconds);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
ensureSchema() {
|
|
94
|
+
try {
|
|
95
|
+
const columns = this.db.prepare("PRAGMA table_info(versions);").all();
|
|
96
|
+
const hasAvailableVersions = columns.some((column) => column.name === "available_versions");
|
|
97
|
+
if (!hasAvailableVersions) {
|
|
98
|
+
this.db.exec("ALTER TABLE versions ADD COLUMN available_versions TEXT;");
|
|
99
|
+
}
|
|
100
|
+
this.db.exec("UPDATE versions SET available_versions = latest_version WHERE available_versions IS NULL;");
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Best-effort migration.
|
|
104
|
+
}
|
|
67
105
|
}
|
|
68
106
|
}
|
|
69
107
|
export class VersionCache {
|
|
@@ -92,11 +130,12 @@ export class VersionCache {
|
|
|
92
130
|
async getAny(packageName, target) {
|
|
93
131
|
return this.store.get(packageName, target);
|
|
94
132
|
}
|
|
95
|
-
async set(packageName, target, latestVersion, ttlSeconds) {
|
|
133
|
+
async set(packageName, target, latestVersion, availableVersions, ttlSeconds) {
|
|
96
134
|
await this.store.set({
|
|
97
135
|
packageName,
|
|
98
136
|
target,
|
|
99
137
|
latestVersion,
|
|
138
|
+
availableVersions,
|
|
100
139
|
fetchedAt: Date.now(),
|
|
101
140
|
ttlSeconds,
|
|
102
141
|
});
|
|
@@ -111,15 +150,21 @@ async function tryCreateSqliteStore(dbPath) {
|
|
|
111
150
|
}
|
|
112
151
|
}
|
|
113
152
|
catch {
|
|
114
|
-
|
|
153
|
+
return null;
|
|
115
154
|
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
function parseJsonArray(raw, fallback) {
|
|
158
|
+
if (typeof raw !== "string")
|
|
159
|
+
return [fallback];
|
|
116
160
|
try {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
161
|
+
const parsed = JSON.parse(raw);
|
|
162
|
+
if (!Array.isArray(parsed))
|
|
163
|
+
return [fallback];
|
|
164
|
+
const values = parsed.filter((value) => typeof value === "string");
|
|
165
|
+
return values.length > 0 ? values : [fallback];
|
|
121
166
|
}
|
|
122
167
|
catch {
|
|
123
|
-
return
|
|
168
|
+
return [fallback];
|
|
124
169
|
}
|
|
125
170
|
}
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DependencyKind, OutputFormat, TargetLevel } from "../types/index.js";
|
|
1
|
+
import type { DependencyKind, FailOnLevel, OutputFormat, TargetLevel } from "../types/index.js";
|
|
2
2
|
export interface FileConfig {
|
|
3
3
|
target?: TargetLevel;
|
|
4
4
|
filter?: string;
|
|
@@ -15,6 +15,8 @@ export interface FileConfig {
|
|
|
15
15
|
offline?: boolean;
|
|
16
16
|
policyFile?: string;
|
|
17
17
|
prReportFile?: string;
|
|
18
|
+
failOn?: FailOnLevel;
|
|
19
|
+
maxUpdates?: number;
|
|
18
20
|
install?: boolean;
|
|
19
21
|
packageManager?: "auto" | "npm" | "pnpm";
|
|
20
22
|
sync?: boolean;
|