forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react-native-github): a script to automate patch version bumping…
… of packages (facebook#35767) Summary: Pull Request resolved: facebook#35767 Changelog: [Internal] Introducing a script, which can be used to identify all packages inside `/packages`, which contain any changes after the last time its version was changed How it works step by step: ``` check that no git changes are present for each package: if package is private -> skip grep id of the last commit that changed package grep id of the last commit that changed version of the package if these ids are different: bump package patch version for each package: check if this package is used and update its version run yarn if required commit changes if required ``` Can be executed only in git environment and by running: `node ./scripts/bump-all-updated-packages` Reviewed By: cortinico Differential Revision: D42295344 fbshipit-source-id: 326b1b28f20f21252154bf9f3ebf42d71de9112b
- Loading branch information
1 parent
66927ec
commit fca5d04
Showing
6 changed files
with
387 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* 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 path = require('path'); | ||
const {writeFileSync} = require('fs'); | ||
|
||
const bumpPackageVersion = require('../bump-all-updated-packages/bump-package-version'); | ||
|
||
jest.mock('fs', () => ({ | ||
writeFileSync: jest.fn(), | ||
readFileSync: jest.fn(() => '{}'), | ||
})); | ||
|
||
jest.mock('../for-each-package', () => callback => {}); | ||
|
||
describe('bumpPackageVersionTest', () => { | ||
it('updates patch version of the package', () => { | ||
const mockedPackageLocation = '~/packages/assets'; | ||
const mockedPackageManifest = { | ||
name: '@react-native/test', | ||
version: '1.2.3', | ||
}; | ||
|
||
bumpPackageVersion(mockedPackageLocation, mockedPackageManifest); | ||
|
||
expect(writeFileSync).toHaveBeenCalledWith( | ||
path.join(mockedPackageLocation, 'package.json'), | ||
JSON.stringify({...mockedPackageManifest, version: '1.2.4'}, null, 2) + | ||
'\r\n', | ||
'utf-8', | ||
); | ||
}); | ||
}); |
128 changes: 128 additions & 0 deletions
128
scripts/monorepo/bump-all-updated-packages/bump-package-version.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** | ||
* 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 {writeFileSync, readFileSync} = require('fs'); | ||
const path = require('path'); | ||
|
||
const forEachPackage = require('../for-each-package'); | ||
|
||
const ROOT_LOCATION = path.join(__dirname, '..', '..', '..'); | ||
const TEMPLATE_LOCATION = path.join(ROOT_LOCATION, 'template'); | ||
const REPO_CONFIG_LOCATION = path.join(ROOT_LOCATION, 'repo-config'); | ||
|
||
const readJSONFile = packageAbsolutePath => | ||
JSON.parse(readFileSync(packageAbsolutePath)); | ||
|
||
const getIncrementedVersion = (version, increment) => | ||
version | ||
.split('.') | ||
.map((token, index) => { | ||
const indexOfVersionToIncrement = increment === 'minor' ? 1 : 2; | ||
|
||
return index === indexOfVersionToIncrement | ||
? parseInt(token, 10) + 1 | ||
: token; | ||
}) | ||
.join('.'); | ||
|
||
const updatePackageVersionInPackageManifest = ( | ||
packageAbsolutePath, | ||
updatedPackageName, | ||
updatedPackageVersion, | ||
) => { | ||
const packageManifestPath = path.join(packageAbsolutePath, 'package.json'); | ||
const packageManifest = readJSONFile(packageManifestPath); | ||
|
||
const dependencyVersion = packageManifest.dependencies?.[updatedPackageName]; | ||
if (dependencyVersion && dependencyVersion !== '*') { | ||
const updatedDependencyVersion = dependencyVersion.startsWith('^') | ||
? `^${updatedPackageVersion}` | ||
: updatedPackageVersion; | ||
const updatedPackageManifest = { | ||
...packageManifest, | ||
dependencies: { | ||
...packageManifest.dependencies, | ||
[updatedPackageName]: updatedDependencyVersion, | ||
}, | ||
}; | ||
|
||
writeFileSync( | ||
packageManifestPath, | ||
JSON.stringify(updatedPackageManifest, null, 2) + '\r\n', | ||
'utf-8', | ||
); | ||
} | ||
|
||
const devDependencyVersion = | ||
packageManifest.devDependencies?.[updatedPackageName]; | ||
if (devDependencyVersion && devDependencyVersion !== '*') { | ||
const updatedDependencyVersion = devDependencyVersion.startsWith('^') | ||
? `^${updatedPackageVersion}` | ||
: updatedPackageVersion; | ||
const updatedPackageManifest = { | ||
...packageManifest, | ||
devDependencies: { | ||
...packageManifest.devDependencies, | ||
[updatedPackageName]: updatedDependencyVersion, | ||
}, | ||
}; | ||
|
||
writeFileSync( | ||
packageManifestPath, | ||
JSON.stringify(updatedPackageManifest, null, 2) + '\r\n', | ||
'utf-8', | ||
); | ||
} | ||
}; | ||
|
||
const bumpPackageVersion = ( | ||
packageAbsolutePath, | ||
packageManifest, | ||
increment = 'patch', | ||
) => { | ||
const updatedVersion = getIncrementedVersion( | ||
packageManifest.version, | ||
increment, | ||
); | ||
|
||
// Not using simple `npm version patch` because it updates dependencies and yarn.lock file | ||
writeFileSync( | ||
path.join(packageAbsolutePath, 'package.json'), | ||
JSON.stringify({...packageManifest, version: updatedVersion}, null, 2) + | ||
'\r\n', | ||
'utf-8', | ||
); | ||
|
||
updatePackageVersionInPackageManifest( | ||
ROOT_LOCATION, | ||
packageManifest.name, | ||
updatedVersion, | ||
); | ||
updatePackageVersionInPackageManifest( | ||
TEMPLATE_LOCATION, | ||
packageManifest.name, | ||
updatedVersion, | ||
); | ||
updatePackageVersionInPackageManifest( | ||
REPO_CONFIG_LOCATION, | ||
packageManifest.name, | ||
updatedVersion, | ||
); | ||
forEachPackage(pathToPackage => | ||
updatePackageVersionInPackageManifest( | ||
pathToPackage, | ||
packageManifest.name, | ||
updatedVersion, | ||
), | ||
); | ||
|
||
return updatedVersion; | ||
}; | ||
|
||
module.exports = bumpPackageVersion; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
/** | ||
* 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 inquirer = require('inquirer'); | ||
const path = require('path'); | ||
const {echo, exec, exit} = require('shelljs'); | ||
|
||
const {BUMP_COMMIT_MESSAGE} = require('../constants'); | ||
const forEachPackage = require('../for-each-package'); | ||
const bumpPackageVersion = require('./bump-package-version'); | ||
|
||
const ROOT_LOCATION = path.join(__dirname, '..', '..', '..'); | ||
|
||
const buildExecutor = | ||
(packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => | ||
async () => { | ||
const {name: packageName} = packageManifest; | ||
if (packageManifest.private) { | ||
echo(`\u23ED Skipping private package ${chalk.dim(packageName)}`); | ||
|
||
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`, | ||
); | ||
|
||
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([ | ||
{ | ||
type: 'list', | ||
name: 'shouldBumpPackage', | ||
message: `Do you want to bump ${packageName}?`, | ||
choices: ['Yes', 'No'], | ||
filter: val => val === 'Yes', | ||
}, | ||
]) | ||
.then(({shouldBumpPackage}) => { | ||
if (!shouldBumpPackage) { | ||
echo(`Skipping bump for ${packageName}`); | ||
return; | ||
} | ||
|
||
return inquirer | ||
.prompt([ | ||
{ | ||
type: 'list', | ||
name: 'increment', | ||
message: 'Which version you want to increment?', | ||
choices: ['patch', 'minor'], | ||
}, | ||
]) | ||
.then(({increment}) => { | ||
const updatedVersion = bumpPackageVersion( | ||
packageAbsolutePath, | ||
packageManifest, | ||
increment, | ||
); | ||
echo( | ||
`\u2705 Successfully bumped ${chalk.green( | ||
packageName, | ||
)} to ${chalk.green(updatedVersion)}`, | ||
); | ||
}); | ||
}); | ||
}; | ||
|
||
const buildAllExecutors = () => { | ||
const executors = []; | ||
|
||
forEachPackage((...params) => { | ||
executors.push(buildExecutor(...params)); | ||
}); | ||
|
||
return executors; | ||
}; | ||
|
||
const checkIfThereIsSomethingToCommit = () => { | ||
const {stdout: thereIsSomethingToCommit} = exec('git status --porcelain', { | ||
cwd: ROOT_LOCATION, | ||
silent: true, | ||
}); | ||
|
||
return Boolean(thereIsSomethingToCommit); | ||
}; | ||
|
||
const main = async () => { | ||
if (checkIfThereIsSomethingToCommit()) { | ||
echo( | ||
chalk.red( | ||
'Found uncommitted changes. Please commit or stash them before running this script', | ||
), | ||
); | ||
exit(1); | ||
} | ||
|
||
const executors = buildAllExecutors(); | ||
for (const executor of executors) { | ||
await executor() | ||
.catch(() => exit(1)) | ||
.then(() => echo()); | ||
} | ||
|
||
await inquirer | ||
.prompt([ | ||
{ | ||
type: 'list', | ||
name: 'shouldRunYarn', | ||
message: 'Do you want to run yarn?', | ||
choices: ['Yes', 'No'], | ||
filter: val => val === 'Yes', | ||
}, | ||
]) | ||
.then(({shouldRunYarn}) => { | ||
if (!shouldRunYarn) { | ||
echo(`Skipping ${chalk.dim('yarn install')}`); | ||
return; | ||
} | ||
|
||
const {code} = exec('yarn', {cwd: ROOT_LOCATION}); | ||
|
||
if (code) { | ||
echo(chalk.red('Failed to run yarn')); | ||
exit(code); | ||
} | ||
}) | ||
.then(() => echo()); | ||
|
||
if (checkIfThereIsSomethingToCommit()) { | ||
await inquirer | ||
.prompt([ | ||
{ | ||
type: 'list', | ||
name: 'shouldSubmitCommit', | ||
message: 'Do you want to submit a commit with these changes?', | ||
choices: ['Yes', 'No'], | ||
filter: val => val === 'Yes', | ||
}, | ||
]) | ||
.then(({shouldSubmitCommit}) => { | ||
if (!shouldSubmitCommit) { | ||
echo('Not submitting a commit, but keeping all changes'); | ||
return; | ||
} | ||
|
||
exec(`git commit -a -m "${BUMP_COMMIT_MESSAGE}"`, {cwd: ROOT_LOCATION}); | ||
}) | ||
.then(() => echo()); | ||
} | ||
|
||
echo(chalk.green('Successfully finished the process of bumping packages')); | ||
exit(0); | ||
}; | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/** | ||
* 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. | ||
* | ||
* @flow strict-local | ||
* @format | ||
*/ | ||
|
||
const BUMP_COMMIT_MESSAGE = '[ci][monorepo] bump package versions'; | ||
|
||
module.exports = {BUMP_COMMIT_MESSAGE}; |
Oops, something went wrong.