dep-up-surgeon 1.6.6 → 2.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/README.md +281 -19
- package/dist/cli/changelogEnricher.d.ts +28 -0
- package/dist/cli/changelogEnricher.d.ts.map +1 -0
- package/dist/cli/changelogEnricher.js +47 -0
- package/dist/cli/changelogEnricher.js.map +1 -0
- package/dist/cli/git.d.ts +90 -0
- package/dist/cli/git.d.ts.map +1 -0
- package/dist/cli/git.js +316 -0
- package/dist/cli/git.js.map +1 -0
- package/dist/cli/gitFlow.d.ts +67 -0
- package/dist/cli/gitFlow.d.ts.map +1 -0
- package/dist/cli/gitFlow.js +290 -0
- package/dist/cli/gitFlow.js.map +1 -0
- package/dist/cli/lastRun.d.ts +60 -0
- package/dist/cli/lastRun.d.ts.map +1 -0
- package/dist/cli/lastRun.js +112 -0
- package/dist/cli/lastRun.js.map +1 -0
- package/dist/cli/report.d.ts +32 -1
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +42 -1
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/summary.d.ts +28 -0
- package/dist/cli/summary.d.ts.map +1 -0
- package/dist/cli/summary.js +311 -0
- package/dist/cli/summary.js.map +1 -0
- package/dist/cli.js +498 -25
- package/dist/cli.js.map +1 -1
- package/dist/config/loadConfig.d.ts +23 -0
- package/dist/config/loadConfig.d.ts.map +1 -1
- package/dist/config/loadConfig.js +45 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/policy.d.ts +83 -0
- package/dist/config/policy.d.ts.map +1 -0
- package/dist/config/policy.js +377 -0
- package/dist/config/policy.js.map +1 -0
- package/dist/core/audit.d.ts +49 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +332 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/resolver.d.ts +4 -1
- package/dist/core/resolver.d.ts.map +1 -1
- package/dist/core/resolver.js +2 -3
- package/dist/core/resolver.js.map +1 -1
- package/dist/core/upgrader.d.ts +194 -3
- package/dist/core/upgrader.d.ts.map +1 -1
- package/dist/core/upgrader.js +649 -36
- package/dist/core/upgrader.js.map +1 -1
- package/dist/core/validator.d.ts +39 -4
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +53 -10
- package/dist/core/validator.js.map +1 -1
- package/dist/core/workspaces.d.ts +43 -0
- package/dist/core/workspaces.d.ts.map +1 -0
- package/dist/core/workspaces.js +294 -0
- package/dist/core/workspaces.js.map +1 -0
- package/dist/types.d.ts +233 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/blastRadius.d.ts +46 -0
- package/dist/utils/blastRadius.d.ts.map +1 -0
- package/dist/utils/blastRadius.js +194 -0
- package/dist/utils/blastRadius.js.map +1 -0
- package/dist/utils/changelog.d.ts +97 -0
- package/dist/utils/changelog.d.ts.map +1 -0
- package/dist/utils/changelog.js +344 -0
- package/dist/utils/changelog.js.map +1 -0
- package/dist/utils/concurrency.d.ts +51 -1
- package/dist/utils/concurrency.d.ts.map +1 -1
- package/dist/utils/concurrency.js +65 -13
- package/dist/utils/concurrency.js.map +1 -1
- package/dist/utils/npm.d.ts +58 -4
- package/dist/utils/npm.d.ts.map +1 -1
- package/dist/utils/npm.js +82 -7
- package/dist/utils/npm.js.map +1 -1
- package/dist/utils/output.d.ts +12 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +21 -0
- package/dist/utils/output.js.map +1 -0
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -73,8 +73,247 @@ dep-up-surgeon [options]
|
|
|
73
73
|
| `--json` | Machine-readable report on stdout (see **JSON report**). |
|
|
74
74
|
| `--fallback-strategy <mode>` | `major-lines` (**default**), `minor-lines`, or `none`. After `@latest` fails, **`major-lines`** tries the best stable version per **major** (e.g. `9.x` → `8.x` → `7.x` …). **`minor-lines`** steps one **`major.minor` line** at a time. If npm output looks like **ESM vs CommonJS** (`ERR_REQUIRE_ESM`), further fallbacks for that package **stop**. `none` only attempts `@latest`. |
|
|
75
75
|
| `--link-groups <mode>` | `auto` (**default**) or `none`. **`auto`** builds **linked batches** from the registry graph and optional **`linkedGroups`**. **`none`** upgrades one dependency per step. |
|
|
76
|
+
| `--validate <cmd>` | Override the validator command run after every install. Defaults to `<manager> test` if a `test` script exists, else `<manager> run build` (yarn classic uses `yarn build`), else nothing. Useful in monorepos where the default build is heavy or fragile (e.g. `--validate "tsc -p tsconfig.json --noEmit"`). |
|
|
77
|
+
| `--no-validate` | Skip validation entirely. Upgrades are kept regardless of test/build outcome. Different from `--force`: `--force` runs the validator and only keeps the bump when it fails, `--no-validate` doesn’t run a validator at all. |
|
|
78
|
+
| `--package-manager <mgr>` | `auto` (**default**), `npm`, `pnpm`, or `yarn`. `auto` reads the `packageManager` field, then falls back to lockfile detection (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm), then `pnpm-workspace.yaml`, then `npm`. The chosen manager drives both the **install** command (`<mgr> install`) and the **default validator** (`<mgr> test` / `<mgr> run build`). |
|
|
79
|
+
| `--include-workspace-deps` | By default, dependencies whose name matches a local **workspace package** (resolved via `workspaces` in `package.json` or `pnpm-workspace.yaml`) are skipped — their version comes from the local workspace, not the registry. Pass this flag to upgrade them anyway (e.g. when local workspace packages also publish to the registry). |
|
|
80
|
+
| `--workspaces` | Traverse the **root** `package.json` **and every workspace member** (one engine pass per `package.json`). Install + validation always run from the workspace root so the lockfile and validator see the whole monorepo. |
|
|
81
|
+
| `--workspaces-only` | Like `--workspaces` but **skips** the root `package.json`. Only workspace members are traversed. |
|
|
82
|
+
| `--workspace <names>` | Comma-separated workspace member **names** (the `name` field from each child `package.json`) to traverse. Pass `root` to also include the root. Example: `--workspace "@org/core,@org/web,root"`. Unknown names produce a friendly error listing the known members. |
|
|
83
|
+
| `--install-mode <mode>` | Workspace install strategy. **`root`** (default) always runs `<mgr> install` from the workspace root after every mutation — the safest option, supported by every package manager. **`filtered`** rewrites per-child installs to their workspace-scoped form: **npm 7+** uses `npm install --workspace <name>`, **pnpm** uses `pnpm install --filter <name>`, **yarn berry (v2+) with `@yarnpkg/plugin-workspace-tools`** uses `yarn workspaces focus <name>`, and **yarn classic / berry without the plugin** falls back to a full root install with a one-time warning explaining the upgrade path. The capability is auto-detected at startup (yarn version + plugin probe) and reported as `project.yarnMajorVersion` + `project.yarnSupportsFocus` in `--json`. Only meaningful with `--workspaces` / `--workspaces-only` / `--workspace <names>`. |
|
|
84
|
+
| `--concurrency <n>` | Maximum number of workspace targets to traverse in parallel (1–16; default `1`). Higher values overlap registry **scan + plan** phases across targets while a shared mutex keeps **install + validation strictly serialized** — the workspace lockfile is shared, so concurrent installs would corrupt it. The default in-process registry cache also deduplicates `pacote.manifest` / `pacote.packument` calls across targets, so even at concurrency `1` you get a speedup when the same dep appears in many workspaces. **Requires `--json`** so per-target log lines don't interleave; non-JSON mode silently downgrades to `1` with a warning. |
|
|
85
|
+
| `--retry-failed` | Read `.dep-up-surgeon.last-run.json` from the previous run and only re-attempt entries that failed for **non-terminal** reasons (`install`, `validation-conflicts`, `versions`, `unknown`). Successful upgrades + terminal failures (`peer`, `validation-script`) from the last run are added to the ignore list automatically. See **Persisted last-run report** below. |
|
|
86
|
+
| `--no-persist-report` | Do **not** write `.dep-up-surgeon.last-run.json` after the run. By default the structured report is written next to the workspace root for `--retry-failed` and CI consumers. |
|
|
87
|
+
| `--summary <format>` | Write a human-friendly summary of the run as `md` (default) or `html`. Destination is `$GITHUB_STEP_SUMMARY` if set (appended), otherwise `--summary-file <path>`, otherwise `./dep-up-surgeon-summary.<ext>`. |
|
|
88
|
+
| `--summary-file <path>` | Override the destination for `--summary`. Wins over `$GITHUB_STEP_SUMMARY`. |
|
|
89
|
+
| `--ci` | Convenience flag for CI / bot use. Disables `--interactive`, auto-enables `--summary md` (great with `$GITHUB_STEP_SUMMARY`), and **exits `0` even when individual upgrades fail** (only pre-flight failures and fatal errors exit `1`) so per-package conflicts surface in the PR description instead of failing the job. |
|
|
90
|
+
| `--git-commit` | Commit successful upgrades to git as the run progresses. Refuses to start on a dirty working tree (override with `--git-allow-dirty`). Only stages `package.json` + the lockfile — never `git add -A`, so unrelated WIP, generated files, and prepare/postinstall side effects are never accidentally swept into a commit. Skipped silently in `--dry-run`. |
|
|
91
|
+
| `--git-commit-mode <mode>` | How to group commits: **`per-success`** (default, one commit per upgrade — best for review and `git revert`-friendly), **`per-target`** (one commit per workspace target with all its successes squashed), or **`all`** (one commit at the end with everything). Linked-group upgrades (e.g. `react` + `react-dom`) always land in a single commit regardless of mode. |
|
|
92
|
+
| `--git-commit-prefix <prefix>` | String prepended to every commit message (default `"deps: "`). Use `"chore(deps): "` for [Conventional Commits](https://www.conventionalcommits.org/) or set it to your team's preferred convention. |
|
|
93
|
+
| `--git-branch <name>` | Create + checkout this branch before any commits. If the branch already exists, switches to it. Pairs nicely with `--ci` for PR-bot workflows (e.g. `--git-branch "deps/auto-$(date +%Y-%m-%d)"`). |
|
|
94
|
+
| `--git-sign` | Pass `--gpg-sign` to every commit. Requires a signing key configured in git (`user.signingkey` + `gpg.format`). Failed signatures are recorded as failed commits in the JSON report rather than aborting the run. |
|
|
95
|
+
| `--git-allow-dirty` | Allow `--git-commit` to run on a dirty working tree. We still only `git add` files we touched, so your WIP isn't swept up — but if you also `git add` your own files manually, they'll land in dep-up-surgeon's commits. |
|
|
96
|
+
| `--changelog` / `--no-changelog` | Fetch the bumped package's release notes (GitHub Releases first, then its published `CHANGELOG.md`) and include them in commit bodies + `--summary`. **Default ON** when `--git-commit` or `--summary` is active. Network failures are non-fatal — missing changelogs are silently skipped. See **Changelog excerpts** below. |
|
|
97
|
+
| `--security-only` | Run `npm audit` (or `pnpm`/`yarn` equivalent) first, then upgrade **only** the packages with open advisories. Every successful bump carries the advisory severity + ID into its commit subject (`[security:high]`) and into the summary's **Security fixes** table. Pairs well with `--git-commit-mode per-success` to produce one PR per CVE. See **Security-first mode** below. |
|
|
98
|
+
| `--min-severity <level>` | Minimum advisory severity to consider under `--security-only`: `low` (default), `moderate`, `high`, or `critical`. Lower-severity advisories are filtered out before the upgrade plan is built. |
|
|
99
|
+
| `--blast-radius` / `--no-blast-radius` | Scan project source files to list which files actually `import`/`require` each upgraded package, and surface the list in `--json` + `--summary`. **Default ON** when `--summary` is active. See **Blast radius** below. |
|
|
100
|
+
|
|
101
|
+
Exit code `1` when any upgrade could not be kept (unless `--force`). The CLI also exits `1` when the **pre-flight** validator (run on the unchanged tree) fails — see **Pre-flight check** below. Fatal errors also exit `1`.
|
|
102
|
+
|
|
103
|
+
### Pre-flight check
|
|
104
|
+
|
|
105
|
+
Before mutating any dependency, the CLI runs the resolved validator command **once** against the unchanged tree:
|
|
106
|
+
|
|
107
|
+
- If it **passes**, the run continues normally.
|
|
108
|
+
- If it **fails**, the run aborts immediately with an error containing the validator command, exit code, and last ~40 lines of output. This prevents the common failure mode where every per-group rollback looks identical because the project build was already broken before the run.
|
|
109
|
+
- To proceed anyway, use `--validate "<cmd>"` to swap the validator, `--no-validate` to skip it, or `--force` to ignore the pre-flight failure.
|
|
110
|
+
|
|
111
|
+
The pre-flight outcome is also surfaced under `preflight` / `preflightAborted` in `--json` output.
|
|
112
|
+
|
|
113
|
+
### Persisted last-run report
|
|
114
|
+
|
|
115
|
+
After every CLI run the structured report is written to `.dep-up-surgeon.last-run.json` next to the workspace root (set `--no-persist-report` to opt out). The file mirrors the `--json` output and adds a small header (`finishedAt`, `toolVersion`, `cwd`, `dryRun`) so CI dashboards / bots can pick it up without re-running the tool. Add it to your `.gitignore` if you don't want it tracked.
|
|
116
|
+
|
|
117
|
+
### Retry-failed mode (`--retry-failed`)
|
|
118
|
+
|
|
119
|
+
Pass `--retry-failed` to **resume** the previous run instead of starting from scratch:
|
|
120
|
+
|
|
121
|
+
- `dep-up-surgeon` reads `.dep-up-surgeon.last-run.json` and **freezes** every package that either:
|
|
122
|
+
- **succeeded** in the last run (no need to redo work), **or**
|
|
123
|
+
- failed for a **terminal** reason: `peer` (real peer-dep conflict; bumping the same package alone almost always fails the same way) or `validation-script` (the project's own test/build script crashed; re-running won't help without a code change).
|
|
124
|
+
- It then **re-attempts** only the residue: failures classified as `install`, `validation-conflicts`, `versions`, or `unknown`. These are the cases where another dependency move during the new run can plausibly unblock them.
|
|
125
|
+
- Linked-group failures (`name === '[group:<id>]'`) are expanded to **every member of the group** via the persisted `groups` field, so freezing a peer-failed group correctly freezes every package in it.
|
|
126
|
+
- If `.dep-up-surgeon.last-run.json` is missing the CLI exits `1` with a friendly message; pass `--retry-failed` only after at least one prior run.
|
|
127
|
+
|
|
128
|
+
Typical workflow:
|
|
76
129
|
|
|
77
|
-
|
|
130
|
+
```bash
|
|
131
|
+
dep-up-surgeon --workspaces # first pass: lots of moves, some failures
|
|
132
|
+
# fix the script that caused a `validation-script` failure (or accept it)
|
|
133
|
+
dep-up-surgeon --retry-failed # second pass: only retries install/conflict residue
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Summary writer (`--summary <md|html>`)
|
|
137
|
+
|
|
138
|
+
Pass `--summary md` (or `--summary html`) to render a human-friendly report alongside the normal output:
|
|
139
|
+
|
|
140
|
+
- **GitHub Actions**: when `GITHUB_STEP_SUMMARY` is set, the Markdown summary is **appended** to that file — it shows up in the job summary tab without any extra workflow plumbing.
|
|
141
|
+
- **Explicit destination**: `--summary-file <path>` overrides everything (wins over `$GITHUB_STEP_SUMMARY`).
|
|
142
|
+
- **Default**: `./dep-up-surgeon-summary.<md|html>`.
|
|
143
|
+
|
|
144
|
+
The summary contains: counts (upgraded / failed / skipped), detected project info, target list, an **Upgraded** table (`Package | Workspace | From | To | Notes`), a **Failed** table (`Package | Workspace | Reason | Attempted | Detail`), pre-flight status when it aborted, and the ignored list. HTML output escapes all dynamic content. Designed to be ~40 lines of code on the producer side and easy to embed in PR comments / dashboards.
|
|
145
|
+
|
|
146
|
+
### CI / bot mode (`--ci`)
|
|
147
|
+
|
|
148
|
+
`--ci` is a convenience flag for unattended runs (GitHub Actions, GitLab CI, Renovate-style bots). It:
|
|
149
|
+
|
|
150
|
+
- **Disables `--interactive`** unconditionally — never blocks on stdin.
|
|
151
|
+
- **Auto-enables `--summary md`** so a Markdown report lands in `$GITHUB_STEP_SUMMARY` (or `./dep-up-surgeon-summary.md` outside Actions). Pass an explicit `--summary html` if you'd rather have HTML.
|
|
152
|
+
- **Remaps the exit code**: per-package failures (peer conflicts, install crashes, validation script errors) are recorded in the report and the run still exits `0`, so the bot's PR carries the diagnostic instead of the job failing red. **Pre-flight failures and fatal errors still exit `1`** — those mean the project itself is broken before any upgrade and a human needs to look.
|
|
153
|
+
|
|
154
|
+
Typical GitHub Actions step:
|
|
155
|
+
|
|
156
|
+
```yaml
|
|
157
|
+
- name: dep-up-surgeon
|
|
158
|
+
run: npx dep-up-surgeon --workspaces --ci
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The job stays green; the **Summary** tab shows the upgraded / failed tables; `.dep-up-surgeon.last-run.json` is committed (or uploaded as an artifact) so a follow-up `--retry-failed` job can resume the residue.
|
|
162
|
+
|
|
163
|
+
### Git integration (`--git-commit`)
|
|
164
|
+
|
|
165
|
+
Pair `dep-up-surgeon` with git so every successful upgrade lands as its own atomic commit — perfect for code-review-friendly auto-update PRs.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# One commit per upgrade (best for review).
|
|
169
|
+
npx dep-up-surgeon --workspaces --git-commit
|
|
170
|
+
|
|
171
|
+
# One commit per workspace target (squashed) on a fresh branch.
|
|
172
|
+
npx dep-up-surgeon --workspaces \
|
|
173
|
+
--git-commit --git-commit-mode per-target \
|
|
174
|
+
--git-branch "deps/auto-$(date +%Y-%m-%d)"
|
|
175
|
+
|
|
176
|
+
# CI bot: per-success commits, Conventional Commits prefix, signed.
|
|
177
|
+
npx dep-up-surgeon --workspaces --ci \
|
|
178
|
+
--git-commit \
|
|
179
|
+
--git-commit-prefix "chore(deps): " \
|
|
180
|
+
--git-sign
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Three commit modes:**
|
|
184
|
+
|
|
185
|
+
- **`per-success` (default)** — one commit per upgrade. Each commit contains exactly the `package.json` + lockfile diff for one dependency. Trivial to revert any single bump (`git revert <sha>`) and trivially reviewable in a PR. Linked-group upgrades (e.g. `react` + `react-dom`) still land as one commit since they were a single install.
|
|
186
|
+
- **`per-target`** — one commit per workspace target, listing every successful upgrade in the commit body. Useful for monorepos where you want each member's bumps grouped.
|
|
187
|
+
- **`all`** — one commit at the end with everything. Good for tiny single-package projects; avoid in monorepos.
|
|
188
|
+
|
|
189
|
+
**Safety:**
|
|
190
|
+
|
|
191
|
+
- Refuses to start on a **dirty working tree** unless you pass `--git-allow-dirty`. We don't want to accidentally commit your WIP.
|
|
192
|
+
- Only stages `package.json` + the lockfile — **never `git add -A`**. Files modified by `prepare`/`postinstall` hooks (e.g. `.husky/`) or other side effects of `npm install` stay uncommitted.
|
|
193
|
+
- Errors out cleanly when not in a git repo (instead of silently skipping).
|
|
194
|
+
- Skipped silently in `--dry-run` (no upgrades happen → nothing to commit).
|
|
195
|
+
- A failed `git commit` (signing rejected, pre-commit hook refused, etc.) is recorded as `commits[].ok === false` in the JSON report with the git stderr — the upgrade itself is **never rolled back** because of a commit failure.
|
|
196
|
+
|
|
197
|
+
**Concurrency-safe.** `--git-commit` works fine with `--workspaces --concurrency 8`: the same async mutex that serializes installs also serializes git invocations, so two targets can't race the index.
|
|
198
|
+
|
|
199
|
+
**Structured report.** Every commit attempt (success or failure) appears under `commits` in `--json` output:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"gitCommitMode": "per-success",
|
|
204
|
+
"commits": [
|
|
205
|
+
{
|
|
206
|
+
"ok": true,
|
|
207
|
+
"sha": "a1b2c3d",
|
|
208
|
+
"message": "deps: bump axios from ^1.6.0 to ^1.7.2",
|
|
209
|
+
"files": ["package.json", "package-lock.json"],
|
|
210
|
+
"workspace": "root"
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Changelog excerpts
|
|
217
|
+
|
|
218
|
+
Every successful upgrade can be annotated with the package's release notes so reviewers don't have to open five GitHub tabs per PR. Enabled by default when `--git-commit` or `--summary` is set; disable with `--no-changelog`.
|
|
219
|
+
|
|
220
|
+
- **Source.** First preference is the **GitHub Releases API** (`GET /repos/:owner/:repo/releases/tags/:tag`), resolved from the package's `repository` field in its `package.json`. Fallback is the `CHANGELOG.md` extracted from the published tarball via `pacote.extract` — the matching version section is parsed out with a Markdown-aware heading scanner (handles `## 1.2.3`, `## [1.2.3] - 2024-...`, `## v1.2.3`, etc.).
|
|
221
|
+
- **Where it shows up.** In `--git-commit-mode per-success`, the excerpt is embedded directly in the commit body. In `per-target` / `all` modes it collapses to a compact `See: <release-url>` footer so the commit doesn't balloon. `--summary md` / `--summary html` renders each excerpt in a collapsible `<details>` block — clean in PR bodies, compact in GitHub's Job Summary.
|
|
222
|
+
- **Caching & resilience.** A run-local cache deduplicates fetches across workspaces. Network errors, missing tags, private repos, and malformed `CHANGELOG.md` files are all silently skipped — a missing excerpt never fails a commit.
|
|
223
|
+
- **GitHub auth.** Anonymous GitHub API requests are rate-limited to 60/hour. Set `GITHUB_TOKEN` (or `GH_TOKEN`) in the environment to lift that to 5,000/hour — `dep-up-surgeon` uses it automatically for changelog fetches and nothing else.
|
|
224
|
+
|
|
225
|
+
### Security-first mode
|
|
226
|
+
|
|
227
|
+
`--security-only` flips the tool from "bump everything safely" to "bump only packages with known CVEs". Competes directly with Dependabot's security-alert surface, but runs locally and respects your validator / policy / link groups.
|
|
228
|
+
|
|
229
|
+
1. Runs `npm audit --json` (or `pnpm audit --json` / `yarn audit` depending on the detected manager) **before** the upgrade plan is built.
|
|
230
|
+
2. Filters the audit to advisories at or above `--min-severity <low|moderate|high|critical>`.
|
|
231
|
+
3. Builds a `restrictToNames` set from the vulnerable package names and passes it to the engine — every other dependency gets added to the ignore list automatically (visible as `reason: "ignored"` in the report).
|
|
232
|
+
4. Attaches the severity + advisory ID + title to every upgraded record's `security` field, which the CLI then propagates into:
|
|
233
|
+
- **Commit subjects**: `deps: [security:high] bump axios from 1.6.0 to 1.7.2`
|
|
234
|
+
- **Commit bodies**: full advisory ID, URL, and title
|
|
235
|
+
- **`--summary`**: a prominent **Security fixes** table above the normal upgraded table
|
|
236
|
+
- **`--json`**: `upgraded[].security = { severity, ids, url, title, vulnerableRange, recommendedVersion }`
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Only critical + high; one commit per CVE on a dedicated branch.
|
|
240
|
+
npx dep-up-surgeon --workspaces --security-only --min-severity high \
|
|
241
|
+
--git-commit --git-commit-mode per-success \
|
|
242
|
+
--git-branch "deps/security-$(date +%Y-%m-%d)"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Policy engine (policy-as-code)
|
|
246
|
+
|
|
247
|
+
Drop a `.dep-up-surgeon.policy.yaml` (or `.json`) in the repo root to encode upgrade rules that survive across runs and humans. Loaded automatically on startup; violations are reported per-package and the engine skips the offending bumps instead of failing.
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
# .dep-up-surgeon.policy.yaml
|
|
251
|
+
freeze:
|
|
252
|
+
- pattern: react # never touch it
|
|
253
|
+
reason: "React 18 pinned until Q3 refactor"
|
|
254
|
+
- pattern: "@types/*" # wildcard — freezes every @types/* scope
|
|
255
|
+
maxVersion:
|
|
256
|
+
- pattern: next
|
|
257
|
+
range: "<=14" # refuse anything outside this semver range
|
|
258
|
+
allowMajorAfter:
|
|
259
|
+
- pattern: eslint
|
|
260
|
+
date: "2026-06-01" # patch/minor OK now, majors blocked until the date
|
|
261
|
+
requireReviewers: 2 # metadata: surfaced in --summary / --json for your bot to consume
|
|
262
|
+
autoMerge: false # metadata: ditto
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**How rules interact**
|
|
266
|
+
|
|
267
|
+
- **`freeze`** always wins. Exact names go straight into the ignore list; wildcards are matched against the scanned deps inside the engine so rules like `@types/*` don't have to be unrolled by hand. Freezes produce a `reason: "policy"` skip record with the originating pattern.
|
|
268
|
+
- **`maxVersion`** caps the candidate list. If no candidate satisfies the range, the package is skipped with `reason: "policy"` — it won't degrade to a no-op install.
|
|
269
|
+
- **`allowMajorAfter`** blocks **cross-major** bumps until the specified date (checked against `Date.now()`), demoting the candidate to the newest in-major version. Patch/minor still flow through normally.
|
|
270
|
+
- **`requireReviewers`** and **`autoMerge`** are **metadata only** — attached to the `policy` block of `--json` + `--summary` for downstream automation (GitHub Actions PR-opener, the SaaS bot, etc.) to consume.
|
|
271
|
+
|
|
272
|
+
Every applied rule appears in the **Policy** section of `--summary` and under `policy.applied` / `policy.frozen` / `policy.warnings` in `--json`, so audits show exactly which rule blocked which package.
|
|
273
|
+
|
|
274
|
+
### Blast radius
|
|
275
|
+
|
|
276
|
+
Before handing the PR to a reviewer, `dep-up-surgeon` can list **which of your own source files actually import each upgraded package**. Surfaced automatically under `--summary`; attach it to `--json` too with `--blast-radius`.
|
|
277
|
+
|
|
278
|
+
- **Scans**: `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.mts`, `.cts`, `.vue`, `.svelte`, `.astro`.
|
|
279
|
+
- **Skips**: `node_modules`, `dist`, `build`, `coverage`, `.git`, `.next`, `.turbo`, `.vercel`, `.cache`, `.parcel-cache`, `out`, `.output`.
|
|
280
|
+
- **Detects**: ES imports (`import x from '<pkg>'`), re-exports (`export … from '<pkg>'`), CommonJS `require('<pkg>')`, dynamic `import('<pkg>')`, and subpath imports (`from '<pkg>/sub'` still counts as a hit on `<pkg>`). Word-boundary safe — looking for `react` does not falsely match `react-dom`; looking for `@types/node` does not match `@types/node-ipc`.
|
|
281
|
+
- **Output**: per-package `{ total, truncated, files[] }` entries in `upgraded[].blastRadius`, plus a collapsible per-package list in the Markdown / HTML summary. Caps at 20 file paths per package by default; `total` keeps counting past the cap so the summary can honestly say "used in 134 files".
|
|
282
|
+
- **Cost**: a single pass over the tree, at most 1 MB read per file, parallel I/O (default concurrency 8). Failures are non-fatal — a broken symlink never aborts the run. Turn it off in huge monorepos with `--no-blast-radius`.
|
|
283
|
+
|
|
284
|
+
### Workspaces & package managers
|
|
285
|
+
|
|
286
|
+
`dep-up-surgeon` is **workspace-aware**:
|
|
287
|
+
|
|
288
|
+
- **Detection.** On startup the tool resolves the **package manager** (`npm` / `pnpm` / `yarn`) by reading, in order: the `--package-manager` flag, the `packageManager` field in `package.json`, the lockfile (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm), the presence of `pnpm-workspace.yaml`, and finally falls back to `npm`. **Workspace globs** are read from `workspaces` (npm/yarn — both array and `{ packages: [...] }` forms are supported) **or** `pnpm-workspace.yaml` (`packages:` list).
|
|
289
|
+
- **Install + validator follow the manager.** `<mgr> install` runs after each bump and the default validator becomes `<mgr> test` → `<mgr> run build` (yarn classic uses `yarn build`). Override with `--validate "<cmd>"` if you need something different (e.g. `pnpm -r build`).
|
|
290
|
+
- **Workspace-internal deps are skipped automatically.** If a dependency name matches a local workspace package, the tool does not try to resolve it from the npm registry — it appears in the report as `skipped` with `detail: "workspace-internal dep …"`. Pass `--include-workspace-deps` to override (useful when those packages are **also** published).
|
|
291
|
+
- **Workspace child traversal (`--workspaces` / `--workspaces-only` / `--workspace <names>`).** By default only the **root** `package.json` is mutated. With `--workspaces`, the tool **also** scans every member's `package.json` (one engine pass per file), but **install + validation always run from the workspace root** so the lockfile resolves correctly and the validator sees the entire monorepo. Pre-flight runs **once** at the workspace root regardless of how many targets are traversed. Every `upgraded` / `failed` row in the report is tagged with a `workspace` field (`"root"` or the member's package `name`) so you can tell at a glance which `package.json` produced each change.
|
|
292
|
+
- **Install mode (`--install-mode root|filtered`).** Default is `root`: every per-child mutation triggers a full `<mgr> install` from the workspace root — slow on large monorepos but supported by every package manager and impossible to misconfigure. Pass `--install-mode filtered` to rewrite per-child installs to their workspace-scoped form so only the affected member is resolved/linked:
|
|
293
|
+
- **npm 7+** → `npm install --workspace <name>`
|
|
294
|
+
- **pnpm** → `pnpm install --filter <name>`
|
|
295
|
+
- **yarn berry (v2+) with [`@yarnpkg/plugin-workspace-tools`](https://yarnpkg.com/cli/workspaces/focus)** → `yarn workspaces focus <name>` (install the plugin once with `yarn plugin import workspace-tools`)
|
|
296
|
+
- **yarn classic (v1.x)** → falls back to a full root install with a one-time warning suggesting an upgrade to yarn berry
|
|
297
|
+
- **yarn berry without the plugin** → falls back to a full root install with a one-time warning telling you the exact `yarn plugin import` command to fix it
|
|
298
|
+
|
|
299
|
+
The yarn capability is auto-probed at startup (`yarn --version` + `yarn workspaces focus --help`) and surfaced as `project.yarnMajorVersion` and `project.yarnSupportsFocus` in `--json`. The mode actually used is recorded as `installMode` in the report, and the exact filtered command appears under `failed[].install.command` when an upgrade rolls back.
|
|
300
|
+
- **Parallel target traversal (`--concurrency <n>`).** With more than one target, pass `--concurrency 4` (or up to `16`) to run target **scans + plans** concurrently. Registry IO (`pacote.manifest` / `pacote.packument`) is the slow part of each engine pass and is fully parallel-safe — overlapping it across targets gives a real wall-clock speedup on monorepos with many workspaces. **Installs and validations stay serialized** under a shared async mutex because they all touch the same root lockfile and `node_modules`; running them in parallel would corrupt the lockfile. An in-process registry cache (always on) also deduplicates fetches so the same dependency name in many workspaces only hits the network once. The effective concurrency is reported as `concurrency` in `--json` output. Parallelism requires `--json`; non-JSON mode silently downgrades to `1` to keep per-target log lines legible.
|
|
301
|
+
|
|
302
|
+
The detected manager + members are surfaced under `project` in `--json` output:
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
"project": {
|
|
306
|
+
"manager": "pnpm",
|
|
307
|
+
"managerVersion": "9.10.0",
|
|
308
|
+
"managerSource": "package.json:packageManager",
|
|
309
|
+
"lockfile": "pnpm-lock.yaml",
|
|
310
|
+
"hasWorkspaces": true,
|
|
311
|
+
"workspaceGlobs": ["packages/*", "apps/*"],
|
|
312
|
+
"workspaceMembers": [
|
|
313
|
+
{ "name": "@org/core", "dir": "/path/to/repo/packages/core" }
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
```
|
|
78
317
|
|
|
79
318
|
### What gets scanned
|
|
80
319
|
|
|
@@ -121,12 +360,10 @@ Create `.dep-up-surgeonrc` in the project root:
|
|
|
121
360
|
"linkedGroups": [
|
|
122
361
|
{
|
|
123
362
|
"id": "my-batch",
|
|
124
|
-
"packages": [
|
|
125
|
-
"package-a",
|
|
126
|
-
"package-b"
|
|
127
|
-
]
|
|
363
|
+
"packages": ["package-a", "package-b"]
|
|
128
364
|
}
|
|
129
|
-
]
|
|
365
|
+
],
|
|
366
|
+
"validate": "tsc -p tsconfig.json --noEmit"
|
|
130
367
|
}
|
|
131
368
|
```
|
|
132
369
|
|
|
@@ -134,11 +371,23 @@ Ignored packages are never upgraded. The CLI `--ignore` list is merged with this
|
|
|
134
371
|
|
|
135
372
|
**`linkedGroups`** defines **forced** batches **before** the dynamic graph runs (exact npm package names).
|
|
136
373
|
|
|
374
|
+
**`validate`** overrides the validator command. Accepts either a shell string (`"tsc --noEmit"`) or an object: `{ "command": "pnpm -r build" }` or `{ "skip": true }`. CLI flags (`--validate`, `--no-validate`) always win over this file.
|
|
375
|
+
|
|
137
376
|
## JSON report (`--json`)
|
|
138
377
|
|
|
139
378
|
Stdout is a single JSON object including:
|
|
140
379
|
|
|
141
380
|
- **`upgraded`**, **`skipped`**, **`failed`**, **`conflicts`** (parsed from npm output), **`unresolved`** (failed entries), **`groups`** (planned linked groups: ids and package names), and **`ignored`**.
|
|
381
|
+
- **`preflight`** (when not skipped): `{ ok, command, exitCode, lastLines, source }` for the unchanged-tree validator run.
|
|
382
|
+
- **`preflightAborted: true`** if the run aborted before any upgrade.
|
|
383
|
+
- For each **failed** entry caused by the validator, a `validation` block with `{ command, exitCode, lastLines, source }` so you can tell at a glance whether the failure was a project-side script crash or an actual dependency conflict.
|
|
384
|
+
- For **every** failed entry, an `install` block with `{ command, exitCode, lastLines, ok }` capturing the install step that triggered the failure. `ok: true` means the installer process exited 0 but a post-install conflict scan triggered the rollback (peer warnings, "Conflicting peer dependency", etc.); `ok: false` means the installer itself crashed. `lastLines` is the **last ~40 lines** of combined stdout/stderr — usually enough to include the actual `npm ERR!` / pnpm / yarn footer.
|
|
385
|
+
- **`targets`**: list of `{ label, cwd, packageJson }` entries describing which `package.json` files were processed. With `--workspaces` / `--workspace <names>` this contains multiple entries (`label` = `"root"` or the workspace member's package `name`); without those flags it is a single root entry. Each `upgraded` / `failed` row also carries a matching `workspace` field. When more than one target is traversed, `groups[].id` values are namespaced as `"<workspace>::<group-id>"` so they stay unique across the aggregated report.
|
|
386
|
+
- The `failed[].reason` field uses `validation-script` for build/test script crashes and `validation-conflicts` for npm-reported peer issues; `peer` and `install` retain their meanings.
|
|
387
|
+
- **`project`**: `{ manager, managerVersion?, managerSource, lockfile?, hasWorkspaces, workspaceGlobs[], workspaceMembers[], yarnMajorVersion?, yarnSupportsFocus? }` — see **Workspaces & package managers** above. The two `yarn*` fields are only present when the active manager is yarn AND the project has workspaces (they drive the `--install-mode filtered` decision).
|
|
388
|
+
- **`installMode`**: `"root"` or `"filtered"` — the workspace install strategy actually used for this run.
|
|
389
|
+
- **`concurrency`**: effective number of targets traversed in parallel (only included when `> 1`).
|
|
390
|
+
- **`commits`** + **`gitCommitMode`**: only present when `--git-commit` was set. See **Git integration (`--git-commit`)** above for the full schema (each `commits[]` entry includes `ok`, `sha?`, `message`, `files`, `workspace?`, `groupId?`, and `error?` for failed signing / hook rejections).
|
|
142
391
|
|
|
143
392
|
Use this for CI or tooling that needs structured results.
|
|
144
393
|
|
|
@@ -167,14 +416,25 @@ Use this for CI or tooling that needs structured results.
|
|
|
167
416
|
|
|
168
417
|
| Area | Role |
|
|
169
418
|
|------|------|
|
|
170
|
-
| `
|
|
171
|
-
| `core/
|
|
172
|
-
| `core/
|
|
173
|
-
| `core/
|
|
419
|
+
| `cli.ts` | CLI entry point: parses flags via `commander`, loads `.dep-up-surgeonrc`, wires the orchestrator + Git flow + summary writer + persisted last-run + retry-failed mode. |
|
|
420
|
+
| `core/upgrader.ts` | Main upgrade engine + multi-target orchestrator (`runUpgradeEngine` / `runUpgradeFlow`). Owns the per-package and per-batch attempt loops, rollback, install/validation lock, parallel target traversal. |
|
|
421
|
+
| `core/workspaces.ts` | Detect package manager (`npm` / `pnpm` / `yarn`) + workspace topology, expand workspace globs, probe yarn capabilities (`yarnMajorVersion` + `yarnSupportsFocus` for `yarn workspaces focus`). |
|
|
422
|
+
| `core/scanner.ts` | Walk a `package.json` and emit the candidate dependency list (deps + devDeps + peerDeps + optionalDeps, registry-only). |
|
|
423
|
+
| `core/graph.ts` | Build the upgrade graph from `package.json` + published **peerDependencies** only (+ `@types/*` pairing); connected components → batches. |
|
|
424
|
+
| `core/dynamicGroups.ts` / `core/groups.ts` | Custom `linkedGroups` from `.dep-up-surgeonrc` merged with graph-driven `LinkedGroup[]`. |
|
|
425
|
+
| `core/conflictParser.ts` / `conflictAnalyzer.ts` | Parse and classify npm/pnpm/yarn log lines; decide whether a “successful” install should be rolled back. |
|
|
426
|
+
| `core/resolver.ts` / `utils/versionFallback.ts` | Semver helpers + per-major / per-minor fallback walking when `@latest` doesn't stick. |
|
|
427
|
+
| `core/validator.ts` | Pre-flight + per-attempt validator runner. Surfaces `ValidationDiagnostic` (`command`, `exitCode`, `lastLines`, `source`) on every failure. |
|
|
174
428
|
| `core/retryEngine.ts` | Generic retry helper. |
|
|
175
|
-
| `cli/interactive.ts` | `prompts`-based choices for failed groups. |
|
|
176
|
-
| `cli/report.ts` | Structured report builder
|
|
177
|
-
| `
|
|
429
|
+
| `cli/interactive.ts` | `prompts`-based choices for failed packages and failed linked groups. |
|
|
430
|
+
| `cli/report.ts` | Structured `--json` report builder + on-screen summary printer. |
|
|
431
|
+
| `cli/summary.ts` | `--summary md\|html` writer; appends to `$GITHUB_STEP_SUMMARY` when present. |
|
|
432
|
+
| `cli/lastRun.ts` | Persist `.dep-up-surgeon.last-run.json` after every run + read it back for `--retry-failed` (terminal-vs-retryable failure classification). |
|
|
433
|
+
| `cli/git.ts` | Low-level git wrappers (`isGitRepo`, `gitAdd`, `gitCommit`, branch helpers) + commit message formatters for the three commit modes. |
|
|
434
|
+
| `cli/gitFlow.ts` | `--git-commit` controller: pre-flight checks (clean tree, branch checkout), buffers per-target / per-run changes, dispatches commits via the engine's `onUpgradeApplied` / `onTargetComplete` hooks under the install lock. |
|
|
435
|
+
| `utils/npm.ts` | `installCommand` (npm/pnpm/yarn variants incl. `yarn workspaces focus`), `runInstall`, registry helpers (`fetchLatestVersion`, `fetchAllPublishedVersions`), peer-conflict + ESM-vs-CJS heuristics. |
|
|
436
|
+
| `utils/registryCache.ts` / `utils/concurrency.ts` | In-process manifest/packument cache + bounded parallel fetch / per-target worker pool + async mutex used to serialize installs and git operations. |
|
|
437
|
+
| `config/loadConfig.ts` | Read + validate `.dep-up-surgeonrc` (`ignore`, `linkedGroups`, `validate`). |
|
|
178
438
|
|
|
179
439
|
## Testing
|
|
180
440
|
|
|
@@ -182,7 +442,7 @@ Use this for CI or tooling that needs structured results.
|
|
|
182
442
|
npm test
|
|
183
443
|
```
|
|
184
444
|
|
|
185
|
-
Runs **unit tests** (conflict parsing,
|
|
445
|
+
Runs **unit tests** (conflict parsing, npm output samples, workspace + yarn-capability detection, install command builder, concurrency primitives, summary writer, persisted last-run + retry classification, git helpers + flow controller — all offline) **and fixture integration tests** (`test/fixtures/*/package.json` exercised with `dep-up-surgeon --dry-run --json`). The fixture suite requires **network** access to the npm registry. See `test/fixtures/README.md`. Run only the offline suite with `npm run test:unit`.
|
|
186
446
|
|
|
187
447
|
## Development
|
|
188
448
|
|
|
@@ -199,8 +459,10 @@ The compiled entry is `dist/cli.js` (see `"bin"` in `package.json`).
|
|
|
199
459
|
|
|
200
460
|
## Future work (tracked in code)
|
|
201
461
|
|
|
202
|
-
-
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
462
|
+
- Auto-open a PR after `--git-commit --git-branch` (today the user / CI step runs `gh pr create` themselves)
|
|
463
|
+
- Deeper automatic resolution using peer-range intersection across a batch
|
|
464
|
+
- Renovate-style scheduling helpers (cron / day-of-week filters, grouping rules)
|
|
465
|
+
- True parallel installs in monorepos that don't share a root lockfile (e.g. nohoist setups), going beyond today's parallel scan + serial install model
|
|
466
|
+
- AI-assisted failure explanation: feed `install.lastLines` + `validation.lastLines` to an LLM and attach a one-sentence "why this broke" note to failed records
|
|
467
|
+
- Integration catalog: webhooks into Slack / Discord / Linear / Jira so the bot can ping a channel when a security bump lands, not just a GitHub PR
|
|
468
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch changelog enrichment for the structured report. Called once after the upgrade flow
|
|
3
|
+
* completes to fill in `UpgradeRecord.changelog` for every successful upgrade, so the summary
|
|
4
|
+
* writer (`--summary md|html`) and CI consumers can show release notes per package.
|
|
5
|
+
*
|
|
6
|
+
* Design notes:
|
|
7
|
+
* - Network-bound; we cap parallelism with `runWithConcurrency` so a monorepo that upgrades
|
|
8
|
+
* 50 packages doesn't fire 50 simultaneous GitHub API calls.
|
|
9
|
+
* - Every failure is swallowed. Enrichment is a courtesy feature — a 503 from the registry
|
|
10
|
+
* or a missing `CHANGELOG.md` must NEVER mutate the exit code.
|
|
11
|
+
* - The cache is shared with the git-commit path when both are active, so a package committed
|
|
12
|
+
* with its changelog during the run is not fetched a second time for the summary.
|
|
13
|
+
*/
|
|
14
|
+
import type { UpgradeRecord } from '../types.js';
|
|
15
|
+
import { type ChangelogCache, type ChangelogFetchers } from '../utils/changelog.js';
|
|
16
|
+
export interface EnrichChangelogOptions {
|
|
17
|
+
cache: ChangelogCache;
|
|
18
|
+
/** Max concurrent fetches. Kept small to respect GitHub's unauth rate limit (~60/h/IP). */
|
|
19
|
+
concurrency?: number;
|
|
20
|
+
fetchers?: ChangelogFetchers;
|
|
21
|
+
githubToken?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Mutate `records` in place: attach `.changelog` to every successful upgrade we can find a
|
|
25
|
+
* release note for. Records without a real semver `to` (e.g. `workspace:*`) are skipped.
|
|
26
|
+
*/
|
|
27
|
+
export declare function enrichWithChangelogs(records: UpgradeRecord[], opts: EnrichChangelogOptions): Promise<void>;
|
|
28
|
+
//# sourceMappingURL=changelogEnricher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelogEnricher.d.ts","sourceRoot":"","sources":["../../src/cli/changelogEnricher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,cAAc,CAAC;IACtB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,aAAa,EAAE,EACxB,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { runWithConcurrency } from '../utils/concurrency.js';
|
|
2
|
+
import { fetchChangelog, } from '../utils/changelog.js';
|
|
3
|
+
/**
|
|
4
|
+
* Mutate `records` in place: attach `.changelog` to every successful upgrade we can find a
|
|
5
|
+
* release note for. Records without a real semver `to` (e.g. `workspace:*`) are skipped.
|
|
6
|
+
*/
|
|
7
|
+
export async function enrichWithChangelogs(records, opts) {
|
|
8
|
+
const eligible = records.filter((r) => {
|
|
9
|
+
if (!r.success || r.skipped) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (r.changelog) {
|
|
13
|
+
// Already enriched (e.g. by the git-commit path) — skip the duplicate fetch.
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const clean = (r.to ?? '').trim().replace(/^[\^~=]/, '');
|
|
17
|
+
return /^\d+\.\d+\.\d+/.test(clean);
|
|
18
|
+
});
|
|
19
|
+
if (eligible.length === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const concurrency = Math.min(Math.max(1, Math.floor(opts.concurrency ?? 4)), 8);
|
|
23
|
+
await runWithConcurrency(eligible, concurrency, async (r) => {
|
|
24
|
+
const clean = (r.to ?? '').trim().replace(/^[\^~=]/, '');
|
|
25
|
+
try {
|
|
26
|
+
const excerpt = await fetchChangelog({
|
|
27
|
+
packageName: r.name,
|
|
28
|
+
toVersion: clean,
|
|
29
|
+
fromVersion: r.from,
|
|
30
|
+
cache: opts.cache,
|
|
31
|
+
fetchers: opts.fetchers,
|
|
32
|
+
githubToken: opts.githubToken,
|
|
33
|
+
});
|
|
34
|
+
if (excerpt) {
|
|
35
|
+
r.changelog = {
|
|
36
|
+
source: excerpt.source,
|
|
37
|
+
url: excerpt.url,
|
|
38
|
+
body: excerpt.body,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// swallow: enrichment is best-effort
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=changelogEnricher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelogEnricher.js","sourceRoot":"","sources":["../../src/cli/changelogEnricher.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,cAAc,GAGf,MAAM,uBAAuB,CAAC;AAU/B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAwB,EACxB,IAA4B;IAE5B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACpC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;YAChB,6EAA6E;YAC7E,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACzD,OAAO,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEhF,MAAM,kBAAkB,CACtB,QAAQ,EACR,WAAW,EACX,KAAK,EAAE,CAAC,EAAE,EAAE;QACV,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC;gBACnC,WAAW,EAAE,CAAC,CAAC,IAAI;gBACnB,SAAS,EAAE,KAAK;gBAChB,WAAW,EAAE,CAAC,CAAC,IAAI;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,WAAW,EAAE,IAAI,CAAC,WAAW;aAC9B,CAAC,CAAC;YACH,IAAI,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,SAAS,GAAG;oBACZ,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,GAAG,EAAE,OAAO,CAAC,GAAG;oBAChB,IAAI,EAAE,OAAO,CAAC,IAAI;iBACnB,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { PackageManager } from '../core/workspaces.js';
|
|
2
|
+
export type GitCommitMode = 'per-success' | 'per-target' | 'all';
|
|
3
|
+
export interface GitOptions {
|
|
4
|
+
/** Working directory used for git invocations (typically the workspace root). */
|
|
5
|
+
cwd: string;
|
|
6
|
+
/** When true, pass `--gpg-sign` to every commit. */
|
|
7
|
+
sign?: boolean;
|
|
8
|
+
/** Override commit author / committer (purely for tests; never wired to the CLI). */
|
|
9
|
+
authorEmail?: string;
|
|
10
|
+
authorName?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GitCommitResult {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
/** Short commit SHA (`git rev-parse --short HEAD`) when ok; undefined when commit failed. */
|
|
15
|
+
sha?: string;
|
|
16
|
+
message: string;
|
|
17
|
+
/** Files that were `git add`-ed for this commit (repo-root-relative). */
|
|
18
|
+
files: string[];
|
|
19
|
+
/** Stderr output from `git commit` when it failed (for surfacing to the user). */
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
/** True when `cwd` is inside a git working tree. */
|
|
23
|
+
export declare function isGitRepo(cwd: string): Promise<boolean>;
|
|
24
|
+
/** Absolute path to the repo's top-level directory (`git rev-parse --show-toplevel`). */
|
|
25
|
+
export declare function getRepoRoot(cwd: string): Promise<string | undefined>;
|
|
26
|
+
/**
|
|
27
|
+
* List of repo-root-relative paths that have uncommitted changes (modified, added, deleted,
|
|
28
|
+
* untracked but not in `.gitignore`). Used by the pre-flight check to refuse running on a
|
|
29
|
+
* dirty tree unless the user passed `--git-allow-dirty`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getUncommittedFiles(cwd: string): Promise<string[]>;
|
|
32
|
+
export declare function getCurrentBranch(cwd: string): Promise<string | undefined>;
|
|
33
|
+
/**
|
|
34
|
+
* Create-and-checkout `branch`. If the branch already exists, just checkout (no -b). Returns
|
|
35
|
+
* the previous branch name for reporting, or `undefined` on first-commit-less repos.
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkoutBranch(cwd: string, branch: string): Promise<string | undefined>;
|
|
38
|
+
/**
|
|
39
|
+
* Stage the given absolute file paths (any path that doesn't exist anymore is silently dropped
|
|
40
|
+
* — git would error on missing pathspec otherwise, and lockfiles can be absent on first install).
|
|
41
|
+
* Returns the repo-root-relative paths that were actually staged.
|
|
42
|
+
*/
|
|
43
|
+
export declare function gitAdd(opts: GitOptions, files: string[]): Promise<string[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Create a commit with `message`. Returns `{ ok: false, error }` when there's nothing to
|
|
46
|
+
* commit (clean index) OR when git itself refuses (pre-commit hook, signing, etc.) — callers
|
|
47
|
+
* use the same path either way: log + record + continue.
|
|
48
|
+
*/
|
|
49
|
+
export declare function gitCommit(opts: GitOptions, message: string, files: string[]): Promise<GitCommitResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Default lockfile basename for the detected package manager. We commit it alongside the
|
|
52
|
+
* mutated `package.json` so the commit is self-contained (anyone checking out that SHA can
|
|
53
|
+
* `<mgr> install` and get the exact tree we tested).
|
|
54
|
+
*/
|
|
55
|
+
export declare function lockfileBasenameFor(manager: PackageManager): string;
|
|
56
|
+
export interface UpgradeChange {
|
|
57
|
+
name: string;
|
|
58
|
+
/** Old version range (e.g. `^1.6.0`). */
|
|
59
|
+
from: string;
|
|
60
|
+
/** New version range (e.g. `^1.7.2`). */
|
|
61
|
+
to: string;
|
|
62
|
+
/** Workspace label (`'root'` or member package name). */
|
|
63
|
+
workspace?: string;
|
|
64
|
+
/** Linked-group id when this change was part of a batch. */
|
|
65
|
+
groupId?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Optional changelog excerpt body (from GitHub Releases or the package tarball CHANGELOG.md).
|
|
68
|
+
* When present, formatters append it to the commit body under a clearly demarcated section so
|
|
69
|
+
* reviewers can see *why* the version moved without opening a browser tab.
|
|
70
|
+
*/
|
|
71
|
+
changelog?: {
|
|
72
|
+
source: 'github-release' | 'changelog.md';
|
|
73
|
+
url?: string;
|
|
74
|
+
body: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Security metadata from `--security-only` audit. Surfaced in commit subjects and bodies so
|
|
78
|
+
* the merge queue / code-review tools can pivot on severity + advisory id.
|
|
79
|
+
*/
|
|
80
|
+
security?: {
|
|
81
|
+
severity: 'low' | 'moderate' | 'high' | 'critical';
|
|
82
|
+
ids: string[];
|
|
83
|
+
url?: string;
|
|
84
|
+
title?: string;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export declare function formatPerSuccessMessage(prefix: string, changes: UpgradeChange[]): string;
|
|
88
|
+
export declare function formatPerTargetMessage(prefix: string, workspace: string, changes: UpgradeChange[]): string;
|
|
89
|
+
export declare function formatAllInOneMessage(prefix: string, changes: UpgradeChange[]): string;
|
|
90
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/cli/git.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,YAAY,GAAG,KAAK,CAAC;AAEjE,MAAM,WAAW,UAAU;IACzB,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,6FAA6F;IAC7F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG7D;AAED,yFAAyF;AACzF,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG1E;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAexE;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG/E;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7F;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BjF;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,eAAe,CAAC,CAkD1B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAUnE;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE;QACV,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAAC;QAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF;;;OAGG;IACH,QAAQ,CAAC,EAAE;QACT,QAAQ,EAAE,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;QACnD,GAAG,EAAE,MAAM,EAAE,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAmED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CAiBxF;AAoBD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,aAAa,EAAE,GACvB,MAAM,CAcR;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CAkCtF"}
|