Skip to content

Commit

Permalink
chore(releases): improve bump oss script to allow less human errors (…
Browse files Browse the repository at this point in the history
…72 edition) (facebook#38888)

Co-authored-by: William Bell <williambell9708@outlook.com>
resolved: facebook#38247
resolved: facebook#38666
  • Loading branch information
kelset and billnbell authored Aug 18, 2023
1 parent 33fc55d commit ef8ac7a
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 31 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"test-ios": "./scripts/objc-test.sh test",
"test-typescript": "dtslint packages/react-native/types",
"test-typescript-offline": "dtslint --localTs node_modules/typescript/lib packages/react-native/types",
"bump-all-updated-packages": "node ./scripts/monorepo/bump-all-updated-packages"
"bump-all-updated-packages": "node ./scripts/monorepo/bump-all-updated-packages",
"trigger-react-native-release": "node ./scripts/trigger-react-native-release.js"
},
"workspaces": [
"packages/*"
Expand Down
49 changes: 49 additions & 0 deletions scripts/monorepo/bump-all-updated-packages/bump-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* 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
*/

const chalk = require('chalk');
const {echo, exec} = require('shelljs');

const detectPackageUnreleasedChanges = (
packageRelativePathFromRoot,
packageName,
ROOT_LOCATION,
) => {
const hashOfLastCommitInsidePackage = exec(
`git log -n 1 --format=format:%H -- ${packageRelativePathFromRoot}`,
{cwd: ROOT_LOCATION, silent: true},
).stdout.trim();

const hashOfLastCommitThatChangedVersion = exec(
`git log -G\\"version\\": --format=format:%H -n 1 -- ${packageRelativePathFromRoot}/package.json`,
{cwd: ROOT_LOCATION, silent: true},
).stdout.trim();

if (hashOfLastCommitInsidePackage === hashOfLastCommitThatChangedVersion) {
echo(
`\uD83D\uDD0E No changes for package ${chalk.green(
packageName,
)} since last version bump`,
);
return false;
} else {
echo(`\uD83D\uDCA1 Found changes for ${chalk.yellow(packageName)}:`);
exec(
`git log --pretty=oneline ${hashOfLastCommitThatChangedVersion}..${hashOfLastCommitInsidePackage} ${packageRelativePathFromRoot}`,
{
cwd: ROOT_LOCATION,
},
);
echo();

return true;
}
};

module.exports = detectPackageUnreleasedChanges;
34 changes: 8 additions & 26 deletions scripts/monorepo/bump-all-updated-packages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const {
const forEachPackage = require('../for-each-package');
const checkForGitChanges = require('../check-for-git-changes');
const bumpPackageVersion = require('./bump-package-version');
const detectPackageUnreleasedChanges = require('./bump-utils');

const ROOT_LOCATION = path.join(__dirname, '..', '..', '..');

Expand Down Expand Up @@ -62,35 +63,16 @@ const buildExecutor =
return;
}

const hashOfLastCommitInsidePackage = exec(
`git log -n 1 --format=format:%H -- ${packageRelativePathFromRoot}`,
{cwd: ROOT_LOCATION, silent: true},
).stdout.trim();

const hashOfLastCommitThatChangedVersion = exec(
`git log -G\\"version\\": --format=format:%H -n 1 -- ${packageRelativePathFromRoot}/package.json`,
{cwd: ROOT_LOCATION, silent: true},
).stdout.trim();

if (hashOfLastCommitInsidePackage === hashOfLastCommitThatChangedVersion) {
echo(
`\uD83D\uDD0E No changes for package ${chalk.green(
packageName,
)} since last version bump`,
);

if (
!detectPackageUnreleasedChanges(
packageRelativePathFromRoot,
packageName,
ROOT_LOCATION,
)
) {
return;
}

echo(`\uD83D\uDCA1 Found changes for ${chalk.yellow(packageName)}:`);
exec(
`git log --pretty=oneline ${hashOfLastCommitThatChangedVersion}..${hashOfLastCommitInsidePackage} ${packageRelativePathFromRoot}`,
{
cwd: ROOT_LOCATION,
},
);
echo();

await inquirer
.prompt([
{
Expand Down
2 changes: 1 addition & 1 deletion scripts/publish-npm.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const rawVersion =
: // 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)
: // For pre-release and stable releases, we use the git tag of the version we're releasing (set in trigger-react-native-release)
buildTag;

let version,
Expand Down
77 changes: 74 additions & 3 deletions scripts/bump-oss-version.js → scripts/trigger-react-native-release.js
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@
* This script walks a releaser through bumping the version for a release
* It will commit the appropriate tags to trigger the CircleCI jobs.
*/
const {exit} = require('shelljs');
const {exit, echo} = require('shelljs');
const chalk = require('chalk');
const yargs = require('yargs');
const inquirer = require('inquirer');
const request = require('request');
const path = require('path');
const {getBranchName, exitIfNotOnGit} = require('./scm-utils');

const {parseVersion, isReleaseBranch} = require('./version-utils');
const {failIfTagExists} = require('./release-utils');
const checkForGitChanges = require('./monorepo/check-for-git-changes');
const forEachPackage = require('./monorepo/for-each-package');
const detectPackageUnreleasedChanges = require('./monorepo/bump-all-updated-packages/bump-utils.js');

const ROOT_LOCATION = path.join(__dirname, '..');

let argv = yargs
.option('r', {
Expand All @@ -42,7 +49,7 @@ let argv = yargs
.check(() => {
const branch = exitIfNotOnGit(
() => getBranchName(),
"Not in git. You can't invoke bump-oss-versions.js from outside a git repo.",
"Not in git. You can't invoke trigger-react-native-release from outside a git repo.",
);
exitIfNotOnReleaseBranch(branch);
return true;
Expand All @@ -57,6 +64,52 @@ function exitIfNotOnReleaseBranch(branch) {
}
}

const buildExecutor =
(packageAbsolutePath, packageRelativePathFromRoot, packageManifest) =>
async () => {
const {name: packageName} = packageManifest;
if (packageManifest.private) {
return;
}

if (
detectPackageUnreleasedChanges(
packageRelativePathFromRoot,
packageName,
ROOT_LOCATION,
)
) {
// if I enter here, I want to throw an error upward
throw new Error(
`Package ${packageName} has unreleased changes. Please release it first.`,
);
}
};

const buildAllExecutors = () => {
const executors = [];

forEachPackage((...params) => {
executors.push(buildExecutor(...params));
});

return executors;
};

async function exitIfUnreleasedPackages() {
// use the other script to verify that there's no packages in the monorepo
// that have changes that haven't been released

const executors = buildAllExecutors();
for (const executor of executors) {
await executor().catch(error => {
echo(chalk.red(error));
// need to throw upward
throw error;
});
}
}

function triggerReleaseWorkflow(options) {
return new Promise((resolve, reject) => {
request(options, function (error, response, body) {
Expand All @@ -72,8 +125,26 @@ function triggerReleaseWorkflow(options) {
async function main() {
const branch = exitIfNotOnGit(
() => getBranchName(),
"Not in git. You can't invoke bump-oss-versions.js from outside a git repo.",
"Not in git. You can't invoke trigger-react-native-release from outside a git repo.",
);

// check for uncommitted changes
if (checkForGitChanges()) {
echo(
chalk.red(
'Found uncommitted changes. Please commit or stash them before running this script',
),
);
exit(1);
}

// now check for unreleased packages
try {
await exitIfUnreleasedPackages();
} catch (error) {
exit(1);
}

const token = argv.token;
const releaseVersion = argv.toVersion;
failIfTagExists(releaseVersion, 'release');
Expand Down

0 comments on commit ef8ac7a

Please sign in to comment.