dep-up-surgeon 1.6.6 → 2.0.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 +206 -18
- package/dist/cli/git.d.ts +70 -0
- package/dist/cli/git.d.ts.map +1 -0
- package/dist/cli/git.js +238 -0
- package/dist/cli/git.js.map +1 -0
- package/dist/cli/gitFlow.d.ts +44 -0
- package/dist/cli/gitFlow.d.ts.map +1 -0
- package/dist/cli/gitFlow.js +226 -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 +30 -1
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +41 -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 +176 -0
- package/dist/cli/summary.js.map +1 -0
- package/dist/cli.js +273 -14
- 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/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 +181 -3
- package/dist/core/upgrader.d.ts.map +1 -1
- package/dist/core/upgrader.js +560 -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 +155 -1
- package/dist/types.d.ts.map +1 -1
- 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 +3 -3
package/README.md
CHANGED
|
@@ -73,8 +73,175 @@ 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. |
|
|
76
96
|
|
|
77
|
-
Exit code `1` when any upgrade could not be kept (unless `--force`). Fatal errors also exit `1`.
|
|
97
|
+
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`.
|
|
98
|
+
|
|
99
|
+
### Pre-flight check
|
|
100
|
+
|
|
101
|
+
Before mutating any dependency, the CLI runs the resolved validator command **once** against the unchanged tree:
|
|
102
|
+
|
|
103
|
+
- If it **passes**, the run continues normally.
|
|
104
|
+
- 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.
|
|
105
|
+
- To proceed anyway, use `--validate "<cmd>"` to swap the validator, `--no-validate` to skip it, or `--force` to ignore the pre-flight failure.
|
|
106
|
+
|
|
107
|
+
The pre-flight outcome is also surfaced under `preflight` / `preflightAborted` in `--json` output.
|
|
108
|
+
|
|
109
|
+
### Persisted last-run report
|
|
110
|
+
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
### Retry-failed mode (`--retry-failed`)
|
|
114
|
+
|
|
115
|
+
Pass `--retry-failed` to **resume** the previous run instead of starting from scratch:
|
|
116
|
+
|
|
117
|
+
- `dep-up-surgeon` reads `.dep-up-surgeon.last-run.json` and **freezes** every package that either:
|
|
118
|
+
- **succeeded** in the last run (no need to redo work), **or**
|
|
119
|
+
- 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).
|
|
120
|
+
- 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.
|
|
121
|
+
- 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.
|
|
122
|
+
- 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.
|
|
123
|
+
|
|
124
|
+
Typical workflow:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
dep-up-surgeon --workspaces # first pass: lots of moves, some failures
|
|
128
|
+
# fix the script that caused a `validation-script` failure (or accept it)
|
|
129
|
+
dep-up-surgeon --retry-failed # second pass: only retries install/conflict residue
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Summary writer (`--summary <md|html>`)
|
|
133
|
+
|
|
134
|
+
Pass `--summary md` (or `--summary html`) to render a human-friendly report alongside the normal output:
|
|
135
|
+
|
|
136
|
+
- **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.
|
|
137
|
+
- **Explicit destination**: `--summary-file <path>` overrides everything (wins over `$GITHUB_STEP_SUMMARY`).
|
|
138
|
+
- **Default**: `./dep-up-surgeon-summary.<md|html>`.
|
|
139
|
+
|
|
140
|
+
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.
|
|
141
|
+
|
|
142
|
+
### CI / bot mode (`--ci`)
|
|
143
|
+
|
|
144
|
+
`--ci` is a convenience flag for unattended runs (GitHub Actions, GitLab CI, Renovate-style bots). It:
|
|
145
|
+
|
|
146
|
+
- **Disables `--interactive`** unconditionally — never blocks on stdin.
|
|
147
|
+
- **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.
|
|
148
|
+
- **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.
|
|
149
|
+
|
|
150
|
+
Typical GitHub Actions step:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
- name: dep-up-surgeon
|
|
154
|
+
run: npx dep-up-surgeon --workspaces --ci
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
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.
|
|
158
|
+
|
|
159
|
+
### Git integration (`--git-commit`)
|
|
160
|
+
|
|
161
|
+
Pair `dep-up-surgeon` with git so every successful upgrade lands as its own atomic commit — perfect for code-review-friendly auto-update PRs.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# One commit per upgrade (best for review).
|
|
165
|
+
npx dep-up-surgeon --workspaces --git-commit
|
|
166
|
+
|
|
167
|
+
# One commit per workspace target (squashed) on a fresh branch.
|
|
168
|
+
npx dep-up-surgeon --workspaces \
|
|
169
|
+
--git-commit --git-commit-mode per-target \
|
|
170
|
+
--git-branch "deps/auto-$(date +%Y-%m-%d)"
|
|
171
|
+
|
|
172
|
+
# CI bot: per-success commits, Conventional Commits prefix, signed.
|
|
173
|
+
npx dep-up-surgeon --workspaces --ci \
|
|
174
|
+
--git-commit \
|
|
175
|
+
--git-commit-prefix "chore(deps): " \
|
|
176
|
+
--git-sign
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Three commit modes:**
|
|
180
|
+
|
|
181
|
+
- **`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.
|
|
182
|
+
- **`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.
|
|
183
|
+
- **`all`** — one commit at the end with everything. Good for tiny single-package projects; avoid in monorepos.
|
|
184
|
+
|
|
185
|
+
**Safety:**
|
|
186
|
+
|
|
187
|
+
- Refuses to start on a **dirty working tree** unless you pass `--git-allow-dirty`. We don't want to accidentally commit your WIP.
|
|
188
|
+
- 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.
|
|
189
|
+
- Errors out cleanly when not in a git repo (instead of silently skipping).
|
|
190
|
+
- Skipped silently in `--dry-run` (no upgrades happen → nothing to commit).
|
|
191
|
+
- 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.
|
|
192
|
+
|
|
193
|
+
**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.
|
|
194
|
+
|
|
195
|
+
**Structured report.** Every commit attempt (success or failure) appears under `commits` in `--json` output:
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"gitCommitMode": "per-success",
|
|
200
|
+
"commits": [
|
|
201
|
+
{
|
|
202
|
+
"ok": true,
|
|
203
|
+
"sha": "a1b2c3d",
|
|
204
|
+
"message": "deps: bump axios from ^1.6.0 to ^1.7.2",
|
|
205
|
+
"files": ["package.json", "package-lock.json"],
|
|
206
|
+
"workspace": "root"
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Workspaces & package managers
|
|
213
|
+
|
|
214
|
+
`dep-up-surgeon` is **workspace-aware**:
|
|
215
|
+
|
|
216
|
+
- **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).
|
|
217
|
+
- **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`).
|
|
218
|
+
- **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).
|
|
219
|
+
- **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.
|
|
220
|
+
- **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:
|
|
221
|
+
- **npm 7+** → `npm install --workspace <name>`
|
|
222
|
+
- **pnpm** → `pnpm install --filter <name>`
|
|
223
|
+
- **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`)
|
|
224
|
+
- **yarn classic (v1.x)** → falls back to a full root install with a one-time warning suggesting an upgrade to yarn berry
|
|
225
|
+
- **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
|
|
226
|
+
|
|
227
|
+
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.
|
|
228
|
+
- **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.
|
|
229
|
+
|
|
230
|
+
The detected manager + members are surfaced under `project` in `--json` output:
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
"project": {
|
|
234
|
+
"manager": "pnpm",
|
|
235
|
+
"managerVersion": "9.10.0",
|
|
236
|
+
"managerSource": "package.json:packageManager",
|
|
237
|
+
"lockfile": "pnpm-lock.yaml",
|
|
238
|
+
"hasWorkspaces": true,
|
|
239
|
+
"workspaceGlobs": ["packages/*", "apps/*"],
|
|
240
|
+
"workspaceMembers": [
|
|
241
|
+
{ "name": "@org/core", "dir": "/path/to/repo/packages/core" }
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
```
|
|
78
245
|
|
|
79
246
|
### What gets scanned
|
|
80
247
|
|
|
@@ -121,12 +288,10 @@ Create `.dep-up-surgeonrc` in the project root:
|
|
|
121
288
|
"linkedGroups": [
|
|
122
289
|
{
|
|
123
290
|
"id": "my-batch",
|
|
124
|
-
"packages": [
|
|
125
|
-
"package-a",
|
|
126
|
-
"package-b"
|
|
127
|
-
]
|
|
291
|
+
"packages": ["package-a", "package-b"]
|
|
128
292
|
}
|
|
129
|
-
]
|
|
293
|
+
],
|
|
294
|
+
"validate": "tsc -p tsconfig.json --noEmit"
|
|
130
295
|
}
|
|
131
296
|
```
|
|
132
297
|
|
|
@@ -134,11 +299,23 @@ Ignored packages are never upgraded. The CLI `--ignore` list is merged with this
|
|
|
134
299
|
|
|
135
300
|
**`linkedGroups`** defines **forced** batches **before** the dynamic graph runs (exact npm package names).
|
|
136
301
|
|
|
302
|
+
**`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.
|
|
303
|
+
|
|
137
304
|
## JSON report (`--json`)
|
|
138
305
|
|
|
139
306
|
Stdout is a single JSON object including:
|
|
140
307
|
|
|
141
308
|
- **`upgraded`**, **`skipped`**, **`failed`**, **`conflicts`** (parsed from npm output), **`unresolved`** (failed entries), **`groups`** (planned linked groups: ids and package names), and **`ignored`**.
|
|
309
|
+
- **`preflight`** (when not skipped): `{ ok, command, exitCode, lastLines, source }` for the unchanged-tree validator run.
|
|
310
|
+
- **`preflightAborted: true`** if the run aborted before any upgrade.
|
|
311
|
+
- 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.
|
|
312
|
+
- 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.
|
|
313
|
+
- **`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.
|
|
314
|
+
- 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.
|
|
315
|
+
- **`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).
|
|
316
|
+
- **`installMode`**: `"root"` or `"filtered"` — the workspace install strategy actually used for this run.
|
|
317
|
+
- **`concurrency`**: effective number of targets traversed in parallel (only included when `> 1`).
|
|
318
|
+
- **`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
319
|
|
|
143
320
|
Use this for CI or tooling that needs structured results.
|
|
144
321
|
|
|
@@ -167,14 +344,25 @@ Use this for CI or tooling that needs structured results.
|
|
|
167
344
|
|
|
168
345
|
| Area | Role |
|
|
169
346
|
|------|------|
|
|
170
|
-
| `
|
|
171
|
-
| `core/
|
|
172
|
-
| `core/
|
|
173
|
-
| `core/
|
|
347
|
+
| `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. |
|
|
348
|
+
| `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. |
|
|
349
|
+
| `core/workspaces.ts` | Detect package manager (`npm` / `pnpm` / `yarn`) + workspace topology, expand workspace globs, probe yarn capabilities (`yarnMajorVersion` + `yarnSupportsFocus` for `yarn workspaces focus`). |
|
|
350
|
+
| `core/scanner.ts` | Walk a `package.json` and emit the candidate dependency list (deps + devDeps + peerDeps + optionalDeps, registry-only). |
|
|
351
|
+
| `core/graph.ts` | Build the upgrade graph from `package.json` + published **peerDependencies** only (+ `@types/*` pairing); connected components → batches. |
|
|
352
|
+
| `core/dynamicGroups.ts` / `core/groups.ts` | Custom `linkedGroups` from `.dep-up-surgeonrc` merged with graph-driven `LinkedGroup[]`. |
|
|
353
|
+
| `core/conflictParser.ts` / `conflictAnalyzer.ts` | Parse and classify npm/pnpm/yarn log lines; decide whether a “successful” install should be rolled back. |
|
|
354
|
+
| `core/resolver.ts` / `utils/versionFallback.ts` | Semver helpers + per-major / per-minor fallback walking when `@latest` doesn't stick. |
|
|
355
|
+
| `core/validator.ts` | Pre-flight + per-attempt validator runner. Surfaces `ValidationDiagnostic` (`command`, `exitCode`, `lastLines`, `source`) on every failure. |
|
|
174
356
|
| `core/retryEngine.ts` | Generic retry helper. |
|
|
175
|
-
| `cli/interactive.ts` | `prompts`-based choices for failed groups. |
|
|
176
|
-
| `cli/report.ts` | Structured report builder
|
|
177
|
-
| `
|
|
357
|
+
| `cli/interactive.ts` | `prompts`-based choices for failed packages and failed linked groups. |
|
|
358
|
+
| `cli/report.ts` | Structured `--json` report builder + on-screen summary printer. |
|
|
359
|
+
| `cli/summary.ts` | `--summary md\|html` writer; appends to `$GITHUB_STEP_SUMMARY` when present. |
|
|
360
|
+
| `cli/lastRun.ts` | Persist `.dep-up-surgeon.last-run.json` after every run + read it back for `--retry-failed` (terminal-vs-retryable failure classification). |
|
|
361
|
+
| `cli/git.ts` | Low-level git wrappers (`isGitRepo`, `gitAdd`, `gitCommit`, branch helpers) + commit message formatters for the three commit modes. |
|
|
362
|
+
| `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. |
|
|
363
|
+
| `utils/npm.ts` | `installCommand` (npm/pnpm/yarn variants incl. `yarn workspaces focus`), `runInstall`, registry helpers (`fetchLatestVersion`, `fetchAllPublishedVersions`), peer-conflict + ESM-vs-CJS heuristics. |
|
|
364
|
+
| `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. |
|
|
365
|
+
| `config/loadConfig.ts` | Read + validate `.dep-up-surgeonrc` (`ignore`, `linkedGroups`, `validate`). |
|
|
178
366
|
|
|
179
367
|
## Testing
|
|
180
368
|
|
|
@@ -182,7 +370,7 @@ Use this for CI or tooling that needs structured results.
|
|
|
182
370
|
npm test
|
|
183
371
|
```
|
|
184
372
|
|
|
185
|
-
Runs **unit tests** (conflict parsing,
|
|
373
|
+
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
374
|
|
|
187
375
|
## Development
|
|
188
376
|
|
|
@@ -199,8 +387,8 @@ The compiled entry is `dist/cli.js` (see `"bin"` in `package.json`).
|
|
|
199
387
|
|
|
200
388
|
## Future work (tracked in code)
|
|
201
389
|
|
|
202
|
-
-
|
|
203
|
-
-
|
|
204
|
-
- pnpm / Yarn
|
|
205
|
-
- Git integration (commit per success)
|
|
390
|
+
- Auto-open a PR after `--git-commit --git-branch` (today the user / CI step runs `gh pr create` themselves)
|
|
391
|
+
- Per-commit changelog excerpt in the commit body (pull `CHANGELOG.md` / GitHub Releases for the bumped version)
|
|
206
392
|
- Deeper automatic resolution using peer-range intersection across a batch
|
|
393
|
+
- Renovate-style scheduling helpers (cron / day-of-week filters, grouping rules)
|
|
394
|
+
- 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
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
export declare function formatPerSuccessMessage(prefix: string, changes: UpgradeChange[]): string;
|
|
68
|
+
export declare function formatPerTargetMessage(prefix: string, workspace: string, changes: UpgradeChange[]): string;
|
|
69
|
+
export declare function formatAllInOneMessage(prefix: string, changes: UpgradeChange[]): string;
|
|
70
|
+
//# 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;CAClB;AAOD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CAexF;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,aAAa,EAAE,GACvB,MAAM,CAWR;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CAiCtF"}
|
package/dist/cli/git.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrappers around `git` for the optional commit-per-success workflow.
|
|
3
|
+
*
|
|
4
|
+
* Design notes:
|
|
5
|
+
* - Every helper uses `reject: false` so we never throw on a non-zero git exit code; callers
|
|
6
|
+
* decide whether the failure should abort the whole run or just degrade to "skip git for
|
|
7
|
+
* this step". A failed commit (e.g. signing rejected by GPG, pre-commit hook refused) must
|
|
8
|
+
* never lose the actual upgrade work — the package.json mutation already happened.
|
|
9
|
+
* - We deliberately `git add` only the files we know we touched (the per-target package.json
|
|
10
|
+
* + the root lockfile), never `git add -A` / `git add .`. That avoids accidentally sweeping
|
|
11
|
+
* up unrelated WIP, generated files, or files modified by user prepare/postinstall hooks.
|
|
12
|
+
* - `relativizeForRepo` translates the absolute paths the engine works with into repo-root-
|
|
13
|
+
* relative paths so commit messages and `git add` arguments don't leak local filesystem
|
|
14
|
+
* prefixes.
|
|
15
|
+
*/
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import fs from 'fs-extra';
|
|
18
|
+
import { execa } from 'execa';
|
|
19
|
+
/** True when `cwd` is inside a git working tree. */
|
|
20
|
+
export async function isGitRepo(cwd) {
|
|
21
|
+
const r = await execa('git', ['rev-parse', '--is-inside-work-tree'], { cwd, reject: false });
|
|
22
|
+
return r.exitCode === 0 && r.stdout.trim() === 'true';
|
|
23
|
+
}
|
|
24
|
+
/** Absolute path to the repo's top-level directory (`git rev-parse --show-toplevel`). */
|
|
25
|
+
export async function getRepoRoot(cwd) {
|
|
26
|
+
const r = await execa('git', ['rev-parse', '--show-toplevel'], { cwd, reject: false });
|
|
27
|
+
return r.exitCode === 0 ? r.stdout.trim() : undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* List of repo-root-relative paths that have uncommitted changes (modified, added, deleted,
|
|
31
|
+
* untracked but not in `.gitignore`). Used by the pre-flight check to refuse running on a
|
|
32
|
+
* dirty tree unless the user passed `--git-allow-dirty`.
|
|
33
|
+
*/
|
|
34
|
+
export async function getUncommittedFiles(cwd) {
|
|
35
|
+
const r = await execa('git', ['status', '--porcelain'], { cwd, reject: false });
|
|
36
|
+
if (r.exitCode !== 0) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return r.stdout
|
|
40
|
+
.split('\n')
|
|
41
|
+
.map((l) => l.trim())
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.map((line) => {
|
|
44
|
+
// Porcelain format is `XY <path>` where XY is two status chars. Renames look like
|
|
45
|
+
// `R old -> new`; we want the new name.
|
|
46
|
+
const m = line.match(/^.{1,3}\s+(?:.+? -> )?(.+)$/);
|
|
47
|
+
return m ? m[1] : line;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export async function getCurrentBranch(cwd) {
|
|
51
|
+
const r = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, reject: false });
|
|
52
|
+
return r.exitCode === 0 ? r.stdout.trim() : undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create-and-checkout `branch`. If the branch already exists, just checkout (no -b). Returns
|
|
56
|
+
* the previous branch name for reporting, or `undefined` on first-commit-less repos.
|
|
57
|
+
*/
|
|
58
|
+
export async function checkoutBranch(cwd, branch) {
|
|
59
|
+
const previous = await getCurrentBranch(cwd);
|
|
60
|
+
// Try to create+switch first; if that fails because the branch exists, fall back to plain
|
|
61
|
+
// checkout. This avoids racing on `git rev-parse --verify` to detect existence.
|
|
62
|
+
const create = await execa('git', ['checkout', '-b', branch], { cwd, reject: false });
|
|
63
|
+
if (create.exitCode === 0) {
|
|
64
|
+
return previous;
|
|
65
|
+
}
|
|
66
|
+
const switchOnly = await execa('git', ['checkout', branch], { cwd, reject: false });
|
|
67
|
+
if (switchOnly.exitCode !== 0) {
|
|
68
|
+
throw new Error(`git checkout ${branch} failed: ${(create.stderr || switchOnly.stderr).trim()}`);
|
|
69
|
+
}
|
|
70
|
+
return previous;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Stage the given absolute file paths (any path that doesn't exist anymore is silently dropped
|
|
74
|
+
* — git would error on missing pathspec otherwise, and lockfiles can be absent on first install).
|
|
75
|
+
* Returns the repo-root-relative paths that were actually staged.
|
|
76
|
+
*/
|
|
77
|
+
export async function gitAdd(opts, files) {
|
|
78
|
+
const repoRoot = (await getRepoRoot(opts.cwd)) ?? opts.cwd;
|
|
79
|
+
// `git rev-parse --show-toplevel` returns the CANONICAL path (with macOS's `/private`
|
|
80
|
+
// prefix resolved); the absolute paths the engine gives us may still go through
|
|
81
|
+
// `/var/folders/...`. Realpath both sides so `path.relative` produces a path actually
|
|
82
|
+
// inside the repo — otherwise git refuses with "outside repository".
|
|
83
|
+
const repoRootReal = await fs.realpath(repoRoot).catch(() => repoRoot);
|
|
84
|
+
const relative = [];
|
|
85
|
+
for (const abs of files) {
|
|
86
|
+
if (!(await fs.pathExists(abs))) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const absReal = await fs.realpath(abs).catch(() => abs);
|
|
90
|
+
const rel = path.relative(repoRootReal, absReal);
|
|
91
|
+
relative.push(rel);
|
|
92
|
+
}
|
|
93
|
+
if (relative.length === 0) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
const r = await execa('git', ['add', '--', ...relative], {
|
|
97
|
+
cwd: repoRootReal,
|
|
98
|
+
reject: false,
|
|
99
|
+
});
|
|
100
|
+
if (r.exitCode !== 0) {
|
|
101
|
+
throw new Error(`git add failed: ${r.stderr.trim() || r.stdout.trim()}`);
|
|
102
|
+
}
|
|
103
|
+
return relative;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create a commit with `message`. Returns `{ ok: false, error }` when there's nothing to
|
|
107
|
+
* commit (clean index) OR when git itself refuses (pre-commit hook, signing, etc.) — callers
|
|
108
|
+
* use the same path either way: log + record + continue.
|
|
109
|
+
*/
|
|
110
|
+
export async function gitCommit(opts, message, files) {
|
|
111
|
+
const repoRoot = (await getRepoRoot(opts.cwd)) ?? opts.cwd;
|
|
112
|
+
// Short-circuit if the index has nothing for the commit (e.g. files were absent or the diff
|
|
113
|
+
// was empty). Querying `--cached` instead of the working tree means we only see what's
|
|
114
|
+
// actually staged for THIS commit.
|
|
115
|
+
const status = await execa('git', ['diff', '--cached', '--name-only'], {
|
|
116
|
+
cwd: repoRoot,
|
|
117
|
+
reject: false,
|
|
118
|
+
});
|
|
119
|
+
if (status.exitCode === 0 && status.stdout.trim() === '') {
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
message,
|
|
123
|
+
files,
|
|
124
|
+
error: 'nothing to commit (no staged changes)',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const args = ['commit', '-m', message];
|
|
128
|
+
if (opts.sign) {
|
|
129
|
+
args.push('--gpg-sign');
|
|
130
|
+
}
|
|
131
|
+
const env = { ...process.env };
|
|
132
|
+
if (opts.authorEmail) {
|
|
133
|
+
env.GIT_AUTHOR_EMAIL = opts.authorEmail;
|
|
134
|
+
env.GIT_COMMITTER_EMAIL = opts.authorEmail;
|
|
135
|
+
}
|
|
136
|
+
if (opts.authorName) {
|
|
137
|
+
env.GIT_AUTHOR_NAME = opts.authorName;
|
|
138
|
+
env.GIT_COMMITTER_NAME = opts.authorName;
|
|
139
|
+
}
|
|
140
|
+
const r = await execa('git', args, { cwd: repoRoot, env, reject: false });
|
|
141
|
+
if (r.exitCode !== 0) {
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
message,
|
|
145
|
+
files,
|
|
146
|
+
error: (r.stderr || r.stdout).trim(),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const sha = await execa('git', ['rev-parse', '--short', 'HEAD'], { cwd: repoRoot, reject: false });
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
sha: sha.exitCode === 0 ? sha.stdout.trim() : undefined,
|
|
153
|
+
message,
|
|
154
|
+
files,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Default lockfile basename for the detected package manager. We commit it alongside the
|
|
159
|
+
* mutated `package.json` so the commit is self-contained (anyone checking out that SHA can
|
|
160
|
+
* `<mgr> install` and get the exact tree we tested).
|
|
161
|
+
*/
|
|
162
|
+
export function lockfileBasenameFor(manager) {
|
|
163
|
+
switch (manager) {
|
|
164
|
+
case 'pnpm':
|
|
165
|
+
return 'pnpm-lock.yaml';
|
|
166
|
+
case 'yarn':
|
|
167
|
+
return 'yarn.lock';
|
|
168
|
+
case 'npm':
|
|
169
|
+
default:
|
|
170
|
+
return 'package-lock.json';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Strip the leading caret/tilde/etc. for a clean "from → to" message. */
|
|
174
|
+
function tidyVersion(v) {
|
|
175
|
+
return v.trim();
|
|
176
|
+
}
|
|
177
|
+
export function formatPerSuccessMessage(prefix, changes) {
|
|
178
|
+
if (changes.length === 1) {
|
|
179
|
+
const c = changes[0];
|
|
180
|
+
const ws = c.workspace && c.workspace !== 'root' ? ` (${c.workspace})` : '';
|
|
181
|
+
return `${prefix}bump ${c.name} from ${tidyVersion(c.from)} to ${tidyVersion(c.to)}${ws}`;
|
|
182
|
+
}
|
|
183
|
+
// Linked group → multi-line message with each member listed.
|
|
184
|
+
const head = changes[0];
|
|
185
|
+
const ws = head.workspace && head.workspace !== 'root' ? ` (${head.workspace})` : '';
|
|
186
|
+
const lines = [
|
|
187
|
+
`${prefix}bump ${changes.length} linked packages${ws}`,
|
|
188
|
+
'',
|
|
189
|
+
...changes.map((c) => `- ${c.name}: ${tidyVersion(c.from)} → ${tidyVersion(c.to)}`),
|
|
190
|
+
];
|
|
191
|
+
return lines.join('\n');
|
|
192
|
+
}
|
|
193
|
+
export function formatPerTargetMessage(prefix, workspace, changes) {
|
|
194
|
+
if (changes.length === 0) {
|
|
195
|
+
return `${prefix}no changes for ${workspace}`;
|
|
196
|
+
}
|
|
197
|
+
const wsLabel = workspace === 'root' ? '' : ` in ${workspace}`;
|
|
198
|
+
const lines = [
|
|
199
|
+
`${prefix}${changes.length} upgrade${changes.length === 1 ? '' : 's'}${wsLabel}`,
|
|
200
|
+
'',
|
|
201
|
+
...changes.map((c) => `- ${c.name}: ${tidyVersion(c.from)} → ${tidyVersion(c.to)}`),
|
|
202
|
+
];
|
|
203
|
+
return lines.join('\n');
|
|
204
|
+
}
|
|
205
|
+
export function formatAllInOneMessage(prefix, changes) {
|
|
206
|
+
if (changes.length === 0) {
|
|
207
|
+
return `${prefix}no upgrades`;
|
|
208
|
+
}
|
|
209
|
+
const targets = new Map();
|
|
210
|
+
for (const c of changes) {
|
|
211
|
+
const key = c.workspace ?? 'root';
|
|
212
|
+
const list = targets.get(key) ?? [];
|
|
213
|
+
list.push(c);
|
|
214
|
+
targets.set(key, list);
|
|
215
|
+
}
|
|
216
|
+
const targetCount = targets.size;
|
|
217
|
+
const head = targetCount === 1
|
|
218
|
+
? `${prefix}${changes.length} upgrade${changes.length === 1 ? '' : 's'}`
|
|
219
|
+
: `${prefix}${changes.length} upgrades across ${targetCount} targets`;
|
|
220
|
+
const lines = [head, ''];
|
|
221
|
+
for (const [ws, list] of targets) {
|
|
222
|
+
if (targetCount > 1) {
|
|
223
|
+
lines.push(`[${ws}]`);
|
|
224
|
+
}
|
|
225
|
+
for (const c of list) {
|
|
226
|
+
lines.push(`- ${c.name}: ${tidyVersion(c.from)} → ${tidyVersion(c.to)}`);
|
|
227
|
+
}
|
|
228
|
+
if (targetCount > 1) {
|
|
229
|
+
lines.push('');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Trim trailing blank line.
|
|
233
|
+
while (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
234
|
+
lines.pop();
|
|
235
|
+
}
|
|
236
|
+
return lines.join('\n');
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/cli/git.ts"],"names":[],"mappings":"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"}
|