@pnpm/releasing.commands 1100.2.8 → 1100.2.10
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/lib/publish/oidc/idToken.d.ts +1 -1
- package/lib/publish/oidc/idToken.js +8 -3
- package/lib/publish/pack.d.ts +2 -0
- package/lib/publish/pack.js +17 -1
- package/lib/publish/publish.d.ts +8 -1
- package/lib/publish/publish.js +21 -6
- package/lib/publish/publishPackedPkg.d.ts +49 -1
- package/lib/publish/publishPackedPkg.js +65 -13
- package/lib/publish/recursivePublish.d.ts +6 -1
- package/lib/publish/recursivePublish.js +13 -6
- package/lib/version/index.js +33 -1
- package/package.json +31 -30
|
@@ -57,7 +57,7 @@ export interface IdTokenParams {
|
|
|
57
57
|
* @see https://github.com/npm/cli/blob/7d900c46/lib/utils/oidc.js#L37-L110 for npm's implementation
|
|
58
58
|
* @see https://github.com/yarnpkg/berry/blob/bafbef55/packages/plugin-npm/sources/npmHttpUtils.ts#L594-L624 for yarn's implementation
|
|
59
59
|
*/
|
|
60
|
-
export declare function getIdToken({ context: { Date, ciInfo: { GITHUB_ACTIONS
|
|
60
|
+
export declare function getIdToken({ context: { Date, ciInfo: { GITHUB_ACTIONS }, fetch, globalInfo, process: { env } }, options, registry }: IdTokenParams): Promise<string | undefined>;
|
|
61
61
|
export declare abstract class IdTokenError extends PnpmError {
|
|
62
62
|
}
|
|
63
63
|
export declare class IdTokenGitHubWorkflowIncorrectPermissionsError extends IdTokenError {
|
|
@@ -10,11 +10,16 @@ import { SHARED_CONTEXT } from '../utils/shared-context.js';
|
|
|
10
10
|
* @see https://github.com/npm/cli/blob/7d900c46/lib/utils/oidc.js#L37-L110 for npm's implementation
|
|
11
11
|
* @see https://github.com/yarnpkg/berry/blob/bafbef55/packages/plugin-npm/sources/npmHttpUtils.ts#L594-L624 for yarn's implementation
|
|
12
12
|
*/
|
|
13
|
-
export async function getIdToken({ context: { Date, ciInfo: { GITHUB_ACTIONS
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
export async function getIdToken({ context: { Date, ciInfo: { GITHUB_ACTIONS }, fetch, globalInfo, process: { env }, } = SHARED_CONTEXT, options, registry, }) {
|
|
14
|
+
// `NPM_ID_TOKEN` is the canonical CI-agnostic injection point for an OIDC ID token.
|
|
15
|
+
// Any CI provider with its own OIDC mechanism (GitLab natively, CircleCI's
|
|
16
|
+
// `CIRCLE_OIDC_TOKEN_V2`, Buildkite, etc.) can forward its token here and trusted
|
|
17
|
+
// publishing will work — we don't need to recognize the provider ourselves.
|
|
16
18
|
if (env?.NPM_ID_TOKEN)
|
|
17
19
|
return env.NPM_ID_TOKEN;
|
|
20
|
+
// Without an explicit token, the only flow we can drive ourselves is GitHub Actions'
|
|
21
|
+
// request-token endpoint. Anywhere else (other CIs without `NPM_ID_TOKEN`, local dev)
|
|
22
|
+
// falls through silently and the publish caller falls back to a static `_authToken`.
|
|
18
23
|
if (!GITHUB_ACTIONS)
|
|
19
24
|
return undefined;
|
|
20
25
|
if (!env?.ACTIONS_ID_TOKEN_REQUEST_TOKEN || !env?.ACTIONS_ID_TOKEN_REQUEST_URL) {
|
package/lib/publish/pack.d.ts
CHANGED
package/lib/publish/pack.js
CHANGED
|
@@ -234,11 +234,27 @@ export async function api(opts) {
|
|
|
234
234
|
else {
|
|
235
235
|
packedTarballPath = path.relative(opts.dir, path.join(dir, tarballName));
|
|
236
236
|
}
|
|
237
|
-
|
|
237
|
+
// Derive `contents` and `unpackedSize` from `filesMap` (the full set of tar entries) rather than
|
|
238
|
+
// from `files` (the packlist subset) so that:
|
|
239
|
+
// - workspace LICENSE files appended to `filesMap` after the packlist call are included; and
|
|
240
|
+
// - `package.yaml` / `package.json5` entries are reported under the name they actually have in
|
|
241
|
+
// the tar (`package.json`), since `packPkg()` rewrites them.
|
|
242
|
+
const sizes = await Promise.all(Object.entries(filesMap).map(async ([name, source]) => {
|
|
243
|
+
if (/^package\/package\.(?:json|json5|yaml)$/.test(name)) {
|
|
244
|
+
return Buffer.byteLength(JSON.stringify(publishManifest, null, 2));
|
|
245
|
+
}
|
|
246
|
+
const stat = await fs.promises.stat(source);
|
|
247
|
+
return stat.size;
|
|
248
|
+
}));
|
|
249
|
+
const unpackedSize = sizes.reduce((acc, size) => acc + size, 0);
|
|
250
|
+
const packedContents = Array.from(new Set(Object.keys(filesMap).map((name) => /^package\/package\.(?:json|json5|yaml)$/.test(name)
|
|
251
|
+
? 'package.json'
|
|
252
|
+
: name.replace(/^package\//, '')))).sort((a, b) => a.localeCompare(b, 'en'));
|
|
238
253
|
return {
|
|
239
254
|
publishedManifest: publishManifest,
|
|
240
255
|
contents: packedContents,
|
|
241
256
|
tarballPath: packedTarballPath,
|
|
257
|
+
unpackedSize,
|
|
242
258
|
};
|
|
243
259
|
}
|
|
244
260
|
function preventBundledDependenciesWithoutHoistedNodeLinker(nodeLinker, manifest) {
|
package/lib/publish/publish.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { type Config, type ConfigContext } from '@pnpm/config.reader';
|
|
|
2
2
|
import { type RunLifecycleHookOptions } from '@pnpm/exec.lifecycle';
|
|
3
3
|
import type { ExportedManifest } from '@pnpm/releasing.exportable-manifest';
|
|
4
4
|
import type { ProjectManifest } from '@pnpm/types';
|
|
5
|
-
import { type
|
|
5
|
+
import { type PublishSummary } from './publishPackedPkg.js';
|
|
6
|
+
import { type PublishRecursiveOpts, type RecursivePublishedPackage } from './recursivePublish.js';
|
|
6
7
|
export declare function rcOptionsTypes(): Record<string, unknown>;
|
|
7
8
|
export declare function cliOptionsTypes(): Record<string, unknown>;
|
|
8
9
|
export declare const commandNames: string[];
|
|
@@ -12,15 +13,21 @@ export declare function handler(opts: Omit<PublishRecursiveOpts, 'workspaceDir'>
|
|
|
12
13
|
original: string[];
|
|
13
14
|
};
|
|
14
15
|
engineStrict?: boolean;
|
|
16
|
+
json?: boolean;
|
|
15
17
|
recursive?: boolean;
|
|
16
18
|
workspaceDir?: string;
|
|
17
19
|
} & Pick<Config, 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme'> & Pick<ConfigContext, 'allProjects'>, params: string[]): Promise<{
|
|
18
20
|
exitCode?: number;
|
|
21
|
+
output?: string;
|
|
19
22
|
} | undefined>;
|
|
20
23
|
export interface PublishResult {
|
|
21
24
|
exitCode?: number;
|
|
22
25
|
manifest?: ProjectManifest;
|
|
23
26
|
publishedManifest?: ExportedManifest;
|
|
27
|
+
/** Per-package summary in the npm-CLI `--json` shape; only populated for single-package publish. */
|
|
28
|
+
publishSummary?: PublishSummary;
|
|
29
|
+
/** Per-package summaries collected by recursive publish. */
|
|
30
|
+
publishedPackages?: RecursivePublishedPackage[];
|
|
24
31
|
}
|
|
25
32
|
export declare function publish(opts: Omit<PublishRecursiveOpts, 'workspaceDir'> & {
|
|
26
33
|
argv: {
|
package/lib/publish/publish.js
CHANGED
|
@@ -106,6 +106,16 @@ export function help() {
|
|
|
106
106
|
const GIT_CHECKS_HINT = 'If you want to disable Git checks on publish, set the "git-checks" setting to "false", or run again with "--no-git-checks".';
|
|
107
107
|
export async function handler(opts, params) {
|
|
108
108
|
const result = await publish(opts, params);
|
|
109
|
+
// Emit per-package summaries on stdout when --json is set: single object for a single-package
|
|
110
|
+
// publish, array for recursive publish. Mirrors `pnpm pack --json`'s shape choice.
|
|
111
|
+
if (opts.json) {
|
|
112
|
+
if (result?.publishSummary) {
|
|
113
|
+
return { output: JSON.stringify(result.publishSummary, null, 2), exitCode: 0 };
|
|
114
|
+
}
|
|
115
|
+
if (result?.publishedPackages) {
|
|
116
|
+
return { output: JSON.stringify(result.publishedPackages, null, 2), exitCode: result.exitCode ?? 0 };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
109
119
|
if (result?.manifest)
|
|
110
120
|
return;
|
|
111
121
|
return result;
|
|
@@ -144,23 +154,27 @@ Do you want to continue?`,
|
|
|
144
154
|
}
|
|
145
155
|
}
|
|
146
156
|
if (opts.recursive && (opts.selectedProjectsGraph != null)) {
|
|
147
|
-
const { exitCode } = await recursivePublish({
|
|
157
|
+
const { exitCode, publishedPackages } = await recursivePublish({
|
|
148
158
|
...opts,
|
|
149
159
|
selectedProjectsGraph: opts.selectedProjectsGraph,
|
|
150
160
|
workspaceDir: opts.workspaceDir ?? process.cwd(),
|
|
151
161
|
});
|
|
152
|
-
return { exitCode };
|
|
162
|
+
return { exitCode, publishedPackages };
|
|
153
163
|
}
|
|
154
164
|
opts = optionsWithOtpEnv(opts, process.env);
|
|
155
165
|
const dirInParams = (params.length > 0) ? params[0] : undefined;
|
|
156
166
|
if (dirInParams != null && isTarballPath(dirInParams)) {
|
|
157
167
|
const tarballPath = dirInParams;
|
|
158
168
|
const publishedManifest = await extractManifestFromPacked(tarballPath);
|
|
159
|
-
|
|
169
|
+
// Publishing a pre-built tarball bypasses `pack.api()`, so we don't have the file listing
|
|
170
|
+
// or unpacked size — those summary fields are reported as empty/zero.
|
|
171
|
+
const publishSummary = await publishPackedPkg({
|
|
160
172
|
tarballPath,
|
|
161
173
|
publishedManifest,
|
|
174
|
+
contents: [],
|
|
175
|
+
unpackedSize: 0,
|
|
162
176
|
}, opts);
|
|
163
|
-
return { exitCode: 0 };
|
|
177
|
+
return { exitCode: 0, publishSummary };
|
|
164
178
|
}
|
|
165
179
|
const dir = dirInParams ?? opts.dir ?? process.cwd();
|
|
166
180
|
const _runScriptsIfPresent = runScriptsIfPresent.bind(null, {
|
|
@@ -187,6 +201,7 @@ Do you want to continue?`,
|
|
|
187
201
|
// that was generated and packed to the tarball.
|
|
188
202
|
const packDestination = temporaryDirectory();
|
|
189
203
|
let publishedManifest;
|
|
204
|
+
let publishSummary;
|
|
190
205
|
try {
|
|
191
206
|
const packResult = await pack.api({
|
|
192
207
|
...opts,
|
|
@@ -194,7 +209,7 @@ Do you want to continue?`,
|
|
|
194
209
|
packDestination,
|
|
195
210
|
dryRun: false,
|
|
196
211
|
});
|
|
197
|
-
await publishPackedPkg(packResult, opts);
|
|
212
|
+
publishSummary = await publishPackedPkg(packResult, opts);
|
|
198
213
|
publishedManifest = packResult.publishedManifest;
|
|
199
214
|
}
|
|
200
215
|
finally {
|
|
@@ -206,7 +221,7 @@ Do you want to continue?`,
|
|
|
206
221
|
'postpublish',
|
|
207
222
|
], manifest);
|
|
208
223
|
}
|
|
209
|
-
return { manifest, publishedManifest };
|
|
224
|
+
return { manifest, publishedManifest, publishSummary };
|
|
210
225
|
}
|
|
211
226
|
export async function runScriptsIfPresent(opts, scriptNames, manifest) {
|
|
212
227
|
for (const scriptName of scriptNames) {
|
|
@@ -8,8 +8,56 @@ export type PublishPackedPkgOptions = Pick<Config, 'configByUri' | 'dryRun' | 'f
|
|
|
8
8
|
provenance?: boolean;
|
|
9
9
|
provenanceFile?: string;
|
|
10
10
|
};
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Per-package summary describing a successful publish, modeled after `npm publish --json`.
|
|
13
|
+
* Returned to callers and serialized to stdout when `pnpm publish --json` is used.
|
|
14
|
+
*/
|
|
15
|
+
export interface PublishSummary {
|
|
16
|
+
/** Human-readable identifier `${name}@${version}`. */
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
/** Compressed tarball size in bytes. */
|
|
21
|
+
size: number;
|
|
22
|
+
/** Total uncompressed size of all files in the tarball, in bytes. */
|
|
23
|
+
unpackedSize: number;
|
|
24
|
+
/** Lowercase hex SHA-1 digest of the tarball. */
|
|
25
|
+
shasum: string;
|
|
26
|
+
/** SRI-formatted SHA-512 digest of the tarball (e.g. `sha512-...`). */
|
|
27
|
+
integrity: string;
|
|
28
|
+
/** Tarball file basename (e.g. `pkg-1.0.0.tgz`). */
|
|
29
|
+
filename: string;
|
|
30
|
+
/** Files inside the tarball, in the same shape `pnpm pack --json` emits. */
|
|
31
|
+
files: Array<{
|
|
32
|
+
path: string;
|
|
33
|
+
}>;
|
|
34
|
+
/** Number of files inside the tarball. */
|
|
35
|
+
entryCount: number;
|
|
36
|
+
/** Names of bundled dependencies included in the tarball (typically empty). */
|
|
37
|
+
bundled: string[];
|
|
38
|
+
}
|
|
39
|
+
export declare function publishPackedPkg(packResult: Pick<PackResult, 'publishedManifest' | 'tarballPath' | 'contents' | 'unpackedSize'>, opts: PublishPackedPkgOptions): Promise<PublishSummary>;
|
|
12
40
|
export declare class PublishUnsupportedRegistryProtocolError extends PnpmError {
|
|
13
41
|
readonly registryUrl: string;
|
|
14
42
|
constructor(registryUrl: string);
|
|
15
43
|
}
|
|
44
|
+
interface OidcTokenProvenanceResult {
|
|
45
|
+
authToken: string;
|
|
46
|
+
provenance?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Try fetching an authentication token and provenance by OpenID Connect.
|
|
50
|
+
*
|
|
51
|
+
* The result, when defined, is intended to take precedence over any statically configured
|
|
52
|
+
* authentication. This mirrors the npm CLI's OIDC flow, which always attempts the exchange
|
|
53
|
+
* in supported CI environments and overwrites a configured `_authToken` on success.
|
|
54
|
+
*
|
|
55
|
+
* @returns the OIDC-derived authToken (and provenance flag) on success, or `undefined` when
|
|
56
|
+
* OIDC is not applicable / not configured on the registry — in which case callers should
|
|
57
|
+
* fall back to whatever static authentication they already have.
|
|
58
|
+
*
|
|
59
|
+
* @internal Exported for unit testing of the precedence rules. Not part of the package's
|
|
60
|
+
* public API.
|
|
61
|
+
*/
|
|
62
|
+
export declare function fetchTokenAndProvenanceByOidc(packageName: string, registry: string, options: PublishPackedPkgOptions): Promise<OidcTokenProvenanceResult | undefined>;
|
|
63
|
+
export {};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
2
4
|
import { PnpmError } from '@pnpm/error';
|
|
3
5
|
import { globalInfo, globalWarn } from '@pnpm/logger';
|
|
4
6
|
import { displayError } from './displayError.js';
|
|
@@ -10,23 +12,53 @@ import { determineProvenance, ProvenanceError } from './oidc/provenance.js';
|
|
|
10
12
|
import { publishWithOtpHandling } from './otp.js';
|
|
11
13
|
import { allRegistryConfigKeys, parseSupportedRegistryUrl } from './registryConfigKeys.js';
|
|
12
14
|
export async function publishPackedPkg(packResult, opts) {
|
|
13
|
-
const { publishedManifest, tarballPath } = packResult;
|
|
15
|
+
const { publishedManifest, tarballPath, contents, unpackedSize } = packResult;
|
|
14
16
|
const tarballData = await fs.readFile(tarballPath);
|
|
15
17
|
const publishOptions = await createPublishOptions(publishedManifest, opts);
|
|
16
18
|
const { name, version } = publishedManifest;
|
|
17
19
|
const { registry } = publishOptions;
|
|
18
20
|
globalInfo(`📦 ${name}@${version} → ${registry ?? 'the default registry'}`);
|
|
21
|
+
const summary = {
|
|
22
|
+
id: `${name}@${version}`,
|
|
23
|
+
name: name,
|
|
24
|
+
version: version,
|
|
25
|
+
size: tarballData.byteLength,
|
|
26
|
+
unpackedSize,
|
|
27
|
+
// SHA-1 is what `npm publish --json` reports as `shasum` for back-compat with the registry's
|
|
28
|
+
// legacy dist.shasum field; `integrity` below is the modern SRI hash.
|
|
29
|
+
shasum: createHash('sha1').update(tarballData).digest('hex'),
|
|
30
|
+
integrity: `sha512-${createHash('sha512').update(tarballData).digest('base64')}`,
|
|
31
|
+
filename: path.basename(tarballPath),
|
|
32
|
+
files: contents.map((file) => ({ path: file })),
|
|
33
|
+
entryCount: contents.length,
|
|
34
|
+
bundled: extractBundledDependencies(publishedManifest),
|
|
35
|
+
};
|
|
19
36
|
if (opts.dryRun) {
|
|
20
37
|
globalWarn(`Skip publishing ${name}@${version} (dry run)`);
|
|
21
|
-
return;
|
|
38
|
+
return summary;
|
|
22
39
|
}
|
|
23
40
|
const response = await publishWithOtpHandling({ manifest: publishedManifest, tarballData, publishOptions });
|
|
24
41
|
if (response.ok) {
|
|
25
42
|
globalInfo(`✅ Published package ${name}@${version}`);
|
|
26
|
-
return;
|
|
43
|
+
return summary;
|
|
27
44
|
}
|
|
28
45
|
throw await createFailedToPublishError(packResult, response);
|
|
29
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* npm accepts both `bundledDependencies` and `bundleDependencies` in package.json and normalizes
|
|
49
|
+
* to a list of dependency names. We mirror that normalization so consumers see a consistent array.
|
|
50
|
+
*/
|
|
51
|
+
function extractBundledDependencies(manifest) {
|
|
52
|
+
const raw = manifest.bundledDependencies ?? manifest.bundleDependencies;
|
|
53
|
+
if (!raw)
|
|
54
|
+
return [];
|
|
55
|
+
if (Array.isArray(raw))
|
|
56
|
+
return raw;
|
|
57
|
+
// `true` means "bundle every dependency" per npm's semantics; expand it to the dependency names.
|
|
58
|
+
if (raw === true)
|
|
59
|
+
return Object.keys(manifest.dependencies ?? {});
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
30
62
|
async function createPublishOptions(manifest, options) {
|
|
31
63
|
const publishConfigRegistry = typeof manifest.publishConfig?.registry === 'string'
|
|
32
64
|
? manifest.publishConfig.registry
|
|
@@ -67,8 +99,14 @@ async function createPublishOptions(manifest, options) {
|
|
|
67
99
|
password: creds?.basicAuth?.password,
|
|
68
100
|
};
|
|
69
101
|
if (registry) {
|
|
70
|
-
|
|
71
|
-
|
|
102
|
+
// OIDC takes precedence over a configured static `_authToken`, mirroring the npm CLI's
|
|
103
|
+
// behavior (see https://github.com/npm/cli/blob/7d900c46/lib/utils/oidc.js). Trusted
|
|
104
|
+
// publishing wins whenever the registry has it configured for the package; the static
|
|
105
|
+
// token is used only as a fallback when OIDC is not applicable.
|
|
106
|
+
const oidcTokenProvenance = await fetchTokenAndProvenanceByOidc(manifest.name, registry, options);
|
|
107
|
+
if (oidcTokenProvenance?.authToken) {
|
|
108
|
+
publishOptions.token = oidcTokenProvenance.authToken;
|
|
109
|
+
}
|
|
72
110
|
publishOptions.provenance ??= oidcTokenProvenance?.provenance;
|
|
73
111
|
appendAuthOptionsForRegistry(publishOptions, registry);
|
|
74
112
|
}
|
|
@@ -131,13 +169,20 @@ export class PublishUnsupportedRegistryProtocolError extends PnpmError {
|
|
|
131
169
|
}
|
|
132
170
|
}
|
|
133
171
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
172
|
+
* Try fetching an authentication token and provenance by OpenID Connect.
|
|
173
|
+
*
|
|
174
|
+
* The result, when defined, is intended to take precedence over any statically configured
|
|
175
|
+
* authentication. This mirrors the npm CLI's OIDC flow, which always attempts the exchange
|
|
176
|
+
* in supported CI environments and overwrites a configured `_authToken` on success.
|
|
177
|
+
*
|
|
178
|
+
* @returns the OIDC-derived authToken (and provenance flag) on success, or `undefined` when
|
|
179
|
+
* OIDC is not applicable / not configured on the registry — in which case callers should
|
|
180
|
+
* fall back to whatever static authentication they already have.
|
|
181
|
+
*
|
|
182
|
+
* @internal Exported for unit testing of the precedence rules. Not part of the package's
|
|
183
|
+
* public API.
|
|
136
184
|
*/
|
|
137
|
-
async function
|
|
138
|
-
if (targetPublishOptions.token != null ||
|
|
139
|
-
(targetPublishOptions.username && targetPublishOptions.password))
|
|
140
|
-
return undefined;
|
|
185
|
+
export async function fetchTokenAndProvenanceByOidc(packageName, registry, options) {
|
|
141
186
|
let idToken;
|
|
142
187
|
try {
|
|
143
188
|
idToken = await getIdToken({
|
|
@@ -153,7 +198,11 @@ async function fetchTokenAndProvenanceByOidcIfApplicable(targetPublishOptions, p
|
|
|
153
198
|
throw error;
|
|
154
199
|
}
|
|
155
200
|
if (!idToken) {
|
|
156
|
-
|
|
201
|
+
// OIDC is simply not applicable here — either we're outside of CI, or we're in a CI
|
|
202
|
+
// that doesn't natively drive OIDC and the user hasn't forwarded a token via
|
|
203
|
+
// `NPM_ID_TOKEN`. This is the common case for local publishes, so it must stay
|
|
204
|
+
// silent — only configuration *errors* in a supported CI environment surface as
|
|
205
|
+
// warnings, and those come back as `IdTokenError` and are handled above.
|
|
157
206
|
return undefined;
|
|
158
207
|
}
|
|
159
208
|
let authToken;
|
|
@@ -190,8 +239,11 @@ async function fetchTokenAndProvenanceByOidcIfApplicable(targetPublishOptions, p
|
|
|
190
239
|
}
|
|
191
240
|
catch (error) {
|
|
192
241
|
if (error instanceof ProvenanceError) {
|
|
242
|
+
// Don't lose the OIDC-derived authToken just because we couldn't determine the
|
|
243
|
+
// provenance flag — the publish itself can still go through, and that's what
|
|
244
|
+
// the npm CLI does too.
|
|
193
245
|
globalWarn(`Skipped setting provenance: ${displayError(error)}`);
|
|
194
|
-
return
|
|
246
|
+
return { authToken };
|
|
195
247
|
}
|
|
196
248
|
throw error;
|
|
197
249
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Config, ConfigContext } from '@pnpm/config.reader';
|
|
2
|
-
import type { PublishPackedPkgOptions } from './publishPackedPkg.js';
|
|
2
|
+
import type { PublishPackedPkgOptions, PublishSummary } from './publishPackedPkg.js';
|
|
3
3
|
export type PublishRecursiveOpts = Required<Pick<Config, 'bin' | 'cacheDir' | 'dir' | 'pnpmHomeDir' | 'configByUri' | 'registries' | 'workspaceDir'>> & Required<Pick<ConfigContext, 'cliOptions'>> & Partial<Pick<Config, 'tag' | 'ca' | 'catalogs' | 'cert' | 'fetchTimeout' | 'force' | 'dryRun' | 'extraBinPaths' | 'extraEnv' | 'fetchRetries' | 'fetchRetryFactor' | 'fetchRetryMaxtimeout' | 'fetchRetryMintimeout' | 'key' | 'httpProxy' | 'httpsProxy' | 'localAddress' | 'lockfileDir' | 'noProxy' | 'npmPath' | 'offline' | 'strictSsl' | 'unsafePerm' | 'userAgent' | 'verifyStoreIntegrity'>> & Partial<Pick<ConfigContext, 'selectedProjectsGraph'>> & {
|
|
4
4
|
access?: 'public' | 'restricted';
|
|
5
5
|
argv: {
|
|
@@ -7,6 +7,11 @@ export type PublishRecursiveOpts = Required<Pick<Config, 'bin' | 'cacheDir' | 'd
|
|
|
7
7
|
};
|
|
8
8
|
reportSummary?: boolean;
|
|
9
9
|
} & PublishPackedPkgOptions;
|
|
10
|
+
export type RecursivePublishedPackage = PublishSummary | {
|
|
11
|
+
name?: string;
|
|
12
|
+
version?: string;
|
|
13
|
+
};
|
|
10
14
|
export declare function recursivePublish(opts: PublishRecursiveOpts & Required<Pick<ConfigContext, 'selectedProjectsGraph'>>): Promise<{
|
|
11
15
|
exitCode: number;
|
|
16
|
+
publishedPackages: RecursivePublishedPackage[];
|
|
12
17
|
}>;
|
|
@@ -83,12 +83,19 @@ export async function recursivePublish(opts) {
|
|
|
83
83
|
gitChecks: false,
|
|
84
84
|
recursive: false,
|
|
85
85
|
}, [pkg.rootDir]);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
publishedPackages.push(pick(['name', 'version'], publishedManifest));
|
|
86
|
+
if (publishResult?.publishSummary != null) {
|
|
87
|
+
publishedPackages.push(publishResult.publishSummary);
|
|
89
88
|
}
|
|
90
|
-
else
|
|
91
|
-
|
|
89
|
+
else {
|
|
90
|
+
// Fallback for paths that don't produce a full PublishSummary (e.g. dry run via the
|
|
91
|
+
// legacy npm-CLI bridge, or future call sites that bypass publishPackedPkg).
|
|
92
|
+
const publishedManifest = publishResult?.publishedManifest ?? publishResult?.manifest;
|
|
93
|
+
if (publishedManifest != null) {
|
|
94
|
+
publishedPackages.push(pick(['name', 'version'], publishedManifest));
|
|
95
|
+
}
|
|
96
|
+
else if (publishResult?.exitCode) {
|
|
97
|
+
return { exitCode: publishResult.exitCode, publishedPackages };
|
|
98
|
+
}
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
101
|
}
|
|
@@ -96,7 +103,7 @@ export async function recursivePublish(opts) {
|
|
|
96
103
|
if (opts.reportSummary) {
|
|
97
104
|
await writeJsonFile(path.join(opts.lockfileDir ?? opts.dir, 'pnpm-publish-summary.json'), { publishedPackages });
|
|
98
105
|
}
|
|
99
|
-
return { exitCode: 0 };
|
|
106
|
+
return { exitCode: 0, publishedPackages };
|
|
100
107
|
}
|
|
101
108
|
async function isAlreadyPublished(opts, pkgName, pkgVersion) {
|
|
102
109
|
try {
|
package/lib/version/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { readProjectManifest } from '@pnpm/cli.utils';
|
|
3
3
|
import { types as allTypes } from '@pnpm/config.reader';
|
|
4
4
|
import { PnpmError } from '@pnpm/error';
|
|
5
|
+
import { runLifecycleHook } from '@pnpm/exec.lifecycle';
|
|
5
6
|
import { isGitRepo, isWorkingTreeClean } from '@pnpm/network.git-utils';
|
|
6
7
|
import { filterProjectsFromDir } from '@pnpm/workspace.projects-filter';
|
|
7
8
|
import { safeExeca as execa } from 'execa';
|
|
@@ -146,6 +147,7 @@ export async function handler(opts, params) {
|
|
|
146
147
|
if (!opts.recursive && opts.gitTagVersion !== false && await isGitRepo({ cwd: gitCwd })) {
|
|
147
148
|
await commitAndTag(changes, { ...opts, cwd: gitCwd });
|
|
148
149
|
}
|
|
150
|
+
await Promise.all(changes.map(change => runVersionLifecycleHook('postversion', change, opts)));
|
|
149
151
|
if (opts.json) {
|
|
150
152
|
return JSON.stringify(changes.map(({ manifestPath: _manifestPath, ...change }) => change), null, 2);
|
|
151
153
|
}
|
|
@@ -164,6 +166,14 @@ async function bumpPackageVersion(pkgDir, rawBump, explicitVersion, opts) {
|
|
|
164
166
|
if (!valid(currentVersion)) {
|
|
165
167
|
throw new PnpmError('INVALID_VERSION', `Invalid version in ${pkgDir}: ${currentVersion}`);
|
|
166
168
|
}
|
|
169
|
+
const preVersionChange = {
|
|
170
|
+
name: manifest.name,
|
|
171
|
+
currentVersion,
|
|
172
|
+
newVersion: currentVersion,
|
|
173
|
+
path: pkgDir,
|
|
174
|
+
manifestPath: path.join(pkgDir, fileName),
|
|
175
|
+
};
|
|
176
|
+
await runVersionLifecycleHook('preversion', preVersionChange, opts);
|
|
167
177
|
const newVersion = explicitVersion ?? inc(currentVersion, rawBump, false, opts.preid);
|
|
168
178
|
if (!newVersion) {
|
|
169
179
|
throw new PnpmError('VERSION_BUMP_FAILED', `Failed to bump version from ${currentVersion} using ${rawBump}`);
|
|
@@ -173,13 +183,35 @@ async function bumpPackageVersion(pkgDir, rawBump, explicitVersion, opts) {
|
|
|
173
183
|
}
|
|
174
184
|
manifest.version = newVersion;
|
|
175
185
|
await writeProjectManifest(manifest);
|
|
176
|
-
|
|
186
|
+
const change = {
|
|
177
187
|
name: manifest.name,
|
|
178
188
|
currentVersion,
|
|
179
189
|
newVersion,
|
|
180
190
|
path: pkgDir,
|
|
181
191
|
manifestPath: path.join(pkgDir, fileName),
|
|
182
192
|
};
|
|
193
|
+
await runVersionLifecycleHook('version', change, opts);
|
|
194
|
+
return change;
|
|
195
|
+
}
|
|
196
|
+
async function runVersionLifecycleHook(stage, change, opts) {
|
|
197
|
+
if (opts.ignoreScripts === true)
|
|
198
|
+
return;
|
|
199
|
+
const { manifest } = await readProjectManifest(change.path);
|
|
200
|
+
const lifecycleOpts = {
|
|
201
|
+
depPath: change.name,
|
|
202
|
+
extraBinPaths: opts.extraBinPaths,
|
|
203
|
+
extraEnv: opts.extraEnv,
|
|
204
|
+
initCwd: opts.dir,
|
|
205
|
+
pkgRoot: change.path,
|
|
206
|
+
rootModulesDir: path.join(change.path, opts.modulesDir ?? 'node_modules'),
|
|
207
|
+
scriptShell: opts.scriptShell,
|
|
208
|
+
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
|
209
|
+
shellEmulator: opts.shellEmulator,
|
|
210
|
+
stdio: 'inherit',
|
|
211
|
+
unsafePerm: opts.unsafePerm ?? false,
|
|
212
|
+
userAgent: opts.userAgent,
|
|
213
|
+
};
|
|
214
|
+
await runLifecycleHook(stage, manifest, lifecycleOpts);
|
|
183
215
|
}
|
|
184
216
|
async function commitAndTag(changes, opts) {
|
|
185
217
|
const resolvedCwd = path.resolve(opts.cwd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/releasing.commands",
|
|
3
|
-
"version": "1100.2.
|
|
3
|
+
"version": "1100.2.10",
|
|
4
4
|
"description": "Commands for deploy, pack, and publish",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -27,23 +27,23 @@
|
|
|
27
27
|
"@pnpm/npm-package-arg": "^2.0.0",
|
|
28
28
|
"@types/normalize-path": "^3.0.2",
|
|
29
29
|
"@zkochan/rimraf": "^4.0.0",
|
|
30
|
-
"chalk": "^5.6.
|
|
31
|
-
"ci-info": "^4.
|
|
32
|
-
"detect-libc": "^2.
|
|
30
|
+
"chalk": "^5.6.2",
|
|
31
|
+
"ci-info": "^4.4.0",
|
|
32
|
+
"detect-libc": "^2.1.2",
|
|
33
33
|
"enquirer": "^2.4.1",
|
|
34
34
|
"execa": "npm:safe-execa@0.3.0",
|
|
35
35
|
"libnpmpublish": "^11.1.3",
|
|
36
36
|
"normalize-path": "^3.0.0",
|
|
37
37
|
"normalize-registry-url": "2.0.1",
|
|
38
38
|
"p-filter": "^4.1.0",
|
|
39
|
-
"p-limit": "^7.
|
|
39
|
+
"p-limit": "^7.3.0",
|
|
40
40
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
41
41
|
"realpath-missing": "^2.0.0",
|
|
42
42
|
"render-help": "^2.0.0",
|
|
43
|
-
"semver": "^7.7.
|
|
44
|
-
"tar-stream": "^3.
|
|
43
|
+
"semver": "^7.7.4",
|
|
44
|
+
"tar-stream": "^3.2.0",
|
|
45
45
|
"tempy": "3.0.0",
|
|
46
|
-
"tinyglobby": "^0.2.
|
|
46
|
+
"tinyglobby": "^0.2.16",
|
|
47
47
|
"validate-npm-package-name": "7.0.2",
|
|
48
48
|
"write-json-file": "^7.0.0",
|
|
49
49
|
"write-yaml-file": "^6.0.0",
|
|
@@ -51,34 +51,34 @@
|
|
|
51
51
|
"@pnpm/catalogs.types": "1100.0.0",
|
|
52
52
|
"@pnpm/cli.common-cli-options-help": "1100.0.1",
|
|
53
53
|
"@pnpm/cli.utils": "1101.0.2",
|
|
54
|
+
"@pnpm/config.reader": "1101.2.1",
|
|
54
55
|
"@pnpm/config.pick-registry-for-package": "1100.0.2",
|
|
55
|
-
"@pnpm/config.reader": "1101.2.0",
|
|
56
56
|
"@pnpm/constants": "1100.0.0",
|
|
57
57
|
"@pnpm/deps.path": "1100.0.2",
|
|
58
|
-
"@pnpm/engine.runtime.commands": "1100.0.
|
|
59
|
-
"@pnpm/engine.runtime.node-resolver": "1101.0.
|
|
58
|
+
"@pnpm/engine.runtime.commands": "1100.0.11",
|
|
59
|
+
"@pnpm/engine.runtime.node-resolver": "1101.0.5",
|
|
60
60
|
"@pnpm/error": "1100.0.0",
|
|
61
|
-
"@pnpm/exec.lifecycle": "1100.0.
|
|
61
|
+
"@pnpm/exec.lifecycle": "1100.0.6",
|
|
62
62
|
"@pnpm/exec.pnpm-cli-runner": "1100.0.0",
|
|
63
|
-
"@pnpm/fetching.directory-fetcher": "1100.0.
|
|
64
|
-
"@pnpm/fs.indexed-pkg-importer": "1100.0.
|
|
63
|
+
"@pnpm/fetching.directory-fetcher": "1100.0.6",
|
|
64
|
+
"@pnpm/fs.indexed-pkg-importer": "1100.0.5",
|
|
65
65
|
"@pnpm/fs.is-empty-dir-or-nothing": "1100.0.0",
|
|
66
66
|
"@pnpm/fs.packlist": "1100.0.0",
|
|
67
|
-
"@pnpm/installing.client": "1100.0.
|
|
68
|
-
"@pnpm/installing.commands": "1100.1.
|
|
69
|
-
"@pnpm/lockfile.
|
|
67
|
+
"@pnpm/installing.client": "1100.0.11",
|
|
68
|
+
"@pnpm/installing.commands": "1100.1.11",
|
|
69
|
+
"@pnpm/lockfile.fs": "1100.0.6",
|
|
70
|
+
"@pnpm/lockfile.types": "1100.0.4",
|
|
70
71
|
"@pnpm/network.fetch": "1100.0.2",
|
|
71
|
-
"@pnpm/lockfile.fs": "1100.0.4",
|
|
72
72
|
"@pnpm/network.git-utils": "1100.0.1",
|
|
73
73
|
"@pnpm/network.web-auth": "1101.0.0",
|
|
74
74
|
"@pnpm/releasing.exportable-manifest": "1100.0.3",
|
|
75
|
+
"@pnpm/resolving.resolver-base": "1100.1.2",
|
|
75
76
|
"@pnpm/types": "1101.0.0",
|
|
76
|
-
"@pnpm/
|
|
77
|
-
"@pnpm/workspace.projects-sorter": "1100.0.1"
|
|
78
|
-
"@pnpm/workspace.projects-filter": "1100.0.7"
|
|
77
|
+
"@pnpm/workspace.projects-filter": "1100.0.8",
|
|
78
|
+
"@pnpm/workspace.projects-sorter": "1100.0.1"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@pnpm/logger": "
|
|
81
|
+
"@pnpm/logger": "^1001.0.1"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@jest/globals": "30.3.0",
|
|
@@ -92,21 +92,22 @@
|
|
|
92
92
|
"@types/tar": "^7.0.87",
|
|
93
93
|
"@types/tar-stream": "^3.1.4",
|
|
94
94
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
95
|
-
"ci-info": "^4.
|
|
95
|
+
"ci-info": "^4.4.0",
|
|
96
96
|
"cross-spawn": "^7.0.6",
|
|
97
97
|
"is-windows": "^1.0.2",
|
|
98
98
|
"load-json-file": "^7.0.1",
|
|
99
|
-
"tar": "^7.5.
|
|
99
|
+
"tar": "^7.5.13",
|
|
100
|
+
"undici": "^7.25.0",
|
|
100
101
|
"write-yaml-file": "^6.0.0",
|
|
102
|
+
"@pnpm/assert-project": "1100.0.5",
|
|
101
103
|
"@pnpm/catalogs.config": "1100.0.0",
|
|
102
|
-
"@pnpm/
|
|
104
|
+
"@pnpm/hooks.pnpmfile": "1100.0.6",
|
|
103
105
|
"@pnpm/logger": "1100.0.0",
|
|
104
|
-
"@pnpm/prepare": "1100.0.
|
|
105
|
-
"@pnpm/releasing.commands": "1100.2.
|
|
106
|
-
"@pnpm/hooks.pnpmfile": "1100.0.5",
|
|
106
|
+
"@pnpm/prepare": "1100.0.5",
|
|
107
|
+
"@pnpm/releasing.commands": "1100.2.10",
|
|
107
108
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
108
|
-
"@pnpm/
|
|
109
|
-
"@pnpm/
|
|
109
|
+
"@pnpm/test-ipc-server": "1100.0.0",
|
|
110
|
+
"@pnpm/testing.command-defaults": "1100.0.1"
|
|
110
111
|
},
|
|
111
112
|
"engines": {
|
|
112
113
|
"node": ">=22.13"
|