@varlock/bumpy 1.13.0 → 1.13.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 +20 -139
- package/dist/{add-DQA1TVHA.mjs → add-Dt1hddMt.mjs} +121 -25
- package/dist/{check-CsF0zh8r.mjs → check-Dvi0DIqC.mjs} +1 -1
- package/dist/{ci-CIamssoq.mjs → ci-B7gF6CFP.mjs} +5 -5
- package/dist/cli.mjs +10 -10
- package/dist/{generate-CvCvUaRV.mjs → generate-DohUlhu3.mjs} +1 -1
- package/dist/{git-DJJ64SW9.mjs → git-BWPimLgc.mjs} +14 -4
- package/dist/index.mjs +1 -1
- package/dist/{publish-h6rM58Cq.mjs → publish-VYBhDYFM.mjs} +62 -8
- package/dist/{publish-pipeline-DSj14dW6.mjs → publish-pipeline-BPedWvKS.mjs} +17 -2
- package/dist/{status-BbsDr6t7.mjs → status-DxzKPM8d.mjs} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,22 +15,21 @@
|
|
|
15
15
|
|
|
16
16
|
# @varlock/bumpy 🐸
|
|
17
17
|
|
|
18
|
-
A modern package versioning, release, and changelog generation tool. Built for monorepos, but works great in
|
|
18
|
+
A modern package versioning, release, and changelog generation tool. Built for monorepos, but works great in simple projects too.
|
|
19
19
|
|
|
20
20
|
## How It Works
|
|
21
21
|
|
|
22
|
-
Bumpy uses **bump files** (you may know them as "changesets" if coming from [that tool 🦋](https://github.com/changesets/changesets)) - small markdown files that declare an
|
|
22
|
+
Bumpy uses **bump files** (you may know them as "changesets" if coming from [that tool 🦋](https://github.com/changesets/changesets)) - small markdown files that declare an _intent to release packages_ with a bump level (patch/minor/major), and a description that ends up in changelogs. Developers create these files as part of their PRs, and these files are then used to consolidate changes, generate changelogs, and trigger publishing.
|
|
23
23
|
|
|
24
24
|
- Devs/agents create bump files as part of their PRs (using `bumpy add` or manually)
|
|
25
25
|
- A git hook (pre-commit or pre-push) can enforce bump files exist for changed packages
|
|
26
26
|
- In CI, a workflow checks PRs for bump files, leaves a comment on the PR detailing changed packages
|
|
27
27
|
- As PRs merge to the base branch, a "release PR" is kept up to date
|
|
28
|
-
- Shows what packages will be released and their changelogs
|
|
29
|
-
- Including packages bumped automatically due to dependency relationships
|
|
28
|
+
- Shows what packages will be released and their changelogs (incl. those bumped via dep relationships)
|
|
30
29
|
- When release PR is merged, publishing is triggered
|
|
31
|
-
- Pending bump files are deleted and packages are published with updated versions and changelogs
|
|
30
|
+
- Pending bump files are deleted and packages are published with updated versions and changelogs, github tags+releases created
|
|
32
31
|
|
|
33
|
-
All of this is automated via two simple GitHub Actions workflows (see [
|
|
32
|
+
All of this is automated via two simple GitHub Actions workflows (see [actions guide](https://github.com/dmno-dev/bumpy/blob/main/docs/github-actions.md)). Or can be triggered locally.
|
|
34
33
|
|
|
35
34
|
### Example bump file
|
|
36
35
|
|
|
@@ -42,21 +41,23 @@ All of this is automated via two simple GitHub Actions workflows (see [CI setup]
|
|
|
42
41
|
'@myorg/utils': patch
|
|
43
42
|
---
|
|
44
43
|
|
|
45
|
-
Added user
|
|
44
|
+
Added user lang prefs to core config.
|
|
46
45
|
Fixed locale fallback logic in utils.
|
|
47
46
|
```
|
|
48
47
|
|
|
49
48
|
## Features
|
|
50
49
|
|
|
51
|
-
- **All package managers** - npm, pnpm, yarn, and bun workspaces
|
|
50
|
+
- **All package managers** - npm, pnpm, yarn, and bun workspaces. With full `workspace:` and `catalog:` support
|
|
52
51
|
- **Smart dependency propagation** - configurable rules for how version bumps cascade through your dependency graph (see [version propagation docs](https://github.com/dmno-dev/bumpy/blob/main/docs/version-propagation.md))
|
|
53
|
-
- **
|
|
52
|
+
- **OIDC + staged publishing** - supports OIDC, provenance, [npm staged publishing](https://docs.npmjs.com/staged-publishing)
|
|
53
|
+
- **Custom release targets** - Per-package custom publish commands let you target anything - VSCode extensions, Docker images, JSR, private registries, etc.
|
|
54
54
|
- **Flexible package management** - include/exclude any package individually via per-package config, glob patterns, or `privatePackages` setting
|
|
55
55
|
- **Non-interactive CLI** - `bumpy add` works fully non-interactively for CI/CD and AI-assisted development
|
|
56
56
|
- **Aggregated GitHub releases** - optionally create a single consolidated release instead of one per package
|
|
57
57
|
- **Auto-generate from commits** - `bumpy generate` creates bump files from branch commits - works with any commit style, with enhanced detection for conventional commits
|
|
58
58
|
- **Pluggable changelog formatters** - built-in `"default"` and `"github"` formatters, or write your own
|
|
59
59
|
- **Zero runtime dependencies** - dependencies are minimal and bundled at release time
|
|
60
|
+
- **No additional action/app needed** - no external github action or app to audit and trust
|
|
60
61
|
|
|
61
62
|
## Getting Started
|
|
62
63
|
|
|
@@ -67,6 +68,9 @@ bun add -d @varlock/bumpy # or npm/pnpm/yarn
|
|
|
67
68
|
# Initialize (creates .bumpy/ directory and config, migrates from changesets if applicable)
|
|
68
69
|
bunx bumpy init
|
|
69
70
|
|
|
71
|
+
# Interactive guidance setting up CI
|
|
72
|
+
bunx bumpy ci setup
|
|
73
|
+
|
|
70
74
|
# Create a bump file
|
|
71
75
|
bunx bumpy add
|
|
72
76
|
|
|
@@ -78,138 +82,15 @@ Then set up CI to automate versioning and publishing (see below).
|
|
|
78
82
|
|
|
79
83
|
## CI / GitHub Actions
|
|
80
84
|
|
|
81
|
-
No GitHub App to install, no separate action to rely on
|
|
82
|
-
|
|
83
|
-
- **`bumpy ci check`** - runs on every PR. Computes the release plan from pending bump files and posts/updates a comment on the PR showing what versions would be released. Warns if any changed packages are missing bump files.
|
|
84
|
-
- **`bumpy ci release`** - runs on push to main. If pending bump files exist, it opens (or updates) a "Version Packages" PR that applies all version bumps and changelog updates. If the current push _is_ the Version Packages PR being merged, it publishes the new versions, creates git tags, and creates GitHub releases.
|
|
85
|
-
|
|
86
|
-
_examples use bun, but works with Node.js_
|
|
87
|
-
|
|
88
|
-
### PR check workflow
|
|
89
|
-
|
|
90
|
-
```yaml
|
|
91
|
-
# .github/workflows/bumpy-check.yaml
|
|
92
|
-
#
|
|
93
|
-
# ⚠️ Uses `pull_request_target` so fork PR comments work — runs with write
|
|
94
|
-
# perms and secrets, so it MUST NOT execute PR code (no `bun install`, no
|
|
95
|
-
# PR-defined scripts). Bumpy only reads files; its version is resolved from
|
|
96
|
-
# the base branch's package.json. See docs/github-actions.md for details.
|
|
97
|
-
name: Bumpy Check
|
|
98
|
-
on: pull_request_target
|
|
99
|
-
|
|
100
|
-
permissions:
|
|
101
|
-
pull-requests: write
|
|
102
|
-
contents: read
|
|
103
|
-
|
|
104
|
-
jobs:
|
|
105
|
-
check:
|
|
106
|
-
runs-on: ubuntu-latest
|
|
107
|
-
steps:
|
|
108
|
-
- uses: actions/checkout@v6
|
|
109
|
-
with:
|
|
110
|
-
ref: ${{ github.event.pull_request.head.sha }}
|
|
111
|
-
- uses: oven-sh/setup-bun@v2
|
|
112
|
-
|
|
113
|
-
# Resolve bumpy's version from the base branch (trusted) — not the PR's
|
|
114
|
-
# package.json (which a fork PR could swap to a malicious version).
|
|
115
|
-
# Change "main" to your base branch if different.
|
|
116
|
-
- name: Resolve bumpy version from base
|
|
117
|
-
run: |
|
|
118
|
-
git fetch origin main --depth=1
|
|
119
|
-
VERSION=$(git show "origin/main:package.json" \
|
|
120
|
-
| jq -r '.devDependencies["@varlock/bumpy"] // .dependencies["@varlock/bumpy"]' \
|
|
121
|
-
| sed 's/[\^~]//')
|
|
122
|
-
echo "BUMPY_VERSION=$VERSION" >> "$GITHUB_ENV"
|
|
123
|
-
|
|
124
|
-
- run: bunx "@varlock/bumpy@$BUMPY_VERSION" ci check
|
|
125
|
-
env:
|
|
126
|
-
GH_TOKEN: ${{ github.token }}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Release workflow
|
|
130
|
-
|
|
131
|
-
```yaml
|
|
132
|
-
# .github/workflows/bumpy-release.yml - trusted publishing (OIDC, no secret needed)
|
|
133
|
-
name: Bumpy Release
|
|
134
|
-
on:
|
|
135
|
-
push:
|
|
136
|
-
branches: [main]
|
|
137
|
-
|
|
138
|
-
jobs:
|
|
139
|
-
release:
|
|
140
|
-
runs-on: ubuntu-latest
|
|
141
|
-
permissions:
|
|
142
|
-
contents: write
|
|
143
|
-
pull-requests: write
|
|
144
|
-
id-token: write # required for npm trusted publishing (OIDC) and provenance
|
|
145
|
-
steps:
|
|
146
|
-
- uses: actions/checkout@v6
|
|
147
|
-
with:
|
|
148
|
-
fetch-depth: 0
|
|
149
|
-
- uses: oven-sh/setup-bun@v2
|
|
150
|
-
- uses: actions/setup-node@v6
|
|
151
|
-
with:
|
|
152
|
-
node-version: latest
|
|
153
|
-
- run: npm install -g npm@latest # ensure npm >= 11.15.0 for OIDC/staged publishing
|
|
154
|
-
- run: bun install
|
|
155
|
-
- run: bunx @varlock/bumpy ci release
|
|
156
|
-
env:
|
|
157
|
-
GH_TOKEN: ${{ github.token }}
|
|
158
|
-
BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} # PAT so that version PR triggers CI
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
> **Trusted publishing setup:** Configure each package on [npmjs.com](https://docs.npmjs.com/trusted-publishers/) → Package Settings → Trusted Publishers → GitHub Actions. Specify your org/user, repo, and the workflow filename (`bumpy-release.yml`). No `NPM_TOKEN` secret needed. Enable `provenance` and `npmStaged` in your [publish config](https://github.com/dmno-dev/bumpy/blob/main/docs/configuration.md#staged-publishing) for maximum security.
|
|
162
|
-
|
|
163
|
-
<details>
|
|
164
|
-
<summary>Alternative: token-based auth (NPM_TOKEN secret)</summary>
|
|
165
|
-
|
|
166
|
-
```yaml
|
|
167
|
-
# .github/workflows/bumpy-release.yml - token-based auth
|
|
168
|
-
name: Bumpy Release
|
|
169
|
-
on:
|
|
170
|
-
push:
|
|
171
|
-
branches: [main]
|
|
172
|
-
|
|
173
|
-
jobs:
|
|
174
|
-
release:
|
|
175
|
-
runs-on: ubuntu-latest
|
|
176
|
-
permissions:
|
|
177
|
-
contents: write
|
|
178
|
-
pull-requests: write
|
|
179
|
-
steps:
|
|
180
|
-
- uses: actions/checkout@v6
|
|
181
|
-
with:
|
|
182
|
-
fetch-depth: 0
|
|
183
|
-
- uses: oven-sh/setup-bun@v2
|
|
184
|
-
- run: bun install
|
|
185
|
-
- run: bunx @varlock/bumpy ci release
|
|
186
|
-
env:
|
|
187
|
-
GH_TOKEN: ${{ github.token }}
|
|
188
|
-
BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }}
|
|
189
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
</details>
|
|
193
|
-
|
|
194
|
-
### Token setup
|
|
195
|
-
|
|
196
|
-
The default `github.token` works for basic functionality, but GitHub's anti-recursion guard means PRs created by the default token won't trigger other workflows - so your regular CI (tests, linting, etc.) won't run automatically on the Version Packages PR. To fix this, provide a `BUMPY_GH_TOKEN` secret using either a **fine-grained PAT** or a **GitHub App token**. See the [full token setup guide](https://github.com/dmno-dev/bumpy/blob/main/docs/github-actions.md#token-setup) for details.
|
|
85
|
+
No GitHub App to install, no separate action to rely on — just call `bumpy ci` directly in your workflows. Three commands across two workflows handle the entire release lifecycle:
|
|
197
86
|
|
|
198
|
-
|
|
87
|
+
- **`bumpy ci check`** — on every PR, posts/updates a comment showing the release plan and warns if changed packages are missing bump files.
|
|
88
|
+
- **`bumpy ci plan`** — on push to main, detects what should happen next (`version-pr`, `publish`, or nothing) without needing write permissions or publish credentials. Used to gate downstream jobs in split-job workflows.
|
|
89
|
+
- **`bumpy ci release`** — opens/updates the "Version Packages" PR, or publishes new versions and creates git tags + GitHub releases when that PR is merged.
|
|
199
90
|
|
|
200
|
-
|
|
201
|
-
- **Repository access:** your repo only
|
|
202
|
-
- **Permissions:** Contents (read & write), Pull requests (read & write)
|
|
203
|
-
2. Add it as a repository secret named `BUMPY_GH_TOKEN`
|
|
204
|
-
3. Add it to your release workflow:
|
|
205
|
-
```yaml
|
|
206
|
-
- run: bunx @varlock/bumpy ci release
|
|
207
|
-
env:
|
|
208
|
-
GH_TOKEN: ${{ github.token }}
|
|
209
|
-
BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }}
|
|
210
|
-
```
|
|
91
|
+
Run `bumpy ci setup` for interactive guidance, and see the [GitHub Actions setup guide](https://github.com/dmno-dev/bumpy/blob/main/docs/github-actions.md) for ready-to-copy workflows, token setup, and trusted publishing.
|
|
211
92
|
|
|
212
|
-
|
|
93
|
+
## Local versioning and publishing
|
|
213
94
|
|
|
214
95
|
If you prefer to version and publish locally instead of via CI:
|
|
215
96
|
|
|
@@ -270,7 +151,7 @@ bunx bumpy --help # invoke built cli
|
|
|
270
151
|
- Better support for versioning non-JS packages and usage without package.json files
|
|
271
152
|
- Plugin system for different publish targets, and support multiple targets per package
|
|
272
153
|
- Tracking workspace-level / non-publishable changes
|
|
273
|
-
- More frogs
|
|
154
|
+
- More frogs 🐸🐸🐸
|
|
274
155
|
|
|
275
156
|
---
|
|
276
157
|
|
|
@@ -2,10 +2,10 @@ import { n as log, r as require_picocolors, s as __toESM } from "./logger-BgksGF
|
|
|
2
2
|
import { n as exists, t as ensureDir } from "./fs-CBXKZhoU.mjs";
|
|
3
3
|
import { a as loadConfig, r as getBumpyDir } from "./config-D_4GYDJi.mjs";
|
|
4
4
|
import { a as writeBumpFile, r as readBumpFiles, s as discoverWorkspace, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { a as getChangedFiles } from "./git-BWPimLgc.mjs";
|
|
6
6
|
import { l as pt, o as gt, r as Ot, s as mt, t as unwrap, u as wt } from "./clack-W95rXis0.mjs";
|
|
7
7
|
import { n as slugify, t as randomName } from "./names-COooXAFg.mjs";
|
|
8
|
-
import { n as findChangedPackages } from "./check-
|
|
8
|
+
import { n as findChangedPackages } from "./check-Dvi0DIqC.mjs";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
import * as readline from "node:readline";
|
|
11
11
|
//#region src/prompts/bump-select.ts
|
|
@@ -24,6 +24,10 @@ const LEVELS = [
|
|
|
24
24
|
* - Changed packages default to "patch", unchanged to "none"
|
|
25
25
|
* - Enter to confirm
|
|
26
26
|
* - Ctrl+C / Escape to cancel
|
|
27
|
+
*
|
|
28
|
+
* Renders a viewport that fits within the terminal so the list scrolls instead of
|
|
29
|
+
* overflowing — otherwise large package counts cause the redraw cursor-up to lose
|
|
30
|
+
* its anchor once content scrolls off-screen.
|
|
27
31
|
*/
|
|
28
32
|
async function bumpSelectPrompt(items) {
|
|
29
33
|
const changedEntries = items.map((item, idx) => ({
|
|
@@ -35,7 +39,44 @@ async function bumpSelectPrompt(items) {
|
|
|
35
39
|
idx
|
|
36
40
|
})).filter(({ item }) => !item.changed);
|
|
37
41
|
const displayOrder = [...changedEntries, ...unchangedEntries];
|
|
42
|
+
const rows = [];
|
|
43
|
+
const itemRowIndex = [];
|
|
44
|
+
{
|
|
45
|
+
let displayIdx = 0;
|
|
46
|
+
if (changedEntries.length > 0) {
|
|
47
|
+
rows.push({
|
|
48
|
+
kind: "header",
|
|
49
|
+
text: "Changed"
|
|
50
|
+
});
|
|
51
|
+
for (const { idx } of changedEntries) {
|
|
52
|
+
itemRowIndex.push(rows.length);
|
|
53
|
+
rows.push({
|
|
54
|
+
kind: "item",
|
|
55
|
+
itemIdx: idx,
|
|
56
|
+
displayIdx
|
|
57
|
+
});
|
|
58
|
+
displayIdx++;
|
|
59
|
+
}
|
|
60
|
+
if (unchangedEntries.length > 0) rows.push({ kind: "separator" });
|
|
61
|
+
}
|
|
62
|
+
if (unchangedEntries.length > 0) {
|
|
63
|
+
rows.push({
|
|
64
|
+
kind: "header",
|
|
65
|
+
text: "Unchanged"
|
|
66
|
+
});
|
|
67
|
+
for (const { idx } of unchangedEntries) {
|
|
68
|
+
itemRowIndex.push(rows.length);
|
|
69
|
+
rows.push({
|
|
70
|
+
kind: "item",
|
|
71
|
+
itemIdx: idx,
|
|
72
|
+
displayIdx
|
|
73
|
+
});
|
|
74
|
+
displayIdx++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
38
78
|
let cursor = 0;
|
|
79
|
+
let scroll = 0;
|
|
39
80
|
const levels = items.map((item) => item.initialLevel !== void 0 ? item.initialLevel : item.changed ? "patch" : "skip");
|
|
40
81
|
return new Promise((resolve) => {
|
|
41
82
|
const { stdin, stdout } = process;
|
|
@@ -57,38 +98,77 @@ async function bumpSelectPrompt(items) {
|
|
|
57
98
|
if (selected.length === 0) lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("(none selected)")}`);
|
|
58
99
|
else for (const { item, idx } of selected) lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.cyan(item.name)} ${import_picocolors.default.dim("→")} ${import_picocolors.default.bold(levels[idx])}`);
|
|
59
100
|
lines.push(import_picocolors.default.dim("│"));
|
|
101
|
+
const output = lines.join("\n") + "\n";
|
|
102
|
+
stdout.write(output);
|
|
103
|
+
renderedLines = lines.length;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const headerChrome = [
|
|
107
|
+
`${import_picocolors.default.cyan("◆")} Select bump levels`,
|
|
108
|
+
`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("↑/↓ navigate · ←/→ change level · enter to confirm")}`,
|
|
109
|
+
`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("0 skip current · x skip all · r reset all to defaults")}`,
|
|
110
|
+
import_picocolors.default.dim("│")
|
|
111
|
+
];
|
|
112
|
+
const selectedCount = levels.filter((l) => l !== "skip").length;
|
|
113
|
+
const footerChrome = [
|
|
114
|
+
import_picocolors.default.dim("│"),
|
|
115
|
+
`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`${selectedCount} package${selectedCount !== 1 ? "s" : ""} selected`)}`,
|
|
116
|
+
import_picocolors.default.dim("└")
|
|
117
|
+
];
|
|
118
|
+
const termRows = stdout.rows || 24;
|
|
119
|
+
const chromeLines = headerChrome.length + footerChrome.length;
|
|
120
|
+
const MIN_BODY = 3;
|
|
121
|
+
const availableBody = Math.max(MIN_BODY, termRows - chromeLines - 1);
|
|
122
|
+
let visibleRows;
|
|
123
|
+
let topIndicator = null;
|
|
124
|
+
let bottomIndicator = null;
|
|
125
|
+
let stickyHeader = null;
|
|
126
|
+
if (rows.length <= availableBody) {
|
|
127
|
+
visibleRows = rows;
|
|
128
|
+
scroll = 0;
|
|
60
129
|
} else {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
130
|
+
let windowSize = Math.max(MIN_BODY, availableBody - 2);
|
|
131
|
+
const focusedRowIdx = itemRowIndex[cursor];
|
|
132
|
+
const adjustScroll = () => {
|
|
133
|
+
if (focusedRowIdx < scroll) scroll = focusedRowIdx;
|
|
134
|
+
else if (focusedRowIdx >= scroll + windowSize) scroll = focusedRowIdx - windowSize + 1;
|
|
135
|
+
scroll = Math.max(0, Math.min(scroll, rows.length - windowSize));
|
|
136
|
+
};
|
|
137
|
+
adjustScroll();
|
|
138
|
+
const section = getCurrentSection(cursor, changedEntries.length, unchangedEntries.length);
|
|
139
|
+
if (section !== null && section.headerRowIdx < scroll) {
|
|
140
|
+
windowSize = Math.max(MIN_BODY, windowSize - 1);
|
|
141
|
+
adjustScroll();
|
|
142
|
+
stickyHeader = `${import_picocolors.default.dim("│")} ${import_picocolors.default.underline(section.name)}`;
|
|
73
143
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
144
|
+
visibleRows = rows.slice(scroll, scroll + windowSize);
|
|
145
|
+
const above = scroll;
|
|
146
|
+
const below = rows.length - (scroll + windowSize);
|
|
147
|
+
if (above > 0) topIndicator = `${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`▲ ${above} more`)}`;
|
|
148
|
+
if (below > 0) bottomIndicator = `${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`▼ ${below} more`)}`;
|
|
149
|
+
}
|
|
150
|
+
lines.push(...headerChrome);
|
|
151
|
+
if (topIndicator !== null) lines.push(topIndicator);
|
|
152
|
+
if (stickyHeader !== null) lines.push(stickyHeader);
|
|
153
|
+
for (const row of visibleRows) if (row.kind === "separator") lines.push(import_picocolors.default.dim("│"));
|
|
154
|
+
else if (row.kind === "header") lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.underline(row.text)}`);
|
|
155
|
+
else {
|
|
156
|
+
const item = items[row.itemIdx];
|
|
157
|
+
const isFocused = row.displayIdx === cursor;
|
|
158
|
+
lines.push(formatRow(item, levels[row.itemIdx], isFocused));
|
|
85
159
|
}
|
|
160
|
+
if (bottomIndicator !== null) lines.push(bottomIndicator);
|
|
161
|
+
lines.push(...footerChrome);
|
|
86
162
|
const output = lines.join("\n") + "\n";
|
|
87
163
|
stdout.write(output);
|
|
88
164
|
renderedLines = lines.length;
|
|
89
165
|
}
|
|
166
|
+
function onResize() {
|
|
167
|
+
render();
|
|
168
|
+
}
|
|
90
169
|
function cleanup() {
|
|
91
170
|
stdin.removeListener("keypress", onKeypress);
|
|
171
|
+
stdout.removeListener("resize", onResize);
|
|
92
172
|
rl.close();
|
|
93
173
|
stdout.write("\x1B[?25h");
|
|
94
174
|
if (stdin.isTTY) stdin.setRawMode(false);
|
|
@@ -141,8 +221,24 @@ async function bumpSelectPrompt(items) {
|
|
|
141
221
|
render();
|
|
142
222
|
}
|
|
143
223
|
stdin.on("keypress", onKeypress);
|
|
224
|
+
stdout.on("resize", onResize);
|
|
144
225
|
});
|
|
145
226
|
}
|
|
227
|
+
/** Returns the section the focused item is in, plus the row index of its header. */
|
|
228
|
+
function getCurrentSection(cursor, changedCount, unchangedCount) {
|
|
229
|
+
if (cursor < changedCount) {
|
|
230
|
+
if (changedCount === 0) return null;
|
|
231
|
+
return {
|
|
232
|
+
headerRowIdx: 0,
|
|
233
|
+
name: "Changed"
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if (unchangedCount === 0) return null;
|
|
237
|
+
return {
|
|
238
|
+
headerRowIdx: changedCount > 0 ? changedCount + 2 : 0,
|
|
239
|
+
name: "Unchanged"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
146
242
|
function formatRow(item, level, focused) {
|
|
147
243
|
return `${import_picocolors.default.dim("│")} ${focused ? import_picocolors.default.cyan("›") : " "} ${focused ? import_picocolors.default.cyan(item.name) : item.name} ${import_picocolors.default.dim(`(${item.version})`)} ${formatLevel(level, focused)}`;
|
|
148
244
|
}
|
|
@@ -4,7 +4,7 @@ import { a as DEP_TYPES } from "./types-Bkh-igOJ.mjs";
|
|
|
4
4
|
import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir } from "./config-D_4GYDJi.mjs";
|
|
5
5
|
import { a as isCatalogRefAffected, i as diffCatalogMaps, n as detectPackageManager, o as parseCatalogs, t as CATALOG_FILES } from "./package-manager-Db_vTztt.mjs";
|
|
6
6
|
import { r as readBumpFiles, s as discoverWorkspace, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { a as getChangedFiles, r as getBaseCompareRef, s as getFileStatuses, u as readFileAtRef } from "./git-BWPimLgc.mjs";
|
|
8
8
|
import { relative, resolve } from "node:path";
|
|
9
9
|
//#region ../../node_modules/.bun/picomatch@4.0.4/node_modules/picomatch/lib/constants.js
|
|
10
10
|
var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -4,9 +4,9 @@ import { n as detectPackageManager } from "./package-manager-Db_vTztt.mjs";
|
|
|
4
4
|
import { i as recoverDeletedBumpFiles, r as readBumpFiles, s as discoverWorkspace, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
5
5
|
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
6
6
|
import { n as runArgs, r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { a as getChangedFiles, f as withGitToken } from "./git-BWPimLgc.mjs";
|
|
8
8
|
import { t as randomName } from "./names-COooXAFg.mjs";
|
|
9
|
-
import { n as findChangedPackages } from "./check-
|
|
9
|
+
import { n as findChangedPackages } from "./check-Dvi0DIqC.mjs";
|
|
10
10
|
import { t as resolveCommitMessage } from "./commit-message-CSWVKPJ-.mjs";
|
|
11
11
|
import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
@@ -157,7 +157,7 @@ async function ciPlanCommand(rootDir) {
|
|
|
157
157
|
packageNames: plan.releases.map((r) => r.name)
|
|
158
158
|
};
|
|
159
159
|
else {
|
|
160
|
-
const { findUnpublishedPackages } = await import("./publish-
|
|
160
|
+
const { findUnpublishedPackages } = await import("./publish-VYBhDYFM.mjs");
|
|
161
161
|
const unpublished = await findUnpublishedPackages(packages, config);
|
|
162
162
|
if (unpublished.length > 0) output = {
|
|
163
163
|
mode: "publish",
|
|
@@ -234,7 +234,7 @@ async function ciReleaseCommand(rootDir, opts) {
|
|
|
234
234
|
const msg = bumpFiles.length === 0 ? "No pending bump files — checking for unpublished packages..." : "Bump files found but no packages would be released — checking for unpublished packages...";
|
|
235
235
|
log.info(msg);
|
|
236
236
|
const recoveredBumpFiles = recoverDeletedBumpFiles(rootDir);
|
|
237
|
-
const { publishCommand } = await import("./publish-
|
|
237
|
+
const { publishCommand } = await import("./publish-VYBhDYFM.mjs");
|
|
238
238
|
await publishCommand(rootDir, {
|
|
239
239
|
tag: opts.tag,
|
|
240
240
|
recoveredBumpFiles
|
|
@@ -287,7 +287,7 @@ async function autoPublish(rootDir, config, plan, tag) {
|
|
|
287
287
|
], { cwd: rootDir });
|
|
288
288
|
}
|
|
289
289
|
log.step("Running bumpy publish...");
|
|
290
|
-
const { publishCommand } = await import("./publish-
|
|
290
|
+
const { publishCommand } = await import("./publish-VYBhDYFM.mjs");
|
|
291
291
|
await publishCommand(rootDir, { tag });
|
|
292
292
|
}
|
|
293
293
|
/**
|
package/dist/cli.mjs
CHANGED
|
@@ -31,7 +31,7 @@ async function main() {
|
|
|
31
31
|
}
|
|
32
32
|
case "add": {
|
|
33
33
|
const rootDir = await findRoot();
|
|
34
|
-
const { addCommand } = await import("./add-
|
|
34
|
+
const { addCommand } = await import("./add-Dt1hddMt.mjs");
|
|
35
35
|
await addCommand(rootDir, {
|
|
36
36
|
packages: flags.packages,
|
|
37
37
|
message: flags.message,
|
|
@@ -43,7 +43,7 @@ async function main() {
|
|
|
43
43
|
}
|
|
44
44
|
case "status": {
|
|
45
45
|
const rootDir = await findRoot();
|
|
46
|
-
const { statusCommand } = await import("./status-
|
|
46
|
+
const { statusCommand } = await import("./status-DxzKPM8d.mjs");
|
|
47
47
|
await statusCommand(rootDir, {
|
|
48
48
|
json: flags.json === true,
|
|
49
49
|
packagesOnly: flags.packages === true,
|
|
@@ -61,7 +61,7 @@ async function main() {
|
|
|
61
61
|
}
|
|
62
62
|
case "generate": {
|
|
63
63
|
const rootDir = await findRoot();
|
|
64
|
-
const { generateCommand } = await import("./generate-
|
|
64
|
+
const { generateCommand } = await import("./generate-DohUlhu3.mjs");
|
|
65
65
|
await generateCommand(rootDir, {
|
|
66
66
|
from: flags.from,
|
|
67
67
|
dryRun: flags["dry-run"] === true,
|
|
@@ -71,7 +71,7 @@ async function main() {
|
|
|
71
71
|
}
|
|
72
72
|
case "check": {
|
|
73
73
|
const rootDir = await findRoot();
|
|
74
|
-
const { checkCommand } = await import("./check-
|
|
74
|
+
const { checkCommand } = await import("./check-Dvi0DIqC.mjs").then((n) => n.t);
|
|
75
75
|
const hookValue = flags.hook;
|
|
76
76
|
if (hookValue && hookValue !== "pre-commit" && hookValue !== "pre-push") {
|
|
77
77
|
log.error(`Invalid --hook value "${hookValue}". Expected "pre-commit" or "pre-push".`);
|
|
@@ -89,17 +89,17 @@ async function main() {
|
|
|
89
89
|
const subcommand = args[1];
|
|
90
90
|
const ciFlags = parseFlags(args.slice(2));
|
|
91
91
|
if (subcommand === "check") {
|
|
92
|
-
const { ciCheckCommand } = await import("./ci-
|
|
92
|
+
const { ciCheckCommand } = await import("./ci-B7gF6CFP.mjs");
|
|
93
93
|
await ciCheckCommand(rootDir, {
|
|
94
94
|
comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
|
|
95
95
|
strict: ciFlags.strict === true,
|
|
96
96
|
noFail: ciFlags["no-fail"] === true
|
|
97
97
|
});
|
|
98
98
|
} else if (subcommand === "plan") {
|
|
99
|
-
const { ciPlanCommand } = await import("./ci-
|
|
99
|
+
const { ciPlanCommand } = await import("./ci-B7gF6CFP.mjs");
|
|
100
100
|
await ciPlanCommand(rootDir);
|
|
101
101
|
} else if (subcommand === "release") {
|
|
102
|
-
const { ciReleaseCommand } = await import("./ci-
|
|
102
|
+
const { ciReleaseCommand } = await import("./ci-B7gF6CFP.mjs");
|
|
103
103
|
const expectModeFlag = ciFlags["expect-mode"];
|
|
104
104
|
const autoPublishFlag = ciFlags["auto-publish"] === true;
|
|
105
105
|
if (expectModeFlag !== void 0 && expectModeFlag !== "version-pr" && expectModeFlag !== "publish") {
|
|
@@ -127,7 +127,7 @@ async function main() {
|
|
|
127
127
|
}
|
|
128
128
|
case "publish": {
|
|
129
129
|
const rootDir = await findRoot();
|
|
130
|
-
const { publishCommand } = await import("./publish-
|
|
130
|
+
const { publishCommand } = await import("./publish-VYBhDYFM.mjs");
|
|
131
131
|
await publishCommand(rootDir, {
|
|
132
132
|
dryRun: flags["dry-run"] === true,
|
|
133
133
|
tag: flags.tag,
|
|
@@ -151,7 +151,7 @@ async function main() {
|
|
|
151
151
|
}
|
|
152
152
|
case "--version":
|
|
153
153
|
case "-v":
|
|
154
|
-
console.log(`bumpy 1.13.
|
|
154
|
+
console.log(`bumpy 1.13.2`);
|
|
155
155
|
break;
|
|
156
156
|
case "help":
|
|
157
157
|
case "--help":
|
|
@@ -171,7 +171,7 @@ async function main() {
|
|
|
171
171
|
}
|
|
172
172
|
function printHelp() {
|
|
173
173
|
console.log(`
|
|
174
|
-
${colorize(`🐸 bumpy v1.13.
|
|
174
|
+
${colorize(`🐸 bumpy v1.13.2`, "bold")} - Modern monorepo versioning
|
|
175
175
|
|
|
176
176
|
Usage: bumpy <command> [options]
|
|
177
177
|
|
|
@@ -3,7 +3,7 @@ import { t as ensureDir } from "./fs-CBXKZhoU.mjs";
|
|
|
3
3
|
import { a as loadConfig, r as getBumpyDir } from "./config-D_4GYDJi.mjs";
|
|
4
4
|
import { a as writeBumpFile, o as discoverPackages } from "./bump-file-B7hmXZlB.mjs";
|
|
5
5
|
import { s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { c as getFilesChangedInCommit, i as getBranchCommits } from "./git-BWPimLgc.mjs";
|
|
7
7
|
import { n as slugify, t as randomName } from "./names-COooXAFg.mjs";
|
|
8
8
|
import { relative } from "node:path";
|
|
9
9
|
//#region src/commands/generate.ts
|
|
@@ -8,13 +8,23 @@ function createTag(tag, opts) {
|
|
|
8
8
|
tag
|
|
9
9
|
], opts);
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Force-push a single tag to origin, using BUMPY_GH_TOKEN if available.
|
|
13
|
+
*
|
|
14
|
+
* Force is required because `gh release create --draft --target SHA` creates
|
|
15
|
+
* the tag on the remote at draft-creation time. If a previous publish attempt
|
|
16
|
+
* failed and HEAD has since moved, the remote tag points at the stale SHA —
|
|
17
|
+
* `git push --tags` would reject. The caller is responsible for ensuring the
|
|
18
|
+
* local tag is at the correct SHA (i.e. only call after a successful publish).
|
|
19
|
+
*/
|
|
20
|
+
function forcePushTag(tag, opts) {
|
|
13
21
|
withGitToken(opts?.cwd, () => {
|
|
14
22
|
runArgs([
|
|
15
23
|
"git",
|
|
16
24
|
"push",
|
|
17
|
-
"
|
|
25
|
+
"origin",
|
|
26
|
+
`refs/tags/${tag}`,
|
|
27
|
+
"--force"
|
|
18
28
|
], opts);
|
|
19
29
|
});
|
|
20
30
|
}
|
|
@@ -260,4 +270,4 @@ function getFileStatuses(dir, opts) {
|
|
|
260
270
|
return statuses;
|
|
261
271
|
}
|
|
262
272
|
//#endregion
|
|
263
|
-
export {
|
|
273
|
+
export { getChangedFiles as a, getFilesChangedInCommit as c, tagExists as d, withGitToken as f, getBranchCommits as i, hasUncommittedChanges as l, forcePushTag as n, getCurrentBranch as o, getBaseCompareRef as r, getFileStatuses as s, createTag as t, readFileAtRef as u };
|
package/dist/index.mjs
CHANGED
|
@@ -4,5 +4,5 @@ import { a as writeBumpFile, n as parseBumpFile, o as discoverPackages, r as rea
|
|
|
4
4
|
import { a as DependencyGraph, i as stripProtocol, n as bumpVersion, r as satisfies, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
5
5
|
import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-CbaET5V6.mjs";
|
|
6
6
|
import { t as applyReleasePlan } from "./apply-release-plan-DD2R7SL2.mjs";
|
|
7
|
-
import { t as publishPackages } from "./publish-pipeline-
|
|
7
|
+
import { t as publishPackages } from "./publish-pipeline-BPedWvKS.mjs";
|
|
8
8
|
export { BUMP_LEVELS, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DependencyGraph, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, normalizeCascadeConfig, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
|
|
@@ -5,9 +5,9 @@ import { s as discoverWorkspace } from "./bump-file-B7hmXZlB.mjs";
|
|
|
5
5
|
import { a as DependencyGraph } from "./release-plan-mK7iGeGq.mjs";
|
|
6
6
|
import { r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
7
7
|
import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-CbaET5V6.mjs";
|
|
8
|
-
import {
|
|
9
|
-
import { t as publishPackages } from "./publish-pipeline-
|
|
10
|
-
import { CI_PLAN_CACHE_PATH } from "./ci-
|
|
8
|
+
import { d as tagExists, l as hasUncommittedChanges, n as forcePushTag } from "./git-BWPimLgc.mjs";
|
|
9
|
+
import { n as willUseOidcExclusively, t as publishPackages } from "./publish-pipeline-BPedWvKS.mjs";
|
|
10
|
+
import { CI_PLAN_CACHE_PATH } from "./ci-B7gF6CFP.mjs";
|
|
11
11
|
//#region src/core/github-release.ts
|
|
12
12
|
/** Get the current HEAD commit SHA */
|
|
13
13
|
function getHeadSha(rootDir) {
|
|
@@ -332,6 +332,17 @@ async function publishCommand(rootDir, opts) {
|
|
|
332
332
|
else log.bold("Publishing:");
|
|
333
333
|
for (const r of toPublish) console.log(` ${r.name}@${colorize(r.newVersion, "cyan")}`);
|
|
334
334
|
console.log();
|
|
335
|
+
if (willUseOidcExclusively(rootDir)) {
|
|
336
|
+
const newPackages = await findPackagesMissingFromNpm(toPublish, packages);
|
|
337
|
+
if (newPackages.length > 0) {
|
|
338
|
+
const logFn = opts.dryRun ? log.warn : log.error;
|
|
339
|
+
logFn(`Trusted publishing (OIDC) cannot create a new package. The following don't exist on npm yet:`);
|
|
340
|
+
for (const name of newPackages) logFn(` • ${name}`);
|
|
341
|
+
logFn(`Publish a 0.0.0 placeholder version manually to claim the name, then configure`);
|
|
342
|
+
logFn(`trusted publishing on npmjs.com. Bumpy will then publish the real version via OIDC.`);
|
|
343
|
+
if (!opts.dryRun) process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
335
346
|
const formatter = config.changelog !== false ? await loadFormatter(config.changelog, rootDir) : void 0;
|
|
336
347
|
const ghAvailable = isGhAvailable();
|
|
337
348
|
const publishTargetsByPkg = /* @__PURE__ */ new Map();
|
|
@@ -489,12 +500,22 @@ async function publishCommand(rootDir, opts) {
|
|
|
489
500
|
log.error(`Failed ${result.failed.length}: ${result.failed.map((f) => `${f.name} (${f.error})`).join(", ")}`);
|
|
490
501
|
process.exit(1);
|
|
491
502
|
}
|
|
492
|
-
if (!opts.dryRun && !opts.noPush && result.published.length > 0)
|
|
503
|
+
if (!opts.dryRun && !opts.noPush && result.published.length > 0) {
|
|
504
|
+
const failed = new Set(result.failed.map((f) => f.name));
|
|
505
|
+
const pushed = [];
|
|
493
506
|
log.step("Pushing tags...");
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
507
|
+
for (const release of releasePlan.releases) {
|
|
508
|
+
if (failed.has(release.name)) continue;
|
|
509
|
+
const tag = `${release.name}@${release.newVersion}`;
|
|
510
|
+
if (!tagExists(tag, { cwd: rootDir })) continue;
|
|
511
|
+
try {
|
|
512
|
+
forcePushTag(tag, { cwd: rootDir });
|
|
513
|
+
pushed.push(tag);
|
|
514
|
+
} catch (err) {
|
|
515
|
+
log.warn(` Failed to push tag ${tag}: ${err instanceof Error ? err.message : err}`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (pushed.length > 0) log.success(`Pushed ${pushed.length} tag(s) to remote`);
|
|
498
519
|
}
|
|
499
520
|
if (!ghAvailable && result.published.length > 0) await createIndividualReleases(releasePlan.releases.filter((r) => result.published.some((p) => p.name === r.name)), releasePlan.bumpFiles, rootDir, {
|
|
500
521
|
dryRun: opts.dryRun,
|
|
@@ -618,5 +639,38 @@ async function checkIfPublished(name, version, pkgConfig) {
|
|
|
618
639
|
return false;
|
|
619
640
|
}
|
|
620
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Check whether a package exists on npm at all (any version).
|
|
644
|
+
* Returns true if the package is registered, false if it doesn't exist or the query fails.
|
|
645
|
+
*/
|
|
646
|
+
async function packageExistsOnNpm(name, registry) {
|
|
647
|
+
const args = [
|
|
648
|
+
"npm",
|
|
649
|
+
"info",
|
|
650
|
+
name,
|
|
651
|
+
"name"
|
|
652
|
+
];
|
|
653
|
+
if (registry) args.push("--registry", registry);
|
|
654
|
+
try {
|
|
655
|
+
return (await runArgsAsync(args)).trim() === name;
|
|
656
|
+
} catch {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Filter `toPublish` to package names that don't exist on npm yet.
|
|
662
|
+
* Skips packages not going through the standard npm publish flow.
|
|
663
|
+
*/
|
|
664
|
+
async function findPackagesMissingFromNpm(toPublish, packages) {
|
|
665
|
+
const missing = [];
|
|
666
|
+
await Promise.all(toPublish.map(async (release) => {
|
|
667
|
+
const pkg = packages.get(release.name);
|
|
668
|
+
const pkgConfig = pkg.bumpy || {};
|
|
669
|
+
if (pkgConfig.publishCommand || pkgConfig.skipNpmPublish) return;
|
|
670
|
+
if (pkg.private && !pkgConfig.publishCommand) return;
|
|
671
|
+
if (!await packageExistsOnNpm(release.name, pkgConfig.registry)) missing.push(release.name);
|
|
672
|
+
}));
|
|
673
|
+
return missing;
|
|
674
|
+
}
|
|
621
675
|
//#endregion
|
|
622
676
|
export { findUnpublishedPackages, publishCommand };
|
|
@@ -3,7 +3,7 @@ import { a as readJson, u as updateJsonNestedField } from "./fs-CBXKZhoU.mjs";
|
|
|
3
3
|
import { s as resolveCatalogDep } from "./package-manager-Db_vTztt.mjs";
|
|
4
4
|
import { i as stripProtocol } from "./release-plan-mK7iGeGq.mjs";
|
|
5
5
|
import { i as runAsync, o as sq, r as runArgsAsync, s as tryRunArgs } from "./shell-C8KgKnMQ.mjs";
|
|
6
|
-
import { d as tagExists, t as createTag } from "./git-
|
|
6
|
+
import { d as tagExists, t as createTag } from "./git-BWPimLgc.mjs";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
import { unlink } from "node:fs/promises";
|
|
9
9
|
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -23,6 +23,21 @@ function detectOidcProvider() {
|
|
|
23
23
|
if (process.env.CIRCLECI && process.env.NPM_ID_TOKEN) return "circleci";
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns true when OIDC trusted publishing is the only available npm auth path:
|
|
28
|
+
* an OIDC provider is detected AND no token env vars or .npmrc auth are present.
|
|
29
|
+
*
|
|
30
|
+
* Used to gate checks that only matter when OIDC will definitely be used — e.g.
|
|
31
|
+
* erroring when a brand-new package can't be bootstrapped via trusted publishing.
|
|
32
|
+
* Detection alone is leaky (id-token: write is also set for provenance), so this
|
|
33
|
+
* helper avoids false positives when a token fallback exists.
|
|
34
|
+
*/
|
|
35
|
+
function willUseOidcExclusively(rootDir) {
|
|
36
|
+
if (!detectOidcProvider()) return false;
|
|
37
|
+
if (process.env.NPM_TOKEN || process.env.NODE_AUTH_TOKEN) return false;
|
|
38
|
+
const npmrcPath = resolve(rootDir, ".npmrc");
|
|
39
|
+
return !(existsSync(npmrcPath) ? readFileSync(npmrcPath, "utf-8") : "").includes(":_authToken=");
|
|
40
|
+
}
|
|
26
41
|
const OIDC_NPM_UPGRADE_HINTS = {
|
|
27
42
|
"github-actions": "Add `actions/setup-node@v6` with `node-version: lts/*` to your workflow",
|
|
28
43
|
gitlab: "Use a Node.js image with npm >= 11.5.1 or run `npm install -g npm@latest`",
|
|
@@ -306,4 +321,4 @@ async function resolveProtocolsInPlace(pkg, packages, releasePlan, catalogs) {
|
|
|
306
321
|
}
|
|
307
322
|
}
|
|
308
323
|
//#endregion
|
|
309
|
-
export { publishPackages as t };
|
|
324
|
+
export { willUseOidcExclusively as n, publishPackages as t };
|
|
@@ -2,7 +2,7 @@ import { n as log, t as colorize } from "./logger-BgksGFuf.mjs";
|
|
|
2
2
|
import { a as loadConfig } from "./config-D_4GYDJi.mjs";
|
|
3
3
|
import { o as discoverPackages, r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-B7hmXZlB.mjs";
|
|
4
4
|
import { a as DependencyGraph, t as assembleReleasePlan } from "./release-plan-mK7iGeGq.mjs";
|
|
5
|
-
import { a as
|
|
5
|
+
import { a as getChangedFiles, o as getCurrentBranch } from "./git-BWPimLgc.mjs";
|
|
6
6
|
//#region src/commands/status.ts
|
|
7
7
|
async function statusCommand(rootDir, opts) {
|
|
8
8
|
const config = await loadConfig(rootDir);
|