Monrepo Utils code-freeze: Update version bump to modify release branches (#39243)

This commit is contained in:
Paul Sealock 2023-07-25 10:03:01 +12:00 committed by GitHub
parent b57f988044
commit 62f02e36f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 124 additions and 62 deletions

View File

@ -83,7 +83,7 @@ jobs:
if: steps.check-freeze.outputs.freeze == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: pnpm run utils code-freeze version-bump -o ${{ github.repository_owner }} -v ${{ steps.milestone.outputs.nextDevelopmentVersion }}.0-dev
run: pnpm run utils code-freeze version-bump ${{ steps.milestone.outputs.nextDevelopmentVersion }}.0-dev -o ${{ github.repository_owner }}
- name: Generate changelog changes
id: changelog

View File

@ -8,7 +8,10 @@ import simpleGit from 'simple-git';
* Internal dependencies
*/
import { Logger } from '../../../core/logger';
import { sparseCheckoutRepoShallow } from '../../../core/git';
import {
sparseCheckoutRepoShallow,
checkoutRemoteBranch,
} from '../../../core/git';
import { createPullRequest } from '../../../core/github/repo';
import { getEnvVar } from '../../../core/environment';
import { getMajorMinor } from '../../../core/version';
@ -16,16 +19,9 @@ import { bumpFiles } from './bump';
import { validateArgs } from './lib/validate';
import { Options } from './types';
const genericErrorFunction = ( err ) => {
if ( err.git ) {
return err.git;
}
throw err;
};
export const versionBumpCommand = new Command( 'version-bump' )
.description( 'Bump versions ahead of new development cycle' )
.requiredOption( '-v, --version <version>', 'Version to bump to' )
.argument( '<version>', 'Version to bump to' )
.option(
'-o --owner <owner>',
'Repository owner. Default: woocommerce',
@ -41,8 +37,19 @@ export const versionBumpCommand = new Command( 'version-bump' )
'Base branch to create the PR against. Default: trunk',
'trunk'
)
.action( async ( options: Options ) => {
const { owner, name, version, base } = options;
.option(
'-d --dry-run',
'Prepare the version bump and log a diff. Do not create a PR or push to branch',
false
)
.option(
'-c --commit-direct-to-base',
'Commit directly to the base branch. Do not create a PR just push directly to base branch',
false
)
.action( async ( version, options: Options ) => {
const { owner, name, base, dryRun, commitDirectToBase } = options;
Logger.startTask(
`Making a temporary clone of '${ owner }/${ name }'`
);
@ -67,52 +74,88 @@ export const versionBumpCommand = new Command( 'version-bump' )
`Temporary clone of '${ owner }/${ name }' created at ${ tmpRepoPath }`
);
await validateArgs( tmpRepoPath, version );
const git = simpleGit( {
baseDir: tmpRepoPath,
config: [ 'core.hooksPath=/dev/null' ],
} );
const majorMinor = getMajorMinor( version );
const branch = `prep/trunk-for-next-dev-cycle-${ majorMinor }`;
const exists = await git.raw( 'ls-remote', 'origin', branch );
if ( exists.trim().length > 0 ) {
Logger.error(
`Branch ${ branch } already exists. Run \`git push <remote> --delete ${ branch }\` and rerun this command.`
);
}
await git.checkoutBranch( branch, base ).catch( genericErrorFunction );
Logger.notice( `Bumping versions in ${ owner }/${ name }` );
bumpFiles( tmpRepoPath, version );
Logger.notice( 'Adding and committing changes' );
await git.add( '.' ).catch( genericErrorFunction );
await git
.commit( `Prep trunk for ${ majorMinor } cycle` )
.catch( genericErrorFunction );
Logger.notice( 'Pushing to Github' );
await git.push( 'origin', branch ).catch( ( e ) => {
Logger.error( e );
} );
const branch = `prep/${ base }-for-next-dev-cycle-${ majorMinor }`;
try {
Logger.startTask( 'Creating a pull request' );
if ( commitDirectToBase ) {
if ( base === 'trunk' ) {
Logger.error(
`The --commit-direct-to-base option cannot be used with the trunk branch as a base. A pull request must be created instead.`
);
}
Logger.notice( `Checking out ${ base }` );
await checkoutRemoteBranch( tmpRepoPath, base );
} else {
const exists = await git.raw( 'ls-remote', 'origin', branch );
const pullRequest = await createPullRequest( {
owner,
name,
title: `Prep trunk for ${ majorMinor } cycle`,
body: `This PR updates the versions in trunk to ${ version } for next development cycle.`,
head: branch,
base,
} );
Logger.notice( `Pull request created: ${ pullRequest.html_url }` );
Logger.endTask();
} catch ( e ) {
Logger.error( e );
if ( ! dryRun && exists.trim().length > 0 ) {
Logger.error(
`Branch ${ branch } already exists. Run \`git push <remote> --delete ${ branch }\` and rerun this command.`
);
}
if ( base !== 'trunk' ) {
// if the base is not trunk, we need to checkout the base branch first before creating a new branch.
Logger.notice( `Checking out ${ base }` );
await checkoutRemoteBranch( tmpRepoPath, base );
}
Logger.notice( `Creating new branch ${ branch }` );
await git.checkoutBranch( branch, base );
}
Logger.notice( 'Validating arguments' );
await validateArgs( tmpRepoPath, version, options );
const workingBranch = commitDirectToBase ? base : branch;
Logger.notice(
`Bumping versions in ${ owner }/${ name } on ${ workingBranch } branch`
);
bumpFiles( tmpRepoPath, version );
if ( dryRun ) {
const diff = await git.diffSummary();
Logger.notice(
`The version has been bumped to ${ version } in the following files:`
);
Logger.warn( diff.files.map( ( f ) => f.file ).join( '\n' ) );
Logger.notice(
'Dry run complete. No pull was request created nor was a commit made.'
);
return;
}
Logger.notice( 'Adding and committing changes' );
await git.add( '.' );
await git.commit(
`Prep ${ base } for ${ majorMinor } cycle with version bump to ${ version }`
);
Logger.notice( `Pushing ${ workingBranch } branch to Github` );
await git.push( 'origin', workingBranch );
if ( ! commitDirectToBase ) {
Logger.startTask( 'Creating a pull request' );
const pullRequest = await createPullRequest( {
owner,
name,
title: `Prep ${ base } for ${ majorMinor } cycle`,
body: `This PR updates the versions in ${ base } to ${ version }.`,
head: branch,
base,
} );
Logger.notice(
`Pull request created: ${ pullRequest.html_url }`
);
Logger.endTask();
}
} catch ( error ) {
Logger.error( error );
}
} );

View File

@ -9,6 +9,7 @@ import { readFile } from 'fs/promises';
* Internal dependencies
*/
import { Logger } from '../../../../core/logger';
import { Options } from '../types';
/**
* Get a plugin's current version.
*
@ -17,10 +18,11 @@ import { Logger } from '../../../../core/logger';
export const getCurrentVersion = async (
tmpRepoPath: string
): Promise< string | void > => {
const filePath = join( tmpRepoPath, `plugins/woocommerce/composer.json` );
const filePath = join( tmpRepoPath, `plugins/woocommerce/woocommerce.php` );
try {
const composerJSON = JSON.parse( await readFile( filePath, 'utf8' ) );
return composerJSON.version;
const data = await readFile( filePath, 'utf8' );
const matches = data.match( /Version:\s*(.*)/ );
return matches ? matches[ 1 ] : undefined;
} catch ( e ) {
Logger.error( e );
}
@ -48,11 +50,14 @@ export const stripPrereleaseParameters = (
*
* @param tmpRepoPath cloned repo path
* @param version version to bump to
* @param options options passed to the command
*/
export const validateArgs = async (
tmpRepoPath: string,
version: string
version: string,
options: Options
): Promise< void > => {
const { base } = options;
const nextVersion = version;
if ( ! valid( nextVersion ) ) {
@ -65,9 +70,9 @@ export const validateArgs = async (
const isDevVersionBump =
prereleaseParameters && prereleaseParameters[ 0 ] === 'dev';
if ( ! isDevVersionBump ) {
if ( ! isDevVersionBump && base === 'trunk' ) {
Logger.error(
`Version ${ nextVersion } is not a development version bump. This tool is only intended to bump development versions for the preparation of the next development cycle.`
`Version ${ nextVersion } is not a development version bump and cannot be applied to trunk, which only accepts development version bumps.`
);
}
@ -76,6 +81,13 @@ export const validateArgs = async (
if ( ! currentVersion ) {
Logger.error( 'Unable to determine current version' );
} else if ( versionLessThan( nextVersion, currentVersion ) ) {
// Semver thinks -a.1 is less than -dev, but -a.1 from -dev will be a valid version bump.
if (
nextVersion.includes( 'a.' ) &&
currentVersion.includes( 'dev' )
) {
return;
}
Logger.error(
'The version supplied is less than the current version, please supply a valid version.'
);

View File

@ -1,6 +1,7 @@
export type Options = {
owner?: string;
name?: string;
version?: string;
base?: string;
dryRun?: boolean;
commitDirectToBase?: boolean;
};

View File

@ -437,12 +437,14 @@ export const generateDiff = async (
/**
*
* @param {string} tmpRepoPath path to temporary repo
* @param {string} branch remote branch to checkout
* @param {string} tmpRepoPath path to temporary repo
* @param {string} branch remote branch to checkout
* @param {boolean} isShallow whether to do a shallow clone and get only the latest commit
*/
export const checkoutRemoteBranch = async (
tmpRepoPath: string,
branch: string
branch: string,
isShallow = true
): Promise< void > => {
const git = simpleGit( {
baseDir: tmpRepoPath,
@ -451,6 +453,10 @@ export const checkoutRemoteBranch = async (
// When the clone is shallow, we need to call this before fetching.
await git.raw( [ 'remote', 'set-branches', '--add', 'origin', branch ] );
await git.raw( [ 'fetch', 'origin', branch ] );
const fetchArgs = [ 'fetch', 'origin', branch ];
if ( isShallow ) {
fetchArgs.push( '--depth=1' );
}
await git.raw( fetchArgs );
await git.raw( [ 'checkout', '-b', branch, `origin/${ branch }` ] );
};