@enke.dev/bumper 0.0.1 → 0.0.2
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 +48 -4
- package/dist/cli.mjs +35 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,9 +84,31 @@ bumper help # show help (also: bumper --help)
|
|
|
84
84
|
bumper detect # show context + applicable modules for the cwd
|
|
85
85
|
bumper detect /path --json # machine-readable detection
|
|
86
86
|
bumper update # run every applicable module, in order
|
|
87
|
-
bumper update
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
bumper update /path/to/repo # target another repo (defaults to cwd)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Flags
|
|
91
|
+
|
|
92
|
+
All flags apply to `bumper update` (except `--json`, on `detect`). Repeatable flags can be given
|
|
93
|
+
several times or comma-separated in one value.
|
|
94
|
+
|
|
95
|
+
| Flag | Repeatable | What it does |
|
|
96
|
+
| ------------------ | :--------: | -------------------------------------------------------------------------------------------- |
|
|
97
|
+
| `--dry-run` | no | Print every intended step, change nothing on disk. |
|
|
98
|
+
| `--only <id>` | yes | Run **only** the named module(s); everything else is skipped. |
|
|
99
|
+
| `--skip <id>` | yes | Run everything **except** the named module(s). |
|
|
100
|
+
| `--exclude <path>` | yes | Skip a repo-relative path this run only, without editing config (see [Excludes](#excludes)). |
|
|
101
|
+
| `--json` | no | `detect` only — emit machine-readable detection output. |
|
|
102
|
+
|
|
103
|
+
`--only` and `--skip` take module ids from the [Modules](#modules) table (`node`, `types-node`,
|
|
104
|
+
`bun`, `npm`, `pnpm`, `docker`, `github-actions`).
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
bumper update --dry-run # preview, no writes
|
|
108
|
+
bumper update --only node,pnpm # just the Node runtime + pnpm modules
|
|
109
|
+
bumper update --skip github-actions # everything but the actions pinner
|
|
110
|
+
bumper update --exclude examples # skip a path this run, without editing config
|
|
111
|
+
bumper update --exclude examples,fixtures # repeat the flag or comma-separate several
|
|
90
112
|
```
|
|
91
113
|
|
|
92
114
|
## Modules
|
|
@@ -121,7 +143,7 @@ Path-scoped overrides. Running in an unknown repo auto-detects everything and pe
|
|
|
121
143
|
"repos": {
|
|
122
144
|
"/absolute/path/to/repository": {
|
|
123
145
|
"mode": "auto", // auto = re-detect; manual = respect stored toggles
|
|
124
|
-
"exclude": ["packages/vendored-pkg"], // repo-relative
|
|
146
|
+
"exclude": ["packages/vendored-pkg"], // repo-relative paths skipped everywhere (see below)
|
|
125
147
|
"modules": { "docker": false }, // explicit per-module on/off overrides, keyed by module id
|
|
126
148
|
},
|
|
127
149
|
},
|
|
@@ -135,3 +157,25 @@ bumper config set /path/to/repo exclude packages/a,packages/b
|
|
|
135
157
|
bumper config set /path/to/repo modules.docker false
|
|
136
158
|
bumper config set /path/to/repo mode manual
|
|
137
159
|
```
|
|
160
|
+
|
|
161
|
+
### Excludes
|
|
162
|
+
|
|
163
|
+
`exclude` is a list of repo-relative paths (an exact dir/file or any descendant) that bumper
|
|
164
|
+
leaves alone. It applies **uniformly**:
|
|
165
|
+
|
|
166
|
+
- workspace members under an excluded path are dropped from every workspace operation, and
|
|
167
|
+
- every file-discovering module (e.g. `docker`, which globs `**/Dockerfile*`) skips matches under
|
|
168
|
+
an excluded path — so vendored packages, fixtures, or example projects used for testing are
|
|
169
|
+
never rewritten.
|
|
170
|
+
|
|
171
|
+
Persist it with `config set … exclude`, or pass `--exclude <path>` on a single `update` run to add
|
|
172
|
+
paths for that run only (repeatable, comma-separated, merged with the stored list, not saved). The
|
|
173
|
+
common case: a repo whose own `examples/` are self-applied test fixtures —
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
bumper config set /path/to/repo exclude examples # always skip
|
|
177
|
+
bumper update --exclude examples # skip just this run
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
> Modules that read a single fixed file at the repo root (`github-actions`, the package managers)
|
|
181
|
+
> are unaffected — `exclude` targets subpaths, and the root is never excluded.
|
package/dist/cli.mjs
CHANGED
|
@@ -1993,17 +1993,11 @@ var configCommand = {
|
|
|
1993
1993
|
};
|
|
1994
1994
|
|
|
1995
1995
|
// src/commands/detect/detect.command.ts
|
|
1996
|
-
import { resolve as
|
|
1997
|
-
|
|
1998
|
-
// src/context/context.ts
|
|
1999
|
-
import { relative as relative2 } from "node:path";
|
|
2000
|
-
|
|
2001
|
-
// src/context/detectors/package-manager.detector.ts
|
|
2002
|
-
import { join as join3 } from "node:path";
|
|
1996
|
+
import { resolve as resolve3 } from "node:path";
|
|
2003
1997
|
|
|
2004
1998
|
// src/utils/fs.utils.ts
|
|
2005
1999
|
import { access, glob, readFile as readFile2, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
2006
|
-
import { join as join2 } from "node:path";
|
|
2000
|
+
import { join as join2, relative as relativePath, resolve as resolve2, sep } from "node:path";
|
|
2007
2001
|
async function pathExists(path) {
|
|
2008
2002
|
try {
|
|
2009
2003
|
await access(path);
|
|
@@ -2022,6 +2016,14 @@ async function globFiles(cwd, pattern) {
|
|
|
2022
2016
|
const files = await Promise.all(absolute.map(async (path) => (await stat(path)).isFile() ? path : null));
|
|
2023
2017
|
return files.filter((path) => path !== null);
|
|
2024
2018
|
}
|
|
2019
|
+
function isExcluded(cwd, path, exclude) {
|
|
2020
|
+
const rel = relativePath(cwd, resolve2(cwd, path));
|
|
2021
|
+
return exclude.some((entry) => rel === entry || rel.startsWith(`${entry}/`));
|
|
2022
|
+
}
|
|
2023
|
+
async function collectFiles(cwd, pattern, exclude = []) {
|
|
2024
|
+
const matches = await globFiles(cwd, pattern);
|
|
2025
|
+
return matches.filter((match) => !match.split(sep).includes("node_modules") && !isExcluded(cwd, match, exclude));
|
|
2026
|
+
}
|
|
2025
2027
|
async function readPackageJson(dir) {
|
|
2026
2028
|
try {
|
|
2027
2029
|
return JSON.parse(await readFile2(join2(dir, "package.json"), "utf8"));
|
|
@@ -2043,6 +2045,7 @@ function allDependencies(pkg) {
|
|
|
2043
2045
|
}
|
|
2044
2046
|
|
|
2045
2047
|
// src/context/detectors/package-manager.detector.ts
|
|
2048
|
+
import { join as join3 } from "node:path";
|
|
2046
2049
|
async function detectPackageManager(cwd) {
|
|
2047
2050
|
const pkg = await readPackageJson(cwd);
|
|
2048
2051
|
const field = pkg?.packageManager ?? "";
|
|
@@ -2088,7 +2091,7 @@ async function exec(cmd, opts = {}) {
|
|
|
2088
2091
|
if (file === undefined) {
|
|
2089
2092
|
throw new Error("exec called with an empty command");
|
|
2090
2093
|
}
|
|
2091
|
-
return new Promise((
|
|
2094
|
+
return new Promise((resolve3) => {
|
|
2092
2095
|
const proc = spawn(file, args, {
|
|
2093
2096
|
...opts.cwd ? { cwd: opts.cwd } : {},
|
|
2094
2097
|
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
@@ -2098,8 +2101,8 @@ async function exec(cmd, opts = {}) {
|
|
|
2098
2101
|
let stderr = "";
|
|
2099
2102
|
proc.stdout.on("data", (chunk) => stdout += chunk);
|
|
2100
2103
|
proc.stderr.on("data", (chunk) => stderr += chunk);
|
|
2101
|
-
proc.on("error", (error) =>
|
|
2102
|
-
proc.on("close", (code) =>
|
|
2104
|
+
proc.on("error", (error) => resolve3({ exitCode: 1, stdout, stderr: String(error) }));
|
|
2105
|
+
proc.on("close", (code) => resolve3({ exitCode: code ?? 0, stdout, stderr }));
|
|
2103
2106
|
});
|
|
2104
2107
|
}
|
|
2105
2108
|
async function execOk(cmd, opts = {}) {
|
|
@@ -2194,12 +2197,10 @@ async function detectWorkspaces(cwd, pm) {
|
|
|
2194
2197
|
}
|
|
2195
2198
|
|
|
2196
2199
|
// src/context/context.ts
|
|
2197
|
-
function isExcluded(cwd, dir, exclude) {
|
|
2198
|
-
const rel = relative2(cwd, dir);
|
|
2199
|
-
return exclude.some((entry) => rel === entry || rel.startsWith(`${entry}/`));
|
|
2200
|
-
}
|
|
2201
2200
|
async function buildContext(cwd, options = {}) {
|
|
2202
|
-
const { config, created } = await resolveForPath(cwd);
|
|
2201
|
+
const { config: stored, created } = await resolveForPath(cwd);
|
|
2202
|
+
const exclude = [...new Set([...stored.exclude, ...options.exclude ?? []])];
|
|
2203
|
+
const config = { ...stored, exclude };
|
|
2203
2204
|
const [runtime, packageManager] = await Promise.all([
|
|
2204
2205
|
detectRuntime(cwd),
|
|
2205
2206
|
detectPackageManager(cwd)
|
|
@@ -2211,7 +2212,7 @@ async function buildContext(cwd, options = {}) {
|
|
|
2211
2212
|
runtime,
|
|
2212
2213
|
packageManager,
|
|
2213
2214
|
isMonorepo,
|
|
2214
|
-
workspaces: workspaces.filter((dir) => !isExcluded(cwd, dir,
|
|
2215
|
+
workspaces: workspaces.filter((dir) => !isExcluded(cwd, dir, exclude)),
|
|
2215
2216
|
versionManager,
|
|
2216
2217
|
config,
|
|
2217
2218
|
dryRun: options.dryRun ?? false
|
|
@@ -2249,7 +2250,7 @@ function planLine(text) {
|
|
|
2249
2250
|
|
|
2250
2251
|
// src/modules/features/docker/docker.feature.ts
|
|
2251
2252
|
import { readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
2252
|
-
import { relative as
|
|
2253
|
+
import { relative as relative2 } from "node:path";
|
|
2253
2254
|
|
|
2254
2255
|
// src/utils/npm-registry.utils.ts
|
|
2255
2256
|
async function curlJson(url, run2 = execOk) {
|
|
@@ -2306,9 +2307,8 @@ async function ensureNodeLts(ctx) {
|
|
|
2306
2307
|
|
|
2307
2308
|
// src/modules/features/docker/docker.feature.ts
|
|
2308
2309
|
var DOCKER_GLOB = "**/{Dockerfile*,docker-compose*.yaml,docker-compose*.yml,compose*.yaml,compose*.yml}";
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
return matches.filter((match) => !match.split(sep).includes("node_modules"));
|
|
2310
|
+
function findDockerFiles(ctx) {
|
|
2311
|
+
return collectFiles(ctx.cwd, DOCKER_GLOB, ctx.config.exclude);
|
|
2312
2312
|
}
|
|
2313
2313
|
var dockerFeature = {
|
|
2314
2314
|
kind: "feature" /* Feature */,
|
|
@@ -2319,13 +2319,13 @@ var dockerFeature = {
|
|
|
2319
2319
|
if (toggle !== undefined) {
|
|
2320
2320
|
return toggle;
|
|
2321
2321
|
}
|
|
2322
|
-
return (await findDockerFiles(ctx
|
|
2322
|
+
return (await findDockerFiles(ctx)).length > 0;
|
|
2323
2323
|
},
|
|
2324
2324
|
async update(ctx) {
|
|
2325
2325
|
const { version } = await ensureNodeLts(ctx);
|
|
2326
|
-
const files = await findDockerFiles(ctx
|
|
2326
|
+
const files = await findDockerFiles(ctx);
|
|
2327
2327
|
await Promise.all(files.map(async (file) => {
|
|
2328
|
-
const label =
|
|
2328
|
+
const label = relative2(ctx.cwd, file);
|
|
2329
2329
|
if (ctx.dryRun) {
|
|
2330
2330
|
planLine(`align node version → ${version} in ${label}`);
|
|
2331
2331
|
return;
|
|
@@ -2368,7 +2368,7 @@ var githubActionsFeature = {
|
|
|
2368
2368
|
};
|
|
2369
2369
|
|
|
2370
2370
|
// src/modules/features/types-node/types-node.feature.ts
|
|
2371
|
-
import { relative as
|
|
2371
|
+
import { relative as relative3 } from "node:path";
|
|
2372
2372
|
|
|
2373
2373
|
// src/utils/spec.utils.ts
|
|
2374
2374
|
var import_semver = __toESM(require_semver2(), 1);
|
|
@@ -2426,7 +2426,7 @@ var typesNodeFeature = {
|
|
|
2426
2426
|
const majorSpec = String(major);
|
|
2427
2427
|
const dirs = await dirsWithTypesNode(ctx);
|
|
2428
2428
|
await Promise.all(dirs.map(async (dir) => {
|
|
2429
|
-
const label =
|
|
2429
|
+
const label = relative3(ctx.cwd, dir) || ".";
|
|
2430
2430
|
if (ctx.dryRun) {
|
|
2431
2431
|
planLine(`pin ${TYPES_NODE_PACKAGE}@${majorSpec} in ${label}`);
|
|
2432
2432
|
return;
|
|
@@ -2745,7 +2745,7 @@ async function detectModules(ctx) {
|
|
|
2745
2745
|
|
|
2746
2746
|
// src/commands/detect/detect.command.ts
|
|
2747
2747
|
async function run2({ values, positionals }) {
|
|
2748
|
-
const cwd =
|
|
2748
|
+
const cwd = resolve3(positionals[0] ?? process.cwd());
|
|
2749
2749
|
const json = values.json ?? false;
|
|
2750
2750
|
const { ctx, configCreated } = await buildContext(cwd);
|
|
2751
2751
|
const modules = await detectModules(ctx);
|
|
@@ -2843,11 +2843,12 @@ function commandHelp(stream = process.stdout) {
|
|
|
2843
2843
|
}
|
|
2844
2844
|
|
|
2845
2845
|
// src/commands/update/update.command.ts
|
|
2846
|
-
import { resolve as
|
|
2846
|
+
import { resolve as resolve4 } from "node:path";
|
|
2847
2847
|
async function run4({ values, positionals }) {
|
|
2848
|
-
const cwd =
|
|
2848
|
+
const cwd = resolve4(positionals[0] ?? process.cwd());
|
|
2849
2849
|
const dryRun = values["dry-run"] ?? false;
|
|
2850
|
-
const
|
|
2850
|
+
const exclude = (values.exclude ?? []).flatMap((entry) => entry.split(",")).map((entry) => entry.trim()).filter(Boolean);
|
|
2851
|
+
const { ctx, configCreated } = await buildContext(cwd, { dryRun, exclude });
|
|
2851
2852
|
if (configCreated) {
|
|
2852
2853
|
process.stdout.write(`${DIM}Discovered new repo, wrote entry to ${configPath()}${RESET}
|
|
2853
2854
|
`);
|
|
@@ -2860,12 +2861,13 @@ var updateCommand = {
|
|
|
2860
2861
|
name: "update",
|
|
2861
2862
|
run: run4,
|
|
2862
2863
|
help: () => ({
|
|
2863
|
-
usage: ["bumper update [path] [--dry-run] [--only id]... [--skip id]..."],
|
|
2864
|
+
usage: ["bumper update [path] [--dry-run] [--only id]... [--skip id]... [--exclude path]..."],
|
|
2864
2865
|
summary: "Run every applicable module in order",
|
|
2865
2866
|
options: [
|
|
2866
2867
|
"--dry-run Print intended steps without changing anything",
|
|
2867
2868
|
"--only id Module id to run exclusively (repeat for several)",
|
|
2868
|
-
"--skip id Module id to skip (repeat for several)"
|
|
2869
|
+
"--skip id Module id to skip (repeat for several)",
|
|
2870
|
+
"--exclude path Repo-relative path skipped this run, not persisted (repeat or comma-separate)"
|
|
2869
2871
|
]
|
|
2870
2872
|
})
|
|
2871
2873
|
};
|
|
@@ -2882,6 +2884,7 @@ var cliOptions = {
|
|
|
2882
2884
|
"dry-run": { type: "boolean" },
|
|
2883
2885
|
only: { type: "string", multiple: true },
|
|
2884
2886
|
skip: { type: "string", multiple: true },
|
|
2887
|
+
exclude: { type: "string", multiple: true },
|
|
2885
2888
|
help: { type: "boolean", short: "h" }
|
|
2886
2889
|
};
|
|
2887
2890
|
|