1/** 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 * 7 * @format 8 */ 9 10'use strict'; 11 12/** 13 * This script prepares a release version of react-native and may publish to NPM. 14 * It is supposed to run in CI environment, not on a developer's machine. 15 * 16 * For a dry run (commitly), this script will: 17 * * Version the commitly of the form `1000.0.0-<commitSha>` 18 * * Create Android artifacts 19 * * It will not publish to npm 20 * 21 * For a nightly run, this script will: 22 * * Version the nightly release of the form `0.0.0-<dateIdentifier>-<commitSha>` 23 * * Create Android artifacts 24 * * Publish to npm using `nightly` tag 25 * 26 * For a release run, this script will: 27 * * Version the release by the tag version that triggered CI 28 * * Create Android artifacts 29 * * Publish to npm 30 * * using `latest` tag if commit is currently tagged `latest` 31 * * or otherwise `{major}.{minor}-stable` 32 */ 33 34const {exec, echo, exit, test, env} = require('shelljs'); 35const {parseVersion, isTaggedLatest} = require('./version-utils'); 36const fs = require('fs'); 37const os = require('os'); 38const path = require('path'); 39const yargs = require('yargs'); 40 41const buildTag = process.env.CIRCLE_TAG; 42const otp = process.env.NPM_CONFIG_OTP; 43 44const argv = yargs 45 .option('n', { 46 alias: 'nightly', 47 type: 'boolean', 48 default: false, 49 }) 50 .option('d', { 51 alias: 'dry-run', 52 type: 'boolean', 53 default: false, 54 }) 55 .option('h', { 56 alias: 'include-hermes', 57 type: 'boolean', 58 default: false, 59 }).argv; 60const nightlyBuild = argv.nightly; 61const dryRunBuild = argv.dryRun; 62const includeHermes = argv.includeHermes; 63const isCommitly = nightlyBuild || dryRunBuild; 64 65if (includeHermes) { 66 const HERMES_INSTALL_LOCATION = 'sdks'; 67 const HERMES_SOURCE_DEST_PATH = `${HERMES_INSTALL_LOCATION}/hermes`; 68 69 let hermesReleaseTag; 70 let hermesReleaseURI; 71 if (isCommitly) { 72 // use latest commit / tarball 73 hermesReleaseURI = 'https://github.com/facebook/hermes/tarball/main'; 74 } else { 75 // use one configured in disk 76 fs.readFile( 77 `${HERMES_INSTALL_LOCATION}/.hermesversion`, 78 { 79 encoding: 'utf8', 80 flag: 'r', 81 }, 82 function (err, data) { 83 if (err) { 84 echo('Failed to read current Hermes release tag.'); 85 // TODO: We'll need to make sure every release going forward has one of these. 86 exit(1); 87 } else { 88 hermesReleaseTag = data.trim(); 89 hermesReleaseURI = `https://github.com/facebook/hermes/archive/refs/tags/${hermesReleaseTag}.tar.gz`; 90 } 91 }, 92 ); 93 } 94 95 const tmpDownloadDir = fs.mkdtempSync( 96 path.join(os.tmpdir(), 'hermes-tarball'), 97 ); 98 const tmpExtractDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes')); 99 100 const hermesInstallScript = ` 101 mkdir -p ${HERMES_SOURCE_DEST_PATH} && \ 102 wget ${hermesReleaseURI} -O ${tmpDownloadDir}/hermes.tar.gz && \ 103 tar -xzf ${tmpDownloadDir}/hermes.tar.gz -C ${tmpExtractDir} && \ 104 HERMES_SOURCE_EXTRACT_PATH=$(ls -d ${tmpExtractDir}/*) && \ 105 mv $HERMES_SOURCE_EXTRACT_PATH ${HERMES_SOURCE_DEST_PATH} 106 `; 107 108 if (fs.existsSync(`${HERMES_SOURCE_DEST_PATH}`)) { 109 if (exec(`rm -rf ./${HERMES_SOURCE_DEST_PATH}`).code) { 110 echo('Failed to clean up previous Hermes installation.'); 111 exit(1); 112 } 113 } 114 if (exec(hermesInstallScript).code) { 115 echo('Failed to include Hermes in release.'); 116 exit(1); 117 } 118} 119 120// 34c034298dc9cad5a4553964a5a324450fda0385 121const currentCommit = exec('git rev-parse HEAD', { 122 silent: true, 123}).stdout.trim(); 124const shortCommit = currentCommit.slice(0, 9); 125 126const rawVersion = 127 // 0.0.0 triggers issues with cocoapods for codegen when building template project. 128 dryRunBuild 129 ? '1000.0.0' 130 : // For nightly we continue to use 0.0.0 for clarity for npm 131 nightlyBuild 132 ? '0.0.0' 133 : // For pre-release and stable releases, we use the git tag of the version we're releasing (set in bump-oss-version) 134 buildTag; 135 136let version, 137 major, 138 minor, 139 prerelease = null; 140try { 141 ({version, major, minor, prerelease} = parseVersion(rawVersion)); 142} catch (e) { 143 echo(e.message); 144 exit(1); 145} 146 147let releaseVersion; 148if (dryRunBuild) { 149 releaseVersion = `${version}-${shortCommit}`; 150} else if (nightlyBuild) { 151 // 2021-09-28T05:38:40.669Z -> 20210928-0538 152 const dateIdentifier = new Date() 153 .toISOString() 154 .slice(0, -8) 155 .replace(/[-:]/g, '') 156 .replace(/[T]/g, '-'); 157 releaseVersion = `${version}-${dateIdentifier}-${shortCommit}`; 158} else { 159 releaseVersion = version; 160} 161 162// Bump version number in various files (package.json, gradle.properties etc) 163// For stable, pre-release releases, we rely on CircleCI job `prepare_package_for_release` to handle this 164if (isCommitly) { 165 if ( 166 exec(`node scripts/set-rn-version.js --to-version ${releaseVersion}`).code 167 ) { 168 echo(`Failed to set version number to ${releaseVersion}`); 169 exit(1); 170 } 171} 172 173// -------- Generating Android Artifacts 174if (exec('./gradlew :ReactAndroid:installArchives').code) { 175 echo('Could not generate artifacts'); 176 exit(1); 177} 178 179// -------- Generating the Hermes Engine Artifacts 180env.REACT_NATIVE_HERMES_SKIP_PREFAB = true; 181if (exec('./gradlew :ReactAndroid:hermes-engine:installArchives').code) { 182 echo('Could not generate artifacts'); 183 exit(1); 184} 185 186// undo uncommenting javadoc setting 187exec('git checkout ReactAndroid/gradle.properties'); 188 189echo('Generated artifacts for Maven'); 190 191let artifacts = [ 192 '.module', 193 '.pom', 194 '-debug.aar', 195 '-release.aar', 196 '-debug-sources.jar', 197 '-release-sources.jar', 198].map(suffix => { 199 return `react-native-${releaseVersion}${suffix}`; 200}); 201 202artifacts.forEach(name => { 203 if ( 204 !test( 205 '-e', 206 `./android/com/facebook/react/react-native/${releaseVersion}/${name}`, 207 ) 208 ) { 209 echo(`Failing as expected file: ${name} was not correctly generated.`); 210 exit(1); 211 } 212}); 213 214if (dryRunBuild) { 215 echo('Skipping `npm publish` because --dry-run is set.'); 216 exit(0); 217} 218 219// Running to see if this commit has been git tagged as `latest` 220const isLatest = isTaggedLatest(currentCommit); 221 222const releaseBranch = `${major}.${minor}-stable`; 223 224// Set the right tag for nightly and prerelease builds 225// If a release is not git-tagged as `latest` we use `releaseBranch` to prevent 226// npm from overriding the current `latest` version tag, which it will do if no tag is set. 227const tagFlag = nightlyBuild 228 ? '--tag nightly' 229 : prerelease != null 230 ? '--tag next' 231 : isLatest 232 ? '--tag latest' 233 : `--tag ${releaseBranch}`; 234 235// use otp from envvars if available 236const otpFlag = otp ? `--otp ${otp}` : ''; 237 238if (exec(`npm publish ${tagFlag} ${otpFlag}`).code) { 239 echo('Failed to publish package to npm'); 240 exit(1); 241} else { 242 echo(`Published to npm ${releaseVersion}`); 243 exit(0); 244} 245