Merge pull request #10010 from backstage/revert-9946-revert-9838-rugvip/auto-bump
Revert "Revert "scripts/prepare-release: update to detect patch versions from patch branches""
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"currentReleaseVersion": {
|
||||
"@backstage/backend-common": "0.12.1",
|
||||
"@backstage/catalog-model": "0.12.1",
|
||||
"@backstage/cli": "0.15.1",
|
||||
"@backstage/plugin-catalog-backend": "0.23.1",
|
||||
"@backstage/plugin-catalog-common": "0.2.1",
|
||||
"@backstage/plugin-catalog-react": "0.8.1"
|
||||
}
|
||||
}
|
||||
@@ -67,12 +67,3 @@ process is used to release an emergency fix as version `6.5.1` in the patch rele
|
||||
- [ ] The fix, which you can likely cherry-pick from your patch branch: `git cherry-pick origin/patch/v1.18.0^`
|
||||
- [ ] An updated `CHANGELOG.md` of all patched packages from the tip of the patch branch, `git checkout origin/patch/v1.18.0 -- {packages,plugins}/*/CHANGELOG.md`.
|
||||
- [ ] A changeset with the message "Applied the fix from version `6.5.1` of this package, which is part of the `v1.18.1` release of Backstage."
|
||||
- [ ] An entry in `.changeset/patched.json` that sets the current release version to `6.5.1`:
|
||||
|
||||
```json
|
||||
{
|
||||
"currentReleaseVersion": {
|
||||
"@backstage/plugin-foo": "6.5.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
+133
-37
@@ -28,6 +28,10 @@ const execFile = promisify(execFileCb);
|
||||
// All of these are considered to be main-line release branches
|
||||
const MAIN_BRANCHES = ['master', 'origin/master', 'changeset-release/master'];
|
||||
|
||||
// This prefix is used for patch branches, followed by the release version
|
||||
// For example, `patch/v1.2.0`
|
||||
const PATCH_BRANCH_PREFIX = 'patch/v';
|
||||
|
||||
const DEPENDENCY_TYPES = [
|
||||
'dependencies',
|
||||
'devDependencies',
|
||||
@@ -35,25 +39,79 @@ const DEPENDENCY_TYPES = [
|
||||
'peerDependencies',
|
||||
];
|
||||
|
||||
/**
|
||||
* Finds the tip of the patch branch of a given release version.
|
||||
* Returns undefined if no patch branch exists.
|
||||
*/
|
||||
async function findTipOfPatchBranch(repo, release) {
|
||||
try {
|
||||
await execFile('git', ['fetch', 'origin', PATCH_BRANCH_PREFIX + release], {
|
||||
shell: true,
|
||||
cwd: repo.root.dir,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.stderr?.match(/fatal: couldn't find remote ref/i)) {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const { stdout: refStr } = await execFile('git', ['rev-parse', 'FETCH_HEAD']);
|
||||
return refStr.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of packages to their versions for any package version
|
||||
* in <ref> that does not match the current version in the working directory.
|
||||
*/
|
||||
async function detectPatchVersionsForRef(repo, ref) {
|
||||
const patchVersions = new Map();
|
||||
|
||||
for (const pkg of repo.packages) {
|
||||
const pkgJsonPath = path.join(
|
||||
path.relative(repo.root.dir, pkg.dir),
|
||||
'package.json',
|
||||
);
|
||||
try {
|
||||
const { stdout: pkgJsonStr } = await execFile('git', [
|
||||
'show',
|
||||
`${ref}:${pkgJsonPath}`,
|
||||
]);
|
||||
if (pkgJsonStr) {
|
||||
const releasePkgJson = JSON.parse(pkgJsonStr);
|
||||
const pkgJson = pkg.packageJson;
|
||||
if (releasePkgJson.name !== pkgJson.name) {
|
||||
throw new Error(
|
||||
`Mismatched package name at ${pkg.dir}, ${releasePkgJson.name} !== ${pkgJson.name}`,
|
||||
);
|
||||
}
|
||||
if (releasePkgJson.version !== pkgJson.version) {
|
||||
patchVersions.set(pkgJson.name, releasePkgJson.version);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (
|
||||
error.stderr?.match(/^fatal: Path .* exists on disk, but not in .*$/m)
|
||||
) {
|
||||
console.log(`Skipping new package ${pkg.packageJson.name}`);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return patchVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bumps up the versions of packages to account for
|
||||
* the base versions that are set in .changeset/patched.json.
|
||||
* This may be needed when we have made emergency releases.
|
||||
*/
|
||||
async function updatePatchVersions() {
|
||||
const patchedJsonPath = path.resolve('.changeset', 'patched.json');
|
||||
const { currentReleaseVersion } = await fs.readJson(patchedJsonPath);
|
||||
if (Object.keys(currentReleaseVersion).length === 0) {
|
||||
console.log('No currentReleaseVersion overrides found, skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
const { packages } = await getPackages(path.resolve('.'));
|
||||
|
||||
async function applyPatchVersions(repo, patchVersions) {
|
||||
const pendingVersionBumps = new Map();
|
||||
|
||||
for (const [name, version] of Object.entries(currentReleaseVersion)) {
|
||||
const pkg = packages.find(p => p.packageJson.name === name);
|
||||
for (const [name, version] of patchVersions) {
|
||||
const pkg = repo.packages.find(p => p.packageJson.name === name);
|
||||
if (!pkg) {
|
||||
throw new Error(`Package ${name} not found`);
|
||||
}
|
||||
@@ -81,7 +139,7 @@ async function updatePatchVersions() {
|
||||
});
|
||||
}
|
||||
|
||||
for (const { dir, packageJson } of packages) {
|
||||
for (const { dir, packageJson } of [repo.root, ...repo.packages]) {
|
||||
let hasChanges = false;
|
||||
|
||||
if (pendingVersionBumps.has(packageJson.name)) {
|
||||
@@ -117,20 +175,45 @@ async function updatePatchVersions() {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await fs.writeJSON(
|
||||
patchedJsonPath,
|
||||
{ currentReleaseVersion: {} },
|
||||
{ spaces: 2, encoding: 'utf8' },
|
||||
);
|
||||
/**
|
||||
* Detects any patched packages version since the most recent release on
|
||||
* the main branch, and then bumps all packages in the repo accordingly.
|
||||
*/
|
||||
async function updatePackageVersions(repo) {
|
||||
const rootPkgPath = path.resolve(repo.root.dir, 'package.json');
|
||||
const { version: currentRelease } = await fs.readJson(rootPkgPath);
|
||||
console.log(`Current release version: ${currentRelease}`);
|
||||
|
||||
const patchRef = await findTipOfPatchBranch(repo, currentRelease);
|
||||
if (patchRef) {
|
||||
console.log(`Tip of the patch branch: ${patchRef}`);
|
||||
|
||||
const patchVersions = await detectPatchVersionsForRef(repo, patchRef);
|
||||
if (patchVersions.size > 0) {
|
||||
console.log(
|
||||
`Found ${patchVersions.size} packages that were patched since the last release`,
|
||||
);
|
||||
for (const [name, version] of patchVersions) {
|
||||
console.log(` ${name}: ${version}`);
|
||||
}
|
||||
|
||||
await applyPatchVersions(repo, patchVersions);
|
||||
} else {
|
||||
console.log('No packages were patched since the last release');
|
||||
}
|
||||
} else {
|
||||
console.log('No patch branch found');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode and tag that is currently set
|
||||
* in the .changeset/pre.json file
|
||||
*/
|
||||
async function getPreInfo(rootPath) {
|
||||
const pre = path.join(rootPath, '.changeset', 'pre.json');
|
||||
async function getPreInfo(repo) {
|
||||
const pre = path.join(repo.root.dir, '.changeset', 'pre.json');
|
||||
if (!(await fs.pathExists(pre))) {
|
||||
return { mode: undefined, tag: undefined };
|
||||
}
|
||||
@@ -139,26 +222,30 @@ async function getPreInfo(rootPath) {
|
||||
return { mode, tag };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the current git branch
|
||||
*/
|
||||
async function getCurrentBranch(repo) {
|
||||
const { stdout } = await execFile(
|
||||
'git',
|
||||
['rev-parse', '--abbrev-ref', 'HEAD'],
|
||||
{ cwd: repo.root.dir, shell: true },
|
||||
);
|
||||
return stdout.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bumps the release version in the root package.json.
|
||||
*
|
||||
* This takes into account whether we're in pre-release mode or on a patch branch.
|
||||
*/
|
||||
async function updateBackstageReleaseVersion() {
|
||||
const rootPath = path.resolve(__dirname, '..');
|
||||
const branchName = await execFile(
|
||||
'git',
|
||||
['rev-parse', '--abbrev-ref', 'HEAD'],
|
||||
{ shell: true },
|
||||
).then(({ stdout }) => stdout.trim());
|
||||
const { mode: preMode, tag: preTag } = await getPreInfo(rootPath);
|
||||
async function updateBackstageReleaseVersion(repo, type) {
|
||||
const { mode: preMode, tag: preTag } = await getPreInfo(repo);
|
||||
|
||||
const packagePath = path.join(rootPath, 'package.json');
|
||||
const package = await fs.readJson(packagePath);
|
||||
const { version: currentVersion } = package;
|
||||
const { version: currentVersion } = repo.root.packageJson;
|
||||
|
||||
let nextVersion;
|
||||
if (MAIN_BRANCHES.includes(branchName)) {
|
||||
if (type === 'minor') {
|
||||
if (preMode === 'pre') {
|
||||
if (semver.prerelease(currentVersion)) {
|
||||
nextVersion = semver.inc(currentVersion, 'pre', preTag);
|
||||
@@ -170,7 +257,7 @@ async function updateBackstageReleaseVersion() {
|
||||
} else {
|
||||
nextVersion = semver.inc(currentVersion, 'minor');
|
||||
}
|
||||
} else {
|
||||
} else if (type === 'patch') {
|
||||
if (preMode) {
|
||||
throw new Error(`Unexpected pre mode ${preMode} on branch ${branchName}`);
|
||||
}
|
||||
@@ -178,9 +265,9 @@ async function updateBackstageReleaseVersion() {
|
||||
}
|
||||
|
||||
await fs.writeJson(
|
||||
packagePath,
|
||||
path.join(repo.root.dir, 'package.json'),
|
||||
{
|
||||
...package,
|
||||
...repo.root.packageJson,
|
||||
version: nextVersion,
|
||||
},
|
||||
{ spaces: 2, encoding: 'utf8' },
|
||||
@@ -188,8 +275,17 @@ async function updateBackstageReleaseVersion() {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await updatePatchVersions();
|
||||
await updateBackstageReleaseVersion();
|
||||
const repo = await getPackages(__dirname);
|
||||
const branchName = await getCurrentBranch(repo);
|
||||
const isMainBranch = MAIN_BRANCHES.includes(branchName);
|
||||
|
||||
console.log(`Current branch: ${branchName}`);
|
||||
if (isMainBranch) {
|
||||
console.log('Main release, updating package versions');
|
||||
await updatePackageVersions(repo);
|
||||
}
|
||||
|
||||
await updateBackstageReleaseVersion(repo, isMainBranch ? 'minor' : 'patch');
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
|
||||
Reference in New Issue
Block a user