@mui/internal-code-infra 0.0.4-canary.96 → 0.0.4-canary.97
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 +3 -0
- package/build/utils/pnpm.d.mts +28 -5
- package/package.json +3 -3
- package/src/cli/cmdPublish.mjs +16 -10
- package/src/cli/cmdPublishNewPackage.mjs +4 -2
- package/src/utils/pnpm.mjs +91 -17
package/README.md
CHANGED
|
@@ -37,6 +37,9 @@ This is stored in the `docs` top-level directory.
|
|
|
37
37
|
|
|
38
38
|
Whenever new packages are added to the repo (that will get published to npm) or a private package is turned into a public one, follow the below steps before invoking the publish workflow of the previous section.
|
|
39
39
|
|
|
40
|
+
> [!NOTE]
|
|
41
|
+
> This applies to packages published to npm, which is where Trusted Publishing needs the package to already exist. Packages that set `publishConfig.registry` to another registry are skipped by this check and need none of these steps.
|
|
42
|
+
|
|
40
43
|
1. Go to your repo's code base on your system, then log in to npm using
|
|
41
44
|
|
|
42
45
|
```bash
|
package/build/utils/pnpm.d.mts
CHANGED
|
@@ -29,22 +29,45 @@ export type PnpmListResultItem = {
|
|
|
29
29
|
export type GetWorkspacePackagesOptions = {
|
|
30
30
|
sinceRef?: string | null;
|
|
31
31
|
publicOnly?: boolean;
|
|
32
|
-
nonPublishedOnly?: boolean;
|
|
33
32
|
cwd?: string;
|
|
34
33
|
filter?: string[];
|
|
35
34
|
};
|
|
36
35
|
export declare function getWorkspacePackages(options?: {
|
|
37
36
|
publicOnly: true;
|
|
38
37
|
} & GetWorkspacePackagesOptions): Promise<PublicPackage[]>;
|
|
39
|
-
export declare function getWorkspacePackages(options?: {
|
|
40
|
-
nonPublishedOnly: true;
|
|
41
|
-
} & GetWorkspacePackagesOptions): Promise<PublicPackage[]>;
|
|
42
38
|
export declare function getWorkspacePackages(options?: {
|
|
43
39
|
publicOnly?: false | undefined;
|
|
44
40
|
} & GetWorkspacePackagesOptions): Promise<PrivatePackage[]>;
|
|
45
41
|
export declare function getWorkspacePackages(options?: GetWorkspacePackagesOptions): Promise<(PrivatePackage | PublicPackage)[]>;
|
|
46
42
|
/**
|
|
47
|
-
*
|
|
43
|
+
* Resolve the registry a package will be published to.
|
|
44
|
+
*
|
|
45
|
+
* Only `publishConfig.registry` and the ambient `npm_config_registry` are
|
|
46
|
+
* consulted — `.npmrc` layering and `@scope:registry` entries are not.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} packagePath - Path to the package directory
|
|
49
|
+
* @returns {Promise<string>} Normalized registry URL, ending in a slash
|
|
50
|
+
*/
|
|
51
|
+
export declare function getPublishRegistry(packagePath: string): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Filter to the packages that must be published by hand before CI can take over.
|
|
54
|
+
*
|
|
55
|
+
* A brand new package has to be pushed once manually (see
|
|
56
|
+
* `code-infra publish-new-package`) before the OIDC-based workflow can publish
|
|
57
|
+
* it, so the release fails with a clear message instead of a confusing 404
|
|
58
|
+
* midway through. See {@link getPackageVersionInfo} for the version-level check.
|
|
59
|
+
*
|
|
60
|
+
* @param {PublicPackage[]} packages - Packages to check
|
|
61
|
+
* @returns {Promise<PublicPackage[]>} The subset needing a manual first publish
|
|
62
|
+
*/
|
|
63
|
+
export declare function getPackagesNeedingManualPublish(packages: PublicPackage[]): Promise<PublicPackage[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Get package version info from registry.
|
|
66
|
+
*
|
|
67
|
+
* Resolves through `pnpm view`, which reports a lookup failure the same way it
|
|
68
|
+
* reports a missing version. Use {@link getPackagesNeedingManualPublish} where
|
|
69
|
+
* absence has to be told apart from an error.
|
|
70
|
+
*
|
|
48
71
|
* @param {string} packageName - Name of the package
|
|
49
72
|
* @param {string} baseVersion - Base version to check
|
|
50
73
|
* @returns {Promise<VersionInfo>} Version information
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/internal-code-infra",
|
|
3
|
-
"version": "0.0.4-canary.
|
|
3
|
+
"version": "0.0.4-canary.97",
|
|
4
4
|
"author": "MUI Team",
|
|
5
5
|
"description": "Infra scripts and configs to be used across MUI repos.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -135,8 +135,8 @@
|
|
|
135
135
|
"unist-util-visit": "^5.1.0",
|
|
136
136
|
"yaml": "^2.9.0",
|
|
137
137
|
"yargs": "^18.0.0",
|
|
138
|
-
"@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.30",
|
|
139
138
|
"@mui/internal-babel-plugin-display-name": "1.0.4-canary.23",
|
|
139
|
+
"@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.30",
|
|
140
140
|
"@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.40"
|
|
141
141
|
},
|
|
142
142
|
"peerDependencies": {
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
"publishConfig": {
|
|
189
189
|
"access": "public"
|
|
190
190
|
},
|
|
191
|
-
"gitSha": "
|
|
191
|
+
"gitSha": "f5098e75e68dc8dcfc3c4535b1a0a3fb97c6c68d",
|
|
192
192
|
"scripts": {
|
|
193
193
|
"build": "tsgo -p tsconfig.build.json",
|
|
194
194
|
"typescript": "tsgo -noEmit",
|
package/src/cli/cmdPublish.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import * as semver from 'semver';
|
|
|
19
19
|
|
|
20
20
|
import { persistentAuthStrategy } from '../utils/github.mjs';
|
|
21
21
|
import {
|
|
22
|
+
getPackagesNeedingManualPublish,
|
|
22
23
|
getWorkspacePackages,
|
|
23
24
|
publishPackages,
|
|
24
25
|
validatePublishDependencies,
|
|
@@ -327,9 +328,9 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
327
328
|
// Get all packages
|
|
328
329
|
console.log('🔍 Discovering all workspace packages...');
|
|
329
330
|
|
|
330
|
-
const
|
|
331
|
+
const filteredPackages = await getWorkspacePackages({ publicOnly: true, filter });
|
|
331
332
|
|
|
332
|
-
if (
|
|
333
|
+
if (filteredPackages.length === 0) {
|
|
333
334
|
console.log(
|
|
334
335
|
`⚠️ No publishable packages found in workspace${filter.length > 0 ? ` matching filter "${filter.join(', ')}"` : ''}`,
|
|
335
336
|
);
|
|
@@ -339,7 +340,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
339
340
|
if (filter.length > 0) {
|
|
340
341
|
console.log('🔍 Validating workspace dependencies for filtered packages...');
|
|
341
342
|
|
|
342
|
-
const { issues } = await validatePublishDependencies(
|
|
343
|
+
const { issues } = await validatePublishDependencies(filteredPackages);
|
|
343
344
|
|
|
344
345
|
if (issues.length > 0) {
|
|
345
346
|
throw new Error(
|
|
@@ -369,20 +370,23 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
369
370
|
githubReleaseData = await validateGitHubRelease(version);
|
|
370
371
|
}
|
|
371
372
|
|
|
372
|
-
const newPackages = await
|
|
373
|
+
const newPackages = await getPackagesNeedingManualPublish(filteredPackages);
|
|
373
374
|
|
|
374
375
|
if (newPackages.length > 0) {
|
|
376
|
+
const newPackageNames = newPackages.map((pkg) => pkg.name).join(', ');
|
|
375
377
|
throw new Error(
|
|
376
|
-
`The following packages are new and need to be published manually first: ${
|
|
377
|
-
', ',
|
|
378
|
-
)}. Read more about it here: https://github.com/mui/mui-public/blob/master/packages/code-infra/README.md#adding-and-publishing-new-packages`,
|
|
378
|
+
`The following packages are new and need to be published to npm manually first: ${newPackageNames}. Read more about it here: https://github.com/mui/mui-public/blob/master/packages/code-infra/README.md#adding-and-publishing-new-packages`,
|
|
379
379
|
);
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
// Publish to npm (pnpm handles duplicate checking automatically)
|
|
383
383
|
// No git checks, we'll do our own
|
|
384
384
|
console.log('\n📦 Publishing packages to npm...');
|
|
385
|
-
const publishedPackages = await publishToNpm(
|
|
385
|
+
const publishedPackages = await publishToNpm(filteredPackages, {
|
|
386
|
+
dryRun,
|
|
387
|
+
noGitChecks: true,
|
|
388
|
+
tag,
|
|
389
|
+
});
|
|
386
390
|
|
|
387
391
|
if (publishedPackages.length === 0) {
|
|
388
392
|
console.log('ℹ️ No packages were published (all may already be up to date on npm)');
|
|
@@ -428,11 +432,13 @@ const PUBLISH_WORKFLOW_ID = `.github/${WORKFLOW_PATH}`;
|
|
|
428
432
|
*/
|
|
429
433
|
async function triggerLocalGithubPublishWorkflow(opts) {
|
|
430
434
|
console.log(`🔍 Checking if there are new packages to publish in the workspace...`);
|
|
431
|
-
const newPackages = await
|
|
435
|
+
const newPackages = await getPackagesNeedingManualPublish(
|
|
436
|
+
await getWorkspacePackages({ publicOnly: true }),
|
|
437
|
+
);
|
|
432
438
|
if (newPackages.length) {
|
|
433
439
|
console.warn(
|
|
434
440
|
`⚠️ Found new packages that should be published to npm first before triggering a release:
|
|
435
|
-
|
|
441
|
+
${newPackages.map((pkg) => ` * ${pkg.name}`).join('\n')}
|
|
436
442
|
Please run the command "${chalk.bold('pnpm code-infra publish-new-package')}" first to publish and configure npm.`,
|
|
437
443
|
);
|
|
438
444
|
return;
|
|
@@ -11,7 +11,7 @@ import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
|
|
|
11
11
|
|
|
12
12
|
import { getRepositoryInfo } from '../utils/git.mjs';
|
|
13
13
|
import { toPosixPath } from '../utils/path.mjs';
|
|
14
|
-
import { getWorkspacePackages } from '../utils/pnpm.mjs';
|
|
14
|
+
import { getPackagesNeedingManualPublish, getWorkspacePackages } from '../utils/pnpm.mjs';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @typedef {Object} Args
|
|
@@ -46,7 +46,9 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
46
46
|
}),
|
|
47
47
|
async handler(args) {
|
|
48
48
|
console.log(`🔍 Detecting new packages to publish in workspace...`);
|
|
49
|
-
const newPackages = await
|
|
49
|
+
const newPackages = await getPackagesNeedingManualPublish(
|
|
50
|
+
await getWorkspacePackages({ publicOnly: true }),
|
|
51
|
+
);
|
|
50
52
|
|
|
51
53
|
if (!newPackages.length) {
|
|
52
54
|
console.log('No new packages to publish.');
|
package/src/utils/pnpm.mjs
CHANGED
|
@@ -7,6 +7,9 @@ import * as path from 'node:path';
|
|
|
7
7
|
import * as semver from 'semver';
|
|
8
8
|
import { parseDocument, isMap } from 'yaml';
|
|
9
9
|
|
|
10
|
+
/** Canonical npm registry URL, in the form `getPublishRegistry` returns. */
|
|
11
|
+
const NPMJS_REGISTRY = 'https://registry.npmjs.org/';
|
|
12
|
+
|
|
10
13
|
/**
|
|
11
14
|
* @typedef {Object} PrivatePackage
|
|
12
15
|
* @property {string} [name] - Package name
|
|
@@ -48,7 +51,6 @@ import { parseDocument, isMap } from 'yaml';
|
|
|
48
51
|
* @typedef {Object} GetWorkspacePackagesOptions
|
|
49
52
|
* @property {string|null} [sinceRef] - Git reference to filter changes since
|
|
50
53
|
* @property {boolean} [publicOnly=false] - Whether to filter to only public packages
|
|
51
|
-
* @property {boolean} [nonPublishedOnly=false] - Whether to filter to only non-published packages. It by default means public packages yet to be published.
|
|
52
54
|
* @property {string} [cwd] - Current working directory to run pnpm command in
|
|
53
55
|
* @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
|
|
54
56
|
*/
|
|
@@ -61,10 +63,6 @@ import { parseDocument, isMap } from 'yaml';
|
|
|
61
63
|
* @returns {Promise<PublicPackage[]>} Array of packages
|
|
62
64
|
*
|
|
63
65
|
* @overload
|
|
64
|
-
* @param {{ nonPublishedOnly: true } & GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
|
|
65
|
-
* @returns {Promise<PublicPackage[]>} Array of packages
|
|
66
|
-
*
|
|
67
|
-
* @overload
|
|
68
66
|
* @param {{ publicOnly?: false | undefined } & GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
|
|
69
67
|
* @returns {Promise<PrivatePackage[]>} Array of packages
|
|
70
68
|
*
|
|
@@ -76,7 +74,7 @@ import { parseDocument, isMap } from 'yaml';
|
|
|
76
74
|
* @returns {Promise<(PrivatePackage | PublicPackage)[]>} Array of packages
|
|
77
75
|
*/
|
|
78
76
|
export async function getWorkspacePackages(options = {}) {
|
|
79
|
-
const { sinceRef = null, publicOnly = false,
|
|
77
|
+
const { sinceRef = null, publicOnly = false, filter = [] } = options;
|
|
80
78
|
|
|
81
79
|
/**
|
|
82
80
|
* Run `pnpm ls` with the given --filter args and return the parsed list.
|
|
@@ -125,24 +123,100 @@ export async function getWorkspacePackages(options = {}) {
|
|
|
125
123
|
];
|
|
126
124
|
});
|
|
127
125
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const filteredPublicPackages = filteredPackages.filter((pkg) => !pkg.isPrivate);
|
|
126
|
+
return filteredPackages;
|
|
127
|
+
}
|
|
131
128
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Resolve the registry a package will be published to.
|
|
131
|
+
*
|
|
132
|
+
* Only `publishConfig.registry` and the ambient `npm_config_registry` are
|
|
133
|
+
* consulted — `.npmrc` layering and `@scope:registry` entries are not.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} packagePath - Path to the package directory
|
|
136
|
+
* @returns {Promise<string>} Normalized registry URL, ending in a slash
|
|
137
|
+
*/
|
|
138
|
+
export async function getPublishRegistry(packagePath) {
|
|
139
|
+
const packageJson = await readPackageJson(packagePath);
|
|
140
|
+
const registry =
|
|
141
|
+
packageJson.publishConfig?.registry || process.env.npm_config_registry || NPMJS_REGISTRY;
|
|
142
|
+
|
|
143
|
+
// Normalizing through the URL parser keeps host casing and default ports from
|
|
144
|
+
// defeating the equality check in `requiresTrustedPublisherBootstrap`. The
|
|
145
|
+
// trailing slash is required because `new URL(name, base)` replaces the last
|
|
146
|
+
// path segment of a base that lacks one, mangling registries served under a
|
|
147
|
+
// path prefix such as Artifactory's `/api/npm/<repo>`.
|
|
148
|
+
let registryUrl;
|
|
149
|
+
try {
|
|
150
|
+
registryUrl = new URL(registry);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
// Node's own message names neither the offending value nor where it came
|
|
153
|
+
// from, leaving the operator to bisect package.json files mid-release.
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Invalid publish registry ${JSON.stringify(registry)} for the package at ${packagePath}`,
|
|
156
|
+
{ cause: error },
|
|
137
157
|
);
|
|
138
|
-
return filteredPublicPackages.filter((_pkg, index) => !!results[index]);
|
|
139
158
|
}
|
|
159
|
+
registryUrl.pathname = `${registryUrl.pathname.replace(/\/+$/, '')}/`;
|
|
160
|
+
return registryUrl.href;
|
|
161
|
+
}
|
|
140
162
|
|
|
141
|
-
|
|
163
|
+
/**
|
|
164
|
+
* Whether a registry requires a package to exist before CI can publish to it.
|
|
165
|
+
* @param {string} registry - Normalized registry URL
|
|
166
|
+
* @returns {boolean}
|
|
167
|
+
*/
|
|
168
|
+
function requiresTrustedPublisherBootstrap(registry) {
|
|
169
|
+
// npm won't attach a Trusted Publisher to a name that doesn't exist yet. No
|
|
170
|
+
// other registry we publish to has an equivalent step.
|
|
171
|
+
return registry === NPMJS_REGISTRY;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Filter to the packages that must be published by hand before CI can take over.
|
|
176
|
+
*
|
|
177
|
+
* A brand new package has to be pushed once manually (see
|
|
178
|
+
* `code-infra publish-new-package`) before the OIDC-based workflow can publish
|
|
179
|
+
* it, so the release fails with a clear message instead of a confusing 404
|
|
180
|
+
* midway through. See {@link getPackageVersionInfo} for the version-level check.
|
|
181
|
+
*
|
|
182
|
+
* @param {PublicPackage[]} packages - Packages to check
|
|
183
|
+
* @returns {Promise<PublicPackage[]>} The subset needing a manual first publish
|
|
184
|
+
*/
|
|
185
|
+
export async function getPackagesNeedingManualPublish(packages) {
|
|
186
|
+
const results = await Promise.all(
|
|
187
|
+
packages.map(async (pkg) => {
|
|
188
|
+
const registry = await getPublishRegistry(pkg.path);
|
|
189
|
+
if (!requiresTrustedPublisherBootstrap(registry)) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// HEAD, because only the status matters — a packument runs to megabytes.
|
|
194
|
+
const res = await fetch(new URL(pkg.name, registry), { method: 'HEAD' });
|
|
195
|
+
if (res.status === 404) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if (!res.ok) {
|
|
199
|
+
// Anything else (401, 5xx, a proxy hiccup) tells us nothing about
|
|
200
|
+
// whether the package exists. Treating it as "already published" would
|
|
201
|
+
// silently disable the check, so fail loudly instead.
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Failed to check whether ${pkg.name} exists on ${registry}: HTTP ${res.status}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return packages.filter((_pkg, index) => results[index]);
|
|
142
211
|
}
|
|
143
212
|
|
|
144
213
|
/**
|
|
145
|
-
* Get package version info from registry
|
|
214
|
+
* Get package version info from registry.
|
|
215
|
+
*
|
|
216
|
+
* Resolves through `pnpm view`, which reports a lookup failure the same way it
|
|
217
|
+
* reports a missing version. Use {@link getPackagesNeedingManualPublish} where
|
|
218
|
+
* absence has to be told apart from an error.
|
|
219
|
+
*
|
|
146
220
|
* @param {string} packageName - Name of the package
|
|
147
221
|
* @param {string} baseVersion - Base version to check
|
|
148
222
|
* @returns {Promise<VersionInfo>} Version information
|