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
…38666) (#38890)

Summary:
One of the limitations of the existing flow for the release crew is that they need to manually remember to publish all the other packages in the monorepo ahead of a new patch release - this PR modifies the logic for the bump-oss-version script (and makes it available via yarn) so that it will not run if:
* there are git changes lying around
* if some of the packages need a new release

it required a bit of refactoring to extract some portions of the logic from the bump-all-package-versions script, but I think the end result is pretty decent.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[INTERNAL] [CHANGED] - improve bump oss script to allow less human errors

Pull Request resolved: #38666

Test Plan:
* checkout this branch
* comment L54 of bump-oss-version.js (to remove the check on the branch name)
* run `yarn bump-all-updated-packages`, verify that it works and that it detects that some packages have unreleased code
* run `yarn bump-oss-version -t asd -v asd` (the "fake" parameters are needed to pass the yargs check), verify that it will throw an error because it finds a package that has unreleased code

Reviewed By: mdvacca

Differential Revision: D48156963

Pulled By: cortinico

fbshipit-source-id: 2473ad5a84578c5236c905fd9aa9a88113fe8d22

# Conflicts:
#	scripts/publish-npm.js

re-add the file

nit

# Conflicts:
#	package.json
  • Loading branch information
kelset authored Aug 16, 2023
1 parent f8c03d1 commit 209e743
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 @@ -103,7 +103,8 @@
"test-typescript": "dtslint types",
"test-typescript-offline": "dtslint --localTs node_modules/typescript/lib types",
"bump-all-updated-packages": "node ./scripts/monorepo/bump-all-updated-packages",
"align-package-versions": "node ./scripts/monorepo/align-package-versions.js"
"align-package-versions": "node ./scripts/monorepo/align-package-versions.js",
"trigger-react-native-release": "node ./scripts/trigger-react-native-release.js"
},
"peerDependencies": {
"react": "18.2.0"
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 @@ -24,6 +24,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 @@ -61,35 +62,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 @@ -99,7 +99,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 209e743

Please sign in to comment.