Files
react-native/scripts/publish-npm.js
T
Héctor Ramos e4b5d3eec9 Circle CI: Upload both tarballs to releases, dry-run the release workflow on every commit (#35015)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/35015

React Native releases are cut every few months. Without testing, the workflow is prone to breakage.

Dry-run the release workflow on every commit in order to surface any issues as they are introduced instead of at release time.

Fixed issues that surfaced during testing of this workflow:
- Upload both Hermes tarballs

Changelog: [internal]

Reviewed By: mdvacca

Differential Revision: D40483764

fbshipit-source-id: 5ca6bd4dcdfd64c24882ffb202edbfd701efd462
2022-10-19 17:54:22 -07:00

231 lines
6.6 KiB
JavaScript
Executable File

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
/**
* This script prepares a release version of react-native and may publish to NPM.
* It is supposed to run in CI environment, not on a developer's machine.
*
* For a dry run (commitly), this script will:
* * Version the commitly of the form `1000.0.0-<commitSha>`
* * Create Android artifacts
* * It will not publish to npm
*
* For a nightly run, this script will:
* * Version the nightly release of the form `0.0.0-<dateIdentifier>-<commitSha>`
* * Create Android artifacts
* * Publish to npm using `nightly` tag
*
* For a release run, this script will:
* * Version the release by the tag version that triggered CI
* * Create Android artifacts
* * Publish to npm
* * using `latest` tag if commit is currently tagged `latest`
* * or otherwise `{major}.{minor}-stable`
*/
const {exec, echo, exit} = require('shelljs');
const {parseVersion} = require('./version-utils');
const {
exitIfNotOnGit,
getCurrentCommit,
isTaggedLatest,
} = require('./scm-utils');
const {
generateAndroidArtifacts,
publishAndroidArtifactsToMaven,
saveFilesToRestore,
} = require('./release-utils');
const fs = require('fs');
const os = require('os');
const path = require('path');
const yargs = require('yargs');
const buildTag = process.env.CIRCLE_TAG;
const otp = process.env.NPM_CONFIG_OTP;
const tmpPublishingFolder = fs.mkdtempSync(
path.join(os.tmpdir(), 'rn-publish-'),
);
echo(`The temp publishing folder is ${tmpPublishingFolder}`);
const argv = yargs
.option('n', {
alias: 'nightly',
type: 'boolean',
default: false,
})
.option('d', {
alias: 'dry-run',
type: 'boolean',
default: false,
})
.option('h', {
alias: 'include-hermes',
type: 'boolean',
default: false,
}).argv;
const nightlyBuild = argv.nightly;
const dryRunBuild = argv.dryRun;
const includeHermes = argv.includeHermes;
const isCommitly = nightlyBuild || dryRunBuild;
saveFilesToRestore(tmpPublishingFolder);
if (includeHermes) {
const HERMES_INSTALL_LOCATION = 'sdks';
const HERMES_SOURCE_DEST_PATH = `${HERMES_INSTALL_LOCATION}/hermes`;
let hermesReleaseTag;
let hermesReleaseURI;
if (isCommitly) {
// use latest commit / tarball
hermesReleaseURI = 'https://github.com/facebook/hermes/tarball/main';
} else {
// use one configured in disk
fs.readFile(
`${HERMES_INSTALL_LOCATION}/.hermesversion`,
{
encoding: 'utf8',
flag: 'r',
},
function (err, data) {
if (err) {
echo('Failed to read current Hermes release tag.');
// TODO: We'll need to make sure every release going forward has one of these.
exit(1);
} else {
hermesReleaseTag = data.trim();
hermesReleaseURI = `https://github.com/facebook/hermes/archive/refs/tags/${hermesReleaseTag}.tar.gz`;
}
},
);
}
const tmpDownloadDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'hermes-tarball'),
);
const tmpExtractDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes'));
const hermesInstallScript = `
mkdir -p ${HERMES_SOURCE_DEST_PATH} && \
wget ${hermesReleaseURI} -O ${tmpDownloadDir}/hermes.tar.gz && \
tar -xzf ${tmpDownloadDir}/hermes.tar.gz -C ${tmpExtractDir} && \
HERMES_SOURCE_EXTRACT_PATH=$(ls -d ${tmpExtractDir}/*) && \
mv $HERMES_SOURCE_EXTRACT_PATH ${HERMES_SOURCE_DEST_PATH}
`;
if (fs.existsSync(`${HERMES_SOURCE_DEST_PATH}`)) {
if (exec(`rm -rf ./${HERMES_SOURCE_DEST_PATH}`).code) {
echo('Failed to clean up previous Hermes installation.');
exit(1);
}
}
if (exec(hermesInstallScript).code) {
echo('Failed to include Hermes in release.');
exit(1);
}
}
// 34c034298dc9cad5a4553964a5a324450fda0385
const currentCommit = getCurrentCommit();
const shortCommit = currentCommit.slice(0, 9);
const rawVersion =
// 0.0.0 triggers issues with cocoapods for codegen when building template project.
dryRunBuild
? '1000.0.0'
: // For nightly we continue to use 0.0.0 for clarity for npm
nightlyBuild
? '0.0.0'
: // For pre-release and stable releases, we use the git tag of the version we're releasing (set in bump-oss-version)
buildTag;
let version,
major,
minor,
prerelease = null;
try {
({version, major, minor, prerelease} = parseVersion(rawVersion));
} catch (e) {
echo(e.message);
exit(1);
}
let releaseVersion;
if (dryRunBuild) {
releaseVersion = `${version}-${shortCommit}`;
} else if (nightlyBuild) {
// 2021-09-28T05:38:40.669Z -> 20210928-0538
const dateIdentifier = new Date()
.toISOString()
.slice(0, -8)
.replace(/[-:]/g, '')
.replace(/[T]/g, '-');
releaseVersion = `${version}-${dateIdentifier}-${shortCommit}`;
} else {
releaseVersion = version;
}
// Bump version number in various files (package.json, gradle.properties etc)
// For stable, pre-release releases, we rely on CircleCI job `prepare_package_for_release` to handle this
if (isCommitly) {
if (
exec(`node scripts/set-rn-version.js --to-version ${releaseVersion}`).code
) {
echo(`Failed to set version number to ${releaseVersion}`);
exit(1);
}
}
generateAndroidArtifacts(releaseVersion, tmpPublishingFolder);
// Write version number to the build folder
const releaseVersionFile = path.join('build', '.version');
fs.writeFileSync(releaseVersionFile, releaseVersion);
if (dryRunBuild) {
echo('Skipping `npm publish` because --dry-run is set.');
exit(0);
}
// Running to see if this commit has been git tagged as `latest`
const isLatest = exitIfNotOnGit(
() => isTaggedLatest(currentCommit),
'Not in git. We do not want to publish anything',
);
// We first publish on Maven Central all the necessary artifacts.
// NPM publishing is done just after.
publishAndroidArtifactsToMaven(nightlyBuild);
const releaseBranch = `${major}.${minor}-stable`;
// Set the right tag for nightly and prerelease builds
// If a release is not git-tagged as `latest` we use `releaseBranch` to prevent
// npm from overriding the current `latest` version tag, which it will do if no tag is set.
const tagFlag = nightlyBuild
? '--tag nightly'
: prerelease != null
? '--tag next'
: isLatest
? '--tag latest'
: `--tag ${releaseBranch}`;
// use otp from envvars if available
const otpFlag = otp ? `--otp ${otp}` : '';
if (exec(`npm publish ${tagFlag} ${otpFlag}`).code) {
echo('Failed to publish package to npm');
exit(1);
} else {
echo(`Published to npm ${releaseVersion}`);
exit(0);
}