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",
|
"license": "GPL-3.0-or-later",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
|
"version": "2.1.0",
|
||||||
"require": {
|
"require": {
|
||||||
"composer/installers": "~1.7"
|
"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/package-release'
|
||||||
- 'tools/cherry-pick'
|
- 'tools/cherry-pick'
|
||||||
- 'tools/release-post-generator'
|
- 'tools/release-post-generator'
|
||||||
|
- 'tools/version-bump'
|
||||||
|
|
|
@ -13,4 +13,4 @@ import './commands/release-post';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
// Start the program
|
// 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