react/compiler/scripts/release/publish.js
Lauren Tan fbe81b214a
[compiler] Publish to latest tag
> [!NOTE]
> The `latest` tag is published by default if no tag is specified, which
> is what we had done since the first release of the compiler

In my last PR to auto publish compiler releases I had added the
experimental tag to be used in publishing. However because we had
already previously published to the latest tag (which is non-removable)
this means that the `latest` tag is pinned to an old version. That makes
untagged installs of the compiler default to that old version instead of
whatever is the latest.

This changes the behavior back to what it was before. Since we are still
in the experimental release of the compiler anyway it seems fine to use
the latest tag. When we reach stable, we can update this to only push to
latest for stable releases.

ghstack-source-id: 1809481b45
Pull Request resolved: https://github.com/facebook/react/pull/30666
2024-08-12 14:41:11 -04:00

213 lines
6.7 KiB
JavaScript
Executable File

#!/usr/bin/env node
'use strict';
const ora = require('ora');
const path = require('path');
const yargs = require('yargs');
const {hashElement} = require('folder-hash');
const promptForOTP = require('./prompt-for-otp');
const {PUBLISHABLE_PACKAGES} = require('./shared/packages');
const {
execHelper,
getDateStringForCommit,
spawnHelper,
} = require('./shared/utils');
const {buildPackages} = require('./shared/build-packages');
const {readJson, writeJson} = require('fs-extra');
/**
* Script for publishing PUBLISHABLE_PACKAGES to npm. By default, this runs in tarball mode, meaning
* the script will only print out what the contents of the files included in the npm tarball would
* be.
*
* Please run this first (ie `yarn npm:publish`) and double check the contents of the files that
* will be pushed to npm.
*
* If it looks good, you can run `yarn npm:publish --for-real` to really publish to npm. You must
* have 2FA enabled first and the script will prompt you to enter a 2FA code before proceeding.
* There's a small annoying delay before the packages are actually pushed to give you time to panic
* cancel. In this mode, we will bump the version field of each package's package.json, and git
* commit it. Then, the packages will be published to npm.
*
* Optionally, you can add the `--debug` flag to `yarn npm:publish --debug --for-real` to run all
* steps, but the final npm publish step will have the `--dry-run` flag added to it. This will make
* the command only report what it would have done, instead of actually publishing to npm.
*/
async function main() {
const argv = yargs(process.argv.slice(2))
.option('packages', {
description: 'which packages to publish, defaults to all',
choices: PUBLISHABLE_PACKAGES,
default: PUBLISHABLE_PACKAGES,
})
.option('for-real', {
alias: 'frfr',
description:
'whether to publish to npm (npm publish) or dryrun (npm publish --dry-run)',
type: 'boolean',
default: false,
})
.option('debug', {
description:
'If enabled, will always run npm commands in dry run mode irregardless of the for-real flag',
type: 'boolean',
default: false,
})
.option('ci', {
description: 'Publish packages via CI',
type: 'boolean',
default: false,
})
.option('tags', {
description: 'Tags to publish to npm',
type: 'string',
default: 'experimental',
})
.help('help')
.parseSync();
if (argv.debug === false) {
const currBranchName = await execHelper('git rev-parse --abbrev-ref HEAD');
const isPristine = (await execHelper('git status --porcelain')) === '';
if (currBranchName !== 'main' || isPristine === false) {
throw new Error(
'This script must be run from the `main` branch with no uncommitted changes'
);
}
}
let pkgNames = argv.packages;
if (Array.isArray(argv.packages) === false) {
pkgNames = [argv.packages];
}
const spinner = ora(
`Preparing to publish ${
argv.forReal === true ? '(for real)' : '(dry run)'
} [debug=${argv.debug}]`
).info();
await buildPackages(pkgNames);
if (argv.forReal === false) {
spinner.info('Dry run: Report tarball contents');
for (const pkgName of pkgNames) {
console.log(`\n========== ${pkgName} ==========\n`);
spinner.start(`Running npm pack --dry-run\n`);
try {
await spawnHelper('npm', ['pack', '--dry-run'], {
cwd: path.resolve(__dirname, `../../packages/${pkgName}`),
stdio: 'inherit',
});
} catch (e) {
spinner.fail(e.toString());
throw e;
}
spinner.stop(`Successfully packed ${pkgName} (dry run)`);
}
spinner.succeed(
'Please confirm contents of packages before publishing. You can run this command again with --for-real to publish to npm'
);
}
if (argv.forReal === true) {
const commit = await execHelper(
'git show -s --no-show-signature --format=%h',
{
cwd: path.resolve(__dirname, '..'),
}
);
const dateString = await getDateStringForCommit(commit);
const otp = argv.ci === false ? await promptForOTP() : null;
for (const pkgName of pkgNames) {
const pkgDir = path.resolve(__dirname, `../../packages/${pkgName}`);
const pkgJsonPath = path.resolve(
__dirname,
`../../packages/${pkgName}/package.json`
);
const {hash} = await hashElement(pkgDir, {
encoding: 'hex',
folders: {exclude: ['node_modules']},
files: {exclude: ['.DS_Store']},
});
const truncatedHash = hash.slice(0, 7);
const newVersion = `0.0.0-experimental-${truncatedHash}-${dateString}`;
spinner.start(`Writing package.json for ${pkgName}@${newVersion}`);
await writeJson(
pkgJsonPath,
{
...(await readJson(pkgJsonPath)),
version: newVersion,
},
{spaces: 2}
);
spinner.succeed(`Wrote package.json for ${pkgName}@${newVersion}`);
console.log(`\n========== ${pkgName} ==========\n`);
spinner.start(`Publishing ${pkgName}@${newVersion} to npm\n`);
let opts = [];
if (argv.debug === true) {
opts.push('--dry-run');
}
if (otp != null) {
opts.push(`--otp=${otp}`);
}
try {
await spawnHelper(
'npm',
[
'publish',
...opts,
'--registry=https://registry.npmjs.org',
// For now, since the compiler is experimental only, to simplify installation we push
// to the `latest` tag
'--tag=latest',
],
{
cwd: pkgDir,
stdio: 'inherit',
}
);
console.log('\n');
} catch (e) {
spinner.fail(e.toString());
throw e;
}
spinner.succeed(`Successfully published ${pkgName} to npm`);
spinner.start('Pushing tags to npm');
if (typeof argv.tags === 'string') {
for (const tag of argv.tags.split(',')) {
try {
let opts = ['dist-tag', 'add', `${pkgName}@${newVersion}`, tag];
if (otp != null) {
opts.push(`--otp=${otp}`);
}
if (argv.debug === true) {
spinner.info(`dry-run: npm ${opts.join(' ')}`);
} else {
await spawnHelper('npm', opts, {
cwd: pkgDir,
stdio: 'inherit',
});
}
} catch (e) {
spinner.fail(e.toString());
throw e;
}
spinner.succeed(
`Successfully pushed dist-tag ${tag} for ${pkgName} to npm`
);
}
}
}
console.log('\n\n✅ All done');
}
}
main();