Version Bump: Add CLI utility (#34555)
This commit is contained in:
parent
52ba69d712
commit
fa2e7f7e96
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Add version number to composer.json
|
|
@ -6,6 +6,7 @@
|
|||
"license": "GPL-3.0-or-later",
|
||||
"prefer-stable": true,
|
||||
"minimum-stability": "dev",
|
||||
"version": "2.1.0",
|
||||
"require": {
|
||||
"composer/installers": "~1.7"
|
||||
},
|
||||
|
|
7524
pnpm-lock.yaml
7524
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -8,4 +8,4 @@ packages:
|
|||
- 'tools/package-release'
|
||||
- 'tools/cherry-pick'
|
||||
- 'tools/release-post-generator'
|
||||
|
||||
- 'tools/version-bump'
|
||||
|
|
|
@ -13,4 +13,4 @@ import './commands/release-post';
|
|||
dotenv.config();
|
||||
|
||||
// Start the program
|
||||
program.parse();
|
||||
program.parse( process.argv );
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": [ "plugin:@woocommerce/eslint-plugin/recommended" ]
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { prerelease } from 'semver';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { program } from '../program';
|
||||
import { validateArgs, stripPrereleaseParameters } from '../lib/validate';
|
||||
import {
|
||||
updatePluginFile,
|
||||
updateReadmeChangelog,
|
||||
updateJSON,
|
||||
updateClassPluginFile,
|
||||
updateReadmeStableTag,
|
||||
} from '../lib/update';
|
||||
|
||||
program
|
||||
.command( 'bump' )
|
||||
.description( 'CLI to automate version bumping.' )
|
||||
.argument( '<plugin>', 'Monorepo plugin' )
|
||||
.requiredOption( '-v, --version <string>', 'Version to bump to' )
|
||||
.action( async ( plugin: string, options ) => {
|
||||
await validateArgs( plugin, options );
|
||||
|
||||
let nextVersion = options.version;
|
||||
|
||||
const prereleaseParameters = prerelease( nextVersion );
|
||||
const isDevVersionBump =
|
||||
prereleaseParameters && prereleaseParameters[ 0 ] === 'dev';
|
||||
|
||||
await updatePluginFile( plugin, nextVersion );
|
||||
|
||||
// Any updated files besides the plugin file get a version stripped of prerelease parameters.
|
||||
nextVersion = stripPrereleaseParameters( nextVersion );
|
||||
|
||||
if ( isDevVersionBump ) {
|
||||
// Bumping the dev version means updating the readme's changelog.
|
||||
await updateReadmeChangelog( plugin, nextVersion );
|
||||
} else {
|
||||
// Only update stable tag on real releases.
|
||||
await updateReadmeStableTag( plugin, nextVersion );
|
||||
}
|
||||
|
||||
await updateJSON( 'composer', plugin, nextVersion );
|
||||
await updateJSON( 'package', plugin, nextVersion );
|
||||
await updateClassPluginFile( plugin, nextVersion );
|
||||
} );
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { program } from './program';
|
||||
import './commands/bump';
|
||||
|
||||
// Start the program
|
||||
program.parse();
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { dirname } from 'path';
|
||||
|
||||
// Escape from ./tools/package-release/src
|
||||
export const MONOREPO_ROOT = dirname( dirname( dirname( __dirname ) ) );
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const chalk = require( 'chalk' );
|
||||
|
||||
const { error } = console;
|
||||
|
||||
/**
|
||||
* This is a temporary Logger until a common one is built.
|
||||
*/
|
||||
export class Logger {
|
||||
static error( message: string ) {
|
||||
error( chalk.red( message ) );
|
||||
process.exit( 1 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { readFile, writeFile, stat } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Logger } from './logger';
|
||||
import { MONOREPO_ROOT } from './const';
|
||||
|
||||
/**
|
||||
* Update plugin readme stable tag.
|
||||
*
|
||||
* @param plugin plugin to update
|
||||
* @param nextVersion version to bump to
|
||||
*/
|
||||
export const updateReadmeStableTag = async (
|
||||
plugin: string,
|
||||
nextVersion: string
|
||||
): Promise< void > => {
|
||||
const filePath = join( MONOREPO_ROOT, `plugins/${ plugin }/readme.txt` );
|
||||
try {
|
||||
const readmeContents = await readFile( filePath, 'utf8' );
|
||||
|
||||
const updatedReadmeContents = readmeContents.replace(
|
||||
/Stable tag: \d.\d.\d\n/m,
|
||||
`Stable tag: ${ nextVersion }\n`
|
||||
);
|
||||
|
||||
await writeFile( filePath, updatedReadmeContents );
|
||||
} catch ( e ) {
|
||||
Logger.error( 'Unable to update readme stable tag' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update plugin readme changelog.
|
||||
*
|
||||
* @param plugin plugin to update
|
||||
* @param nextVersion version to bump to
|
||||
*/
|
||||
export const updateReadmeChangelog = async (
|
||||
plugin: string,
|
||||
nextVersion: string
|
||||
): Promise< void > => {
|
||||
const filePath = join( MONOREPO_ROOT, `plugins/${ plugin }/readme.txt` );
|
||||
try {
|
||||
const readmeContents = await readFile( filePath, 'utf8' );
|
||||
|
||||
const updatedReadmeContents = readmeContents.replace(
|
||||
/= \d.\d.\d \d\d\d\d-XX-XX =\n/m,
|
||||
`= ${ nextVersion } ${ new Date().getFullYear() }-XX-XX =\n`
|
||||
);
|
||||
|
||||
await writeFile( filePath, updatedReadmeContents );
|
||||
} catch ( e ) {
|
||||
Logger.error( 'Unable to update readme changelog' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update plugin class file.
|
||||
*
|
||||
* @param plugin plugin to update
|
||||
* @param nextVersion version to bump to
|
||||
*/
|
||||
export const updateClassPluginFile = async (
|
||||
plugin: string,
|
||||
nextVersion: string
|
||||
): Promise< void > => {
|
||||
const filePath = join(
|
||||
MONOREPO_ROOT,
|
||||
`plugins/${ plugin }/includes/class-${ plugin }.php`
|
||||
);
|
||||
|
||||
try {
|
||||
await stat( filePath );
|
||||
} catch ( e ) {
|
||||
// Class file does not exist, return early.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const classPluginFileContents = await readFile( filePath, 'utf8' );
|
||||
|
||||
const updatedClassPluginFileContents = classPluginFileContents.replace(
|
||||
/public \$version = '\d.\d.\d';\n/m,
|
||||
`public $version = '${ nextVersion }';\n`
|
||||
);
|
||||
|
||||
await writeFile( filePath, updatedClassPluginFileContents );
|
||||
} catch ( e ) {
|
||||
Logger.error( 'Unable to update plugin file.' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update plugin JSON files.
|
||||
*
|
||||
* @param {string} type plugin to update
|
||||
* @param {string} plugin plugin to update
|
||||
* @param {string} nextVersion version to bump to
|
||||
*/
|
||||
export const updateJSON = async (
|
||||
type: 'package' | 'composer',
|
||||
plugin: string,
|
||||
nextVersion: string
|
||||
): Promise< void > => {
|
||||
const filePath = join(
|
||||
MONOREPO_ROOT,
|
||||
`plugins/${ plugin }/${ type }.json`
|
||||
);
|
||||
try {
|
||||
const composerJson = JSON.parse( await readFile( filePath, 'utf8' ) );
|
||||
composerJson.version = nextVersion;
|
||||
await writeFile(
|
||||
filePath,
|
||||
JSON.stringify( composerJson, null, '\t' ) + '\n'
|
||||
);
|
||||
} catch ( e ) {
|
||||
Logger.error( 'Unable to update composer.json' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update plugin main file.
|
||||
*
|
||||
* @param plugin plugin to update
|
||||
* @param nextVersion version to bump to
|
||||
*/
|
||||
export const updatePluginFile = async (
|
||||
plugin: string,
|
||||
nextVersion: string
|
||||
): Promise< void > => {
|
||||
const filePath = join(
|
||||
MONOREPO_ROOT,
|
||||
`plugins/${ plugin }/${ plugin }.php`
|
||||
);
|
||||
try {
|
||||
const pluginFileContents = await readFile( filePath, 'utf8' );
|
||||
|
||||
const updatedPluginFileContents = pluginFileContents.replace(
|
||||
/Version: \d.\d.\d.*\n/m,
|
||||
`Version: ${ nextVersion }\n`
|
||||
);
|
||||
await writeFile( filePath, updatedPluginFileContents );
|
||||
} catch ( e ) {
|
||||
Logger.error( 'Unable to update plugin file.' );
|
||||
}
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { valid, lt as versionLessThan, prerelease, parse } from 'semver';
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Logger } from './logger';
|
||||
|
||||
/**
|
||||
* Get a plugin's current version.
|
||||
*
|
||||
* @param plugin plugin to update.
|
||||
*/
|
||||
export const getCurrentVersion = async (
|
||||
plugin: string
|
||||
): Promise< string | void > => {
|
||||
try {
|
||||
const composerJSON = JSON.parse(
|
||||
await readFile( `plugins/${ plugin }/composer.json`, 'utf8' )
|
||||
);
|
||||
return composerJSON.version;
|
||||
} catch ( e ) {
|
||||
Logger.error( 'Unable to read current version.' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* When given a prerelease version, return just the version.
|
||||
*
|
||||
* @param {string} prereleaseVersion version with prerelease params
|
||||
* @return {string} version
|
||||
*/
|
||||
export const stripPrereleaseParameters = (
|
||||
prereleaseVersion: string
|
||||
): string => {
|
||||
const parsedVersion = parse( prereleaseVersion );
|
||||
if ( parsedVersion ) {
|
||||
const { major, minor, patch } = parsedVersion;
|
||||
return `${ major }.${ minor }.${ patch }`;
|
||||
}
|
||||
return prereleaseVersion;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate inputs.
|
||||
*
|
||||
* @param plugin plugin
|
||||
* @param options options
|
||||
* @param options.version version
|
||||
*/
|
||||
export const validateArgs = async (
|
||||
plugin: string,
|
||||
options: { version: string }
|
||||
): Promise< void > => {
|
||||
const nextVersion = options.version;
|
||||
|
||||
if ( ! valid( nextVersion ) ) {
|
||||
Logger.error(
|
||||
'Invalid version supplied, please pass in a semantically correct version.'
|
||||
);
|
||||
}
|
||||
|
||||
const currentVersion = await getCurrentVersion( plugin );
|
||||
|
||||
if ( ! currentVersion ) {
|
||||
Logger.error( 'Unable to determine current version' );
|
||||
} else if ( versionLessThan( nextVersion, currentVersion ) ) {
|
||||
Logger.error(
|
||||
'The version supplied is less than the current version, please supply a valid version.'
|
||||
);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "version-bump",
|
||||
"version": "0.1.0",
|
||||
"description": "Automate version bumping for WooCommerce plugins",
|
||||
"main": " ",
|
||||
"scripts": {},
|
||||
"author": "",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
"@types/express": "^4.17.13",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@commander-js/extra-typings": "^0.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"commander": "9.4.0",
|
||||
"express": "^4.18.1",
|
||||
"ora": "^5.4.1",
|
||||
"semver": "^7.3.2",
|
||||
"ts-node": "^10.9.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Command } from '@commander-js/extra-typings';
|
||||
|
||||
export const program = new Command();
|
||||
|
||||
program
|
||||
.name( 'version-bump' )
|
||||
.description( 'CLI to automate version bumping for WooCommerce plugins.' );
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "@tsconfig/node16/tsconfig.json",
|
||||
"ts-node": {
|
||||
"transpileOnly": true,
|
||||
"files": true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue