Skip to content

Commit

Permalink
Update bump-oss-version.js to guide releaser through release actions (f…
Browse files Browse the repository at this point in the history
…acebook#32769)

Summary:
Pull Request resolved: facebook#32769

Changelog: [Internal] Re-purpose bump-oss-version to guide releaser to correctly tag the release and trigger relevant CircleCI jobs

Reviewed By: sota000

Differential Revision: D33121691

fbshipit-source-id: e3df99305fc2a2970a8e020fa4ee0075201eb440
  • Loading branch information
Luna Wei authored and facebook-github-bot committed Dec 16, 2021
1 parent e17f2e8 commit c098c0b
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 208 deletions.
1 change: 1 addition & 0 deletions repo-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"eslint-plugin-react-native": "^3.11.0",
"eslint-plugin-relay": "^1.8.2",
"flow-bin": "^0.167.1",
"inquirer": "^7.1.0",
"jest": "^26.6.3",
"jest-junit": "^10.0.0",
"jscodeshift": "^0.11.0",
Expand Down
298 changes: 95 additions & 203 deletions scripts/bump-oss-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,239 +11,131 @@
'use strict';

/**
* This script bumps a new version for open source releases.
* It updates the version in json/gradle files and makes sure they are consistent between each other
* After changing the files it makes a commit and tags it.
* All you have to do is push changes to remote and CI will make a new build.
* This script walks a releaser through bumping the version for a release
* It will commit the appropriate tags to trigger the CircleCI jobs.
*/
const fs = require('fs');
const {cat, echo, exec, exit, sed} = require('shelljs');
const {exec, exit} = require('shelljs');
const yargs = require('yargs');
const {parseVersion} = require('./version-utils');
const inquirer = require('inquirer');
const {
parseVersion,
isReleaseBranch,
getBranchName,
} = require('./version-utils');

let argv = yargs
.option('r', {
alias: 'remote',
default: 'origin',
})
.option('n', {
alias: 'nightly',
type: 'boolean',
default: false,
})
.option('v', {
alias: 'to-version',
type: 'string',
})
.option('l', {
alias: 'latest',
type: 'boolean',
default: false,
.check(() => {
const branch = getBranchName();
exitIfNotOnReleaseBranch(branch);
return true;
}).argv;

const nightlyBuild = argv.nightly;
const version = argv.toVersion;

if (!version) {
echo('You must specify a version using -v');
exit(1);
}

let branch;
if (!nightlyBuild) {
// Check we are in release branch, e.g. 0.33-stable
branch = exec('git symbolic-ref --short HEAD', {
silent: true,
}).stdout.trim();

if (branch.indexOf('-stable') === -1) {
echo('You must be in 0.XX-stable branch to bump a version');
exit(1);
}

// e.g. 0.33
let versionMajor = branch.slice(0, branch.indexOf('-stable'));

// - check that argument version matches branch
// e.g. 0.33.1 or 0.33.0-rc4
if (version.indexOf(versionMajor) !== 0) {
echo(
`You must specify a version tag like 0.${versionMajor}.[X]-rc[Y] to bump a version`,
function exitIfNotOnReleaseBranch(branch) {
if (!isReleaseBranch(branch)) {
console.log(
'This script must be run in a react-native git repository checkout and on a release branch',
);
exit(1);
}
}

let major,
minor,
patch,
prerelease = -1;
try {
({major, minor, patch, prerelease} = parseVersion(version));
} catch (e) {
echo(e.message);
exit(1);
function getLatestTag(versionPrefix) {
const tags = exec(`git tag --list "v${versionPrefix}*" --sort=-refname`, {
silent: true,
})
.stdout.trim()
.split('\n')
.filter(tag => tag.length > 0);
if (tags.length > 0) {
return tags[0];
}
return null;
}

fs.writeFileSync(
'ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java',
cat('scripts/versiontemplates/ReactNativeVersion.java.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace(
'${prerelease}',
prerelease !== undefined ? `"${prerelease}"` : 'null',
),
'utf-8',
);

fs.writeFileSync(
'React/Base/RCTVersion.m',
cat('scripts/versiontemplates/RCTVersion.m.template')
.replace('${major}', `@(${major})`)
.replace('${minor}', `@(${minor})`)
.replace('${patch}', `@(${patch})`)
.replace(
'${prerelease}',
prerelease !== undefined ? `@"${prerelease}"` : '[NSNull null]',
),
'utf-8',
);

fs.writeFileSync(
'ReactCommon/cxxreact/ReactNativeVersion.h',
cat('scripts/versiontemplates/ReactNativeVersion.h.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace(
'${prerelease}',
prerelease !== undefined ? `"${prerelease}"` : '""',
),
'utf-8',
);

fs.writeFileSync(
'Libraries/Core/ReactNativeVersion.js',
cat('scripts/versiontemplates/ReactNativeVersion.js.template')
.replace('${major}', major)
.replace('${minor}', minor)
.replace('${patch}', patch)
.replace(
'${prerelease}',
prerelease !== undefined ? `'${prerelease}'` : 'null',
),
'utf-8',
);

let packageJson = JSON.parse(cat('package.json'));
packageJson.version = version;
delete packageJson.workspaces;
delete packageJson.private;

// Copy repo-config/package.json dependencies as devDependencies
const repoConfigJson = JSON.parse(cat('repo-config/package.json'));
packageJson.devDependencies = {
...packageJson.devDependencies,
...repoConfigJson.dependencies,
};
// Make react-native-codegen a direct dependency of react-native
delete packageJson.devDependencies['react-native-codegen'];
packageJson.dependencies = {
...packageJson.dependencies,
'react-native-codegen': repoConfigJson.dependencies['react-native-codegen'],
};
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2), 'utf-8');

// Change ReactAndroid/gradle.properties
if (
sed(
'-i',
/^VERSION_NAME=.*/,
`VERSION_NAME=${version}`,
'ReactAndroid/gradle.properties',
).code
) {
echo("Couldn't update version for Gradle");
exit(1);
}
async function main() {
const branch = getBranchName();

// Change react-native version in the template's package.json
exec(`node scripts/set-rn-template-version.js ${version}`);

// Verify that files changed, we just do a git diff and check how many times version is added across files
const filesToValidate = [
'package.json',
'ReactAndroid/gradle.properties',
'template/package.json',
];
let numberOfChangedLinesWithNewVersion = exec(
`git diff -U0 ${filesToValidate.join(
' ',
)}| grep '^[+]' | grep -c ${version} `,
{silent: true},
).stdout.trim();

// Make sure to update ruby version
if (exec('scripts/update-ruby.sh').code) {
echo('Failed to update Ruby version');
exit(1);
}
const {pulled} = await inquirer.prompt({
type: 'confirm',
name: 'pulled',
message: `You are currently on branch: ${branch}. Have you run "git pull ${argv.remote} ${branch} --tags"?`,
});

// Release builds should commit the version bumps, and create tags.
// Nightly builds do not need to do that.
if (!nightlyBuild) {
if (+numberOfChangedLinesWithNewVersion !== filesToValidate.length) {
echo(
`Failed to update all the files: [${filesToValidate.join(
', ',
)}] must have versions in them`,
);
echo('Fix the issue, revert and try again');
exec('git diff');
if (!pulled) {
console.log(`Please run 'git pull ${argv.remote} ${branch} --tags'`);
exit(1);
return;
}

// Update Podfile.lock only on release builds, not nightlies.
// Nightly builds don't need it as the main branch will already be up-to-date.
echo('Updating RNTester Podfile.lock...');
if (exec('source scripts/update_podfile_lock.sh && update_pods').code) {
echo('Failed to update RNTester Podfile.lock.');
echo('Fix the issue, revert and try again.');
exit(1);
const lastVersionTag = getLatestTag(branch.replace('-stable', ''));
const lastVersion = lastVersionTag
? parseVersion(lastVersionTag).version
: null;
const lastVersionMessage = lastVersion
? `Last version tagged is ${lastVersion}.\n`
: '';

const {releaseVersion} = await inquirer.prompt({
type: 'input',
name: 'releaseVersion',
message: `What version are you releasing? (Ex. 0.66.0-rc.4)\n${lastVersionMessage}`,
});

let setLatest = false;

const {version, prerelease} = parseVersion(releaseVersion);
if (!prerelease) {
const {latest} = await inquirer.prompt({
type: 'confirm',
name: 'latest',
message: 'Set this version as "latest" on npm?',
});
setLatest = latest;
}

// Make commit [0.21.0-rc] Bump version numbers
if (exec(`git commit -a -m "[${version}] Bump version numbers"`).code) {
echo('failed to commit');
exit(1);
const npmTag = setLatest ? 'latest' : !prerelease ? branch : 'next';
const {confirmRelease} = await inquirer.prompt({
type: 'confirm',
name: 'confirmRelease',
message: `Releasing version "${version}" with npm tag "${npmTag}". Is this correct?`,
});

if (!confirmRelease) {
console.log('Aborting.');
return;
}

// Add tag v0.21.0-rc
if (exec(`git tag v${version}`).code) {
echo(
`failed to tag the commit with v${version}, are you sure this release wasn't made earlier?`,
);
echo('You may want to rollback the last commit');
echo('git reset --hard HEAD~1');
if (
exec(`git tag -a publish-v${version} -m "publish version ${version}"`).code
) {
console.error(`Failed to tag publish-v${version}`);
exit(1);
return;
}

// Push newly created tag
let remote = argv.remote;
exec(`git push ${remote} v${version}`);

// Tag latest if doing stable release.
// This will also tag npm release as `latest`
if (prerelease == null && argv.latest) {
if (setLatest) {
exec('git tag -d latest');
exec(`git push ${remote} :latest`);
exec('git tag latest');
exec(`git push ${remote} latest`);
exec(`git push ${argv.remote} :latest`);
exec('git tag -a latest -m "latest"');
}

if (exec(`git push ${argv.remote} ${branch} --follow-tags`).code) {
console.error(`Failed to push tag publish-v${version}`);
exit(1);
return;
}

exec(`git push ${remote} ${branch} --follow-tags`);
// TODO
// 1. Link to CircleCI job to watch
// 2. Output the release changelog to paste into Github releases
// 3. Link to release discussions to update
// 4. Verify RN-diff publish is through
// 5. General changelog update on PR?
}

exit(0);
main().then(() => {
exit(0);
});
4 changes: 3 additions & 1 deletion scripts/prepare-package-for-release.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ const {
isReleaseBranch,
isTaggedLatest,
getPublishVersion,
getBranchName,
} = require('./version-utils');

const branch = process.env.CIRCLE_BRANCH;
const currentCommit = process.env.CIRCLE_SHA1;
const tag = process.env.CIRCLE_TAG;

Expand All @@ -35,6 +35,8 @@ const argv = yargs.option('r', {
default: 'origin',
}).argv;

// This script is triggered from a tag so CIRCLE_BRANCH will not be set
const branch = getBranchName();
if (!isReleaseBranch(branch)) {
console.error('This needs to be on a release branch');
exit(1);
Expand Down
7 changes: 7 additions & 0 deletions scripts/version-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ function isReleaseBranch(branch) {
return branch.endsWith('-stable');
}

function getBranchName() {
return exec('git rev-parse --abbrev-ref HEAD', {
silent: true,
}).stdout.trim();
}

function getPublishVersion(tag) {
if (!tag.startsWith('publish-')) {
return null;
Expand All @@ -50,6 +56,7 @@ function isTaggedLatest(commitSha) {
}

module.exports = {
getBranchName,
isTaggedLatest,
getPublishVersion,
parseVersion,
Expand Down
Loading

0 comments on commit c098c0b

Please sign in to comment.