@mui/internal-code-infra 0.0.3-canary.21 → 0.0.3-canary.23

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 CHANGED
@@ -1,3 +1,50 @@
1
1
  # @mui/internal-code-infra
2
2
 
3
3
  Scripts and configs to be used across MUI repos.
4
+
5
+ ## Publishing packages
6
+
7
+ 1. Go to the publish action -
8
+
9
+ - [Base UI](https://github.com/mui/base-ui/actions/workflows/publish.yml)
10
+ - [Core](https://github.com/mui/material-ui/actions/workflows/publish.yml)
11
+ - [MUI X](https://github.com/mui/mui-x/actions/workflows/publish.yml)
12
+
13
+ 2. Choose "Run workflow" dropdown
14
+
15
+ > - **Branch:** master
16
+ > - **Commit SHA to release from:** the commit that contains the merged release on master. This commit is linked to the GitHub release.
17
+ > - **Run in dry-run mode:** Used for debugging.
18
+ > - **Create GitHub release:** Keep selected if you want a GitHub release to be automatically created from the changelog.
19
+ > - **npm dist tag to publish to** Use to publish legacy or canary versions.
20
+
21
+ 3. Click "Run workflow"
22
+ 4. Refresh the page to see the newly created workflow, and click it.
23
+ 5. The next screen shows "@username requested your review to deploy to npm-publish", click "Review deployments" and authorize your workflow run. **Never approve workflow runs you didn't initiaite.**
24
+
25
+ > [!IMPORTANT]
26
+ > Go through the below steps if there is an error that says `The following packages are new and need to be published manually first` in the publish flow.
27
+
28
+ ### Adding and publishing new packages
29
+
30
+ Whenever news 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.
31
+
32
+ 1. Goto your repo's code base on your system, open terminal and run:
33
+
34
+ ```bash
35
+ pnpm code-infra publish-new-package
36
+ ```
37
+
38
+ This command detects the new public packages in the repo and asks for your confirmation before publishing them to the npm registry. Add the `--dryRun` flag to skip the actual publishing.
39
+
40
+ 2. Goto the settings link for each packages, ie, https://www.npmjs.com/package/<pkg-name>/access , and setup `Trusted Publisher`.
41
+ 3. In `Select your publisher` step in the above link, click on the `Github Actions` button to configure Github actions based trusted publishing.
42
+ 4. Fill in the details of the repo -
43
+ 1. `Organization or user` as `mui`,
44
+ 2. `Repository` as per the new package
45
+ 3. `Workflow filename*` should be `publish.yml`
46
+ 4. `Environment name` should be `npm-publish`
47
+ 5. In the `Publishing access` section, toggle the recommended option of `Require two-factor authentication and disallow tokens`.
48
+ 6. Finally, save the changes by clicking on `Update Package Settings` button.
49
+
50
+ After following these steps, the `Publish` workflow can be invoked again.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-code-infra",
3
- "version": "0.0.3-canary.21",
3
+ "version": "0.0.3-canary.23",
4
4
  "description": "Infra scripts and configs to be used across MUI repos.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,6 +33,7 @@
33
33
  "@babel/preset-typescript": "^7.27.1",
34
34
  "@eslint/compat": "^1.4.0",
35
35
  "@eslint/js": "^9.36.0",
36
+ "@inquirer/confirm": "^5.1.18",
36
37
  "@next/eslint-plugin-next": "^15.5.4",
37
38
  "@octokit/auth-action": "^6.0.1",
38
39
  "@octokit/rest": "^22.0.0",
@@ -64,8 +65,8 @@
64
65
  "stylelint-config-standard": "^39.0.0",
65
66
  "typescript-eslint": "^8.45.0",
66
67
  "yargs": "^18.0.0",
67
- "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.23",
68
68
  "@mui/internal-babel-plugin-display-name": "1.0.4-canary.7",
69
+ "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.23",
69
70
  "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.10"
70
71
  },
71
72
  "peerDependencies": {
@@ -97,7 +98,7 @@
97
98
  "publishConfig": {
98
99
  "access": "public"
99
100
  },
100
- "gitSha": "e79fecf38c0782906060995f47503918a10c19c5",
101
+ "gitSha": "d9d08c1e6632b905ea3cd3bc91b33493efa7ccc1",
101
102
  "scripts": {
102
103
  "typescript": "tsc -p tsconfig.json",
103
104
  "test": "pnpm -w test --project @mui/internal-code-infra",
@@ -63,8 +63,11 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
63
63
  const screenshots = await globby(`${folder}/**/*`, {
64
64
  onlyFiles: true,
65
65
  });
66
+ const threshold = process.env.ARGOS_THRESHOLD ? parseFloat(process.env.ARGOS_THRESHOLD) : 0.5;
66
67
 
67
- console.log(`Found ${screenshots.length} screenshots.`);
68
+ console.log(
69
+ `Found ${screenshots.length} screenshots. Uploading with threshold ${threshold}.`,
70
+ );
68
71
  if (verbose) {
69
72
  console.log('Screenshots found:');
70
73
  screenshots.forEach((screenshot) => {
@@ -99,14 +102,22 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
99
102
  commit: circleSha1,
100
103
  branch: circleBranch,
101
104
  token: argosToken,
105
+ threshold,
102
106
  parallel: {
103
107
  total: batches.length,
104
108
  nonce: circleBuildNum,
105
109
  },
106
110
  });
107
111
 
112
+ if (verbose) {
113
+ console.log('Screenshots uploaded:');
114
+ for (const screenshot of result.screenshots) {
115
+ console.log(`- ${screenshot.name}. Threshold: ${screenshot.threshold}.`);
116
+ }
117
+ }
118
+
108
119
  console.log(
109
- `Batch of ${batches[i].length} screenshots uploaded. Build URL: ${result.build.url}`,
120
+ `Batch of ${batches[i].length} screenshots uploaded. Threshold: ${threshold}. Build URL: ${result.build.url}`,
110
121
  );
111
122
  }
112
123
  } finally {
@@ -307,6 +307,16 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
307
307
  githubReleaseData = await validateGitHubRelease(version);
308
308
  }
309
309
 
310
+ const newPackages = await getWorkspacePackages({ nonPublishedOnly: true });
311
+
312
+ if (newPackages.length > 0) {
313
+ throw new Error(
314
+ `The following packages are new and need to be published manually first: ${newPackages.join(
315
+ ', ',
316
+ )}. Read more about it here: https://github.com/mui/mui-public/blob/master/packages/code-infra/README.md#adding-and-publishing-new-packages`,
317
+ );
318
+ }
319
+
310
320
  // Publish to npm (pnpm handles duplicate checking automatically)
311
321
  // No git checks, we'll do our own
312
322
  await publishToNpm(allPackages, { dryRun, noGitChecks: true, tag });
@@ -0,0 +1,86 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import confirm from '@inquirer/confirm';
4
+ import chalk from 'chalk';
5
+ import { $ } from 'execa';
6
+ import fs from 'node:fs/promises';
7
+ import os from 'node:os';
8
+ import path from 'node:path';
9
+
10
+ import { getWorkspacePackages } from './pnpm.mjs';
11
+
12
+ /**
13
+ * @typedef {Object} Args
14
+ * @property {boolean} [dryRun] If true, will only log the commands without executing them
15
+ */
16
+
17
+ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
18
+ command: 'publish-new-package [pkg...]',
19
+ describe: 'Publish new empty package(s) to the npm registry.',
20
+ builder: (yargs) =>
21
+ yargs.option('dryRun', {
22
+ type: 'boolean',
23
+ default: false,
24
+ description: 'If true, will only log the commands without executing them.',
25
+ }),
26
+ async handler(args) {
27
+ console.log(`🔍 Detecting new packages to publish in workspace...`);
28
+ const newPackages = await getWorkspacePackages({ nonPublishedOnly: true });
29
+
30
+ if (!newPackages.length) {
31
+ console.log('No new packages to publish.');
32
+ return;
33
+ }
34
+
35
+ console.log(`Found ${newPackages.map((pkg) => pkg.name).join(', ')} to publish.`);
36
+
37
+ const answer = await confirm({
38
+ message: `Do you want to publish ${newPackages.length} new package(s) to the npm registry?`,
39
+ });
40
+
41
+ if (!answer) {
42
+ return;
43
+ }
44
+
45
+ await Promise.all(
46
+ newPackages.map(async (pkg) => {
47
+ const newPkgDir = await fs.mkdtemp(path.join(os.tmpdir(), 'publish-new-package-'));
48
+ try {
49
+ await fs.mkdir(newPkgDir, { recursive: true });
50
+ const packageJson = {
51
+ name: pkg.name,
52
+ version: '0.0.1',
53
+ };
54
+ await fs.writeFile(
55
+ path.join(newPkgDir, 'package.json'),
56
+ `${JSON.stringify(packageJson, null, 2)}\n`,
57
+ );
58
+ /**
59
+ * @type {string[]}
60
+ */
61
+ const publishArgs = [];
62
+
63
+ if (args.dryRun) {
64
+ publishArgs.push('--dry-run');
65
+ }
66
+ await $({
67
+ cwd: newPkgDir,
68
+ })`npm publish --access public --tag=canary ${publishArgs}`;
69
+ console.log(
70
+ `✅ ${args.dryRun ? '[Dry run] ' : ''}Published ${chalk.bold(`${pkg.name}@${packageJson.version}`)} to npm registry.`,
71
+ );
72
+ } finally {
73
+ await fs.rm(newPkgDir, { recursive: true, force: true });
74
+ }
75
+ }),
76
+ );
77
+
78
+ const trustedPublisherLinks = newPackages
79
+ .map((pkg) => `https://www.npmjs.com/package/${pkg.name}/access`)
80
+ .join('\n');
81
+ console.log(`
82
+ 📝 Please ensure that the ${chalk.underline(chalk.bold('Trusted Publishers'))} settings are configured for the new packages:
83
+ ${trustedPublisherLinks}
84
+ Read how to do that here - https://github.com/mui/mui-public/blob/master/packages/code-infra/README.md#adding-and-publishing-new-packages`);
85
+ },
86
+ });
package/src/cli/index.mjs CHANGED
@@ -10,6 +10,7 @@ import cmdJsonLint from './cmdJsonLint.mjs';
10
10
  import cmdListWorkspaces from './cmdListWorkspaces.mjs';
11
11
  import cmdPublish from './cmdPublish.mjs';
12
12
  import cmdPublishCanary from './cmdPublishCanary.mjs';
13
+ import cmdPublishNewPackage from './cmdPublishNewPackage.mjs';
13
14
  import cmdSetVersionOverrides from './cmdSetVersionOverrides.mjs';
14
15
 
15
16
  const pkgJson = createRequire(import.meta.url)('../../package.json');
@@ -25,6 +26,7 @@ yargs()
25
26
  .command(cmdListWorkspaces)
26
27
  .command(cmdPublish)
27
28
  .command(cmdPublishCanary)
29
+ .command(cmdPublishNewPackage)
28
30
  .command(cmdSetVersionOverrides)
29
31
  .demandCommand(1, 'You need at least one command before moving on')
30
32
  .strict()
package/src/cli/pnpm.mjs CHANGED
@@ -46,6 +46,7 @@ import * as semver from 'semver';
46
46
  * @typedef {Object} GetWorkspacePackagesOptions
47
47
  * @property {string|null} [sinceRef] - Git reference to filter changes since
48
48
  * @property {boolean} [publicOnly=false] - Whether to filter to only public packages
49
+ * @property {boolean} [nonPublishedOnly=false] - Whether to filter to only non-published packages. It by default means public packages yet to be published.
49
50
  */
50
51
 
51
52
  /**
@@ -56,6 +57,10 @@ import * as semver from 'semver';
56
57
  * @returns {Promise<PublicPackage[]>} Array of packages
57
58
  *
58
59
  * @overload
60
+ * @param {{ nonPublishedOnly: true } & GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
61
+ * @returns {Promise<PublicPackage[]>} Array of packages
62
+ *
63
+ * @overload
59
64
  * @param {{ publicOnly?: false | undefined } & GetWorkspacePackagesOptions} [options={}] - Options for filtering packages
60
65
  * @returns {Promise<PrivatePackage[]>} Array of packages
61
66
  *
@@ -67,7 +72,7 @@ import * as semver from 'semver';
67
72
  * @returns {Promise<(PrivatePackage | PublicPackage)[]>} Array of packages
68
73
  */
69
74
  export async function getWorkspacePackages(options = {}) {
70
- const { sinceRef = null, publicOnly = false } = options;
75
+ const { sinceRef = null, publicOnly = false, nonPublishedOnly = false } = options;
71
76
 
72
77
  // Build command with conditional filter
73
78
  const filterArg = sinceRef ? ['--filter', `...[${sinceRef}]`] : [];
@@ -91,6 +96,19 @@ export async function getWorkspacePackages(options = {}) {
91
96
  ];
92
97
  });
93
98
 
99
+ if (nonPublishedOnly) {
100
+ // Check if any of the packages are new/need manual publishing first.
101
+ const filteredPublicPackages = filteredPackages.filter((pkg) => !pkg.isPrivate);
102
+
103
+ const results = await Promise.all(
104
+ filteredPublicPackages.map(async (pkg) => {
105
+ const url = `${process.env.npm_config_registry || 'https://registry.npmjs.org'}/${pkg.name}`;
106
+ return fetch(url).then((res) => res.status === 404);
107
+ }),
108
+ );
109
+ return filteredPublicPackages.filter((_pkg, index) => !!results[index]);
110
+ }
111
+
94
112
  return filteredPackages;
95
113
  }
96
114