Code Freeze CLI: Add release changelog command (#38082)
This commit is contained in:
parent
68fe31abe4
commit
1188197a2f
|
@ -3250,6 +3250,9 @@ importers:
|
||||||
'@octokit/graphql-schema':
|
'@octokit/graphql-schema':
|
||||||
specifier: ^14.1.0
|
specifier: ^14.1.0
|
||||||
version: 14.1.0
|
version: 14.1.0
|
||||||
|
'@octokit/types':
|
||||||
|
specifier: ^9.2.0
|
||||||
|
version: 9.2.1
|
||||||
'@types/uuid':
|
'@types/uuid':
|
||||||
specifier: ^9.0.1
|
specifier: ^9.0.1
|
||||||
version: 9.0.1
|
version: 9.0.1
|
||||||
|
@ -3507,43 +3510,6 @@ importers:
|
||||||
specifier: ^5.70.0
|
specifier: ^5.70.0
|
||||||
version: 5.70.0(uglify-js@3.14.5)(webpack-cli@4.9.2)
|
version: 5.70.0(uglify-js@3.14.5)(webpack-cli@4.9.2)
|
||||||
|
|
||||||
tools/version-bump:
|
|
||||||
dependencies:
|
|
||||||
'@commander-js/extra-typings':
|
|
||||||
specifier: ^0.1.0
|
|
||||||
version: 0.1.0(commander@9.4.0)
|
|
||||||
'@woocommerce/monorepo-utils':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../monorepo-utils
|
|
||||||
chalk:
|
|
||||||
specifier: ^4.1.2
|
|
||||||
version: 4.1.2
|
|
||||||
commander:
|
|
||||||
specifier: 9.4.0
|
|
||||||
version: 9.4.0
|
|
||||||
express:
|
|
||||||
specifier: ^4.18.1
|
|
||||||
version: 4.18.1
|
|
||||||
ora:
|
|
||||||
specifier: ^5.4.1
|
|
||||||
version: 5.4.1
|
|
||||||
semver:
|
|
||||||
specifier: ^7.3.2
|
|
||||||
version: 7.3.7
|
|
||||||
ts-node:
|
|
||||||
specifier: ^10.9.1
|
|
||||||
version: 10.9.1(@types/node@16.18.21)(typescript@4.9.5)
|
|
||||||
devDependencies:
|
|
||||||
'@tsconfig/node16':
|
|
||||||
specifier: ^1.0.3
|
|
||||||
version: 1.0.3
|
|
||||||
'@types/express':
|
|
||||||
specifier: ^4.17.13
|
|
||||||
version: 4.17.14
|
|
||||||
typescript:
|
|
||||||
specifier: ^4.9.5
|
|
||||||
version: 4.9.5
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
/@actions/core@1.10.0:
|
/@actions/core@1.10.0:
|
||||||
|
@ -3871,7 +3837,7 @@ packages:
|
||||||
'@babel/core': ^7.0.0-0
|
'@babel/core': ^7.0.0-0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.21.3
|
'@babel/core': 7.21.3
|
||||||
'@jridgewell/trace-mapping': 0.3.17
|
'@jridgewell/trace-mapping': 0.3.16
|
||||||
commander: 4.1.1
|
commander: 4.1.1
|
||||||
convert-source-map: 1.8.0
|
convert-source-map: 1.8.0
|
||||||
fs-readdir-recursive: 1.1.0
|
fs-readdir-recursive: 1.1.0
|
||||||
|
@ -8720,9 +8686,9 @@ packages:
|
||||||
'@babel/core': 7.21.3
|
'@babel/core': 7.21.3
|
||||||
'@babel/helper-annotate-as-pure': 7.16.7
|
'@babel/helper-annotate-as-pure': 7.16.7
|
||||||
'@babel/helper-module-imports': 7.16.7
|
'@babel/helper-module-imports': 7.16.7
|
||||||
'@babel/helper-plugin-utils': 7.18.9
|
'@babel/helper-plugin-utils': 7.20.2
|
||||||
'@babel/plugin-syntax-jsx': 7.16.7(@babel/core@7.21.3)
|
'@babel/plugin-syntax-jsx': 7.16.7(@babel/core@7.21.3)
|
||||||
'@babel/types': 7.17.0
|
'@babel/types': 7.21.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/plugin-transform-react-jsx@7.19.0(@babel/core@7.12.9):
|
/@babel/plugin-transform-react-jsx@7.19.0(@babel/core@7.12.9):
|
||||||
|
@ -8951,8 +8917,8 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.21.3
|
'@babel/core': 7.21.3
|
||||||
'@babel/helper-module-imports': 7.16.0
|
'@babel/helper-module-imports': 7.16.0
|
||||||
'@babel/helper-plugin-utils': 7.20.2
|
'@babel/helper-plugin-utils': 7.14.5
|
||||||
babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.3)
|
babel-plugin-polyfill-corejs2: 0.3.0(@babel/core@7.21.3)
|
||||||
babel-plugin-polyfill-corejs3: 0.4.0(@babel/core@7.21.3)
|
babel-plugin-polyfill-corejs3: 0.4.0(@babel/core@7.21.3)
|
||||||
babel-plugin-polyfill-regenerator: 0.3.0(@babel/core@7.21.3)
|
babel-plugin-polyfill-regenerator: 0.3.0(@babel/core@7.21.3)
|
||||||
semver: 6.3.0
|
semver: 6.3.0
|
||||||
|
@ -11969,7 +11935,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/resolve-uri': 3.1.0
|
'@jridgewell/resolve-uri': 3.1.0
|
||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@jridgewell/trace-mapping@0.3.17:
|
/@jridgewell/trace-mapping@0.3.17:
|
||||||
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
|
||||||
|
@ -12494,7 +12459,7 @@ packages:
|
||||||
'@octokit/graphql': 4.8.0
|
'@octokit/graphql': 4.8.0
|
||||||
'@octokit/request': 5.6.3
|
'@octokit/request': 5.6.3
|
||||||
'@octokit/request-error': 2.1.0
|
'@octokit/request-error': 2.1.0
|
||||||
'@octokit/types': 6.34.0
|
'@octokit/types': 6.41.0
|
||||||
before-after-hook: 2.2.2
|
before-after-hook: 2.2.2
|
||||||
universal-user-agent: 6.0.0
|
universal-user-agent: 6.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -12594,10 +12559,6 @@ packages:
|
||||||
- encoding
|
- encoding
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@octokit/openapi-types@11.2.0:
|
|
||||||
resolution: {integrity: sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@octokit/openapi-types@12.11.0:
|
/@octokit/openapi-types@12.11.0:
|
||||||
resolution: {integrity: sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==}
|
resolution: {integrity: sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==}
|
||||||
|
|
||||||
|
@ -12794,12 +12755,6 @@ packages:
|
||||||
- encoding
|
- encoding
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@octokit/types@6.34.0:
|
|
||||||
resolution: {integrity: sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==}
|
|
||||||
dependencies:
|
|
||||||
'@octokit/openapi-types': 11.2.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@octokit/types@6.41.0:
|
/@octokit/types@6.41.0:
|
||||||
resolution: {integrity: sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==}
|
resolution: {integrity: sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -20857,8 +20812,8 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
postcss: ^8.1.0
|
postcss: ^8.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.20.2
|
browserslist: 4.21.4
|
||||||
caniuse-lite: 1.0.30001352
|
caniuse-lite: 1.0.30001418
|
||||||
fraction.js: 4.2.0
|
fraction.js: 4.2.0
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
|
@ -22401,6 +22356,7 @@ packages:
|
||||||
escalade: 3.1.1
|
escalade: 3.1.1
|
||||||
node-releases: 2.0.6
|
node-releases: 2.0.6
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/browserslist@4.20.4:
|
/browserslist@4.20.4:
|
||||||
resolution: {integrity: sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==}
|
resolution: {integrity: sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"@commander-js/extra-typings": "^10.0.3",
|
"@commander-js/extra-typings": "^10.0.3",
|
||||||
"@octokit/graphql": "4.8.0",
|
"@octokit/graphql": "4.8.0",
|
||||||
"@octokit/graphql-schema": "^14.1.0",
|
"@octokit/graphql-schema": "^14.1.0",
|
||||||
|
"@octokit/types": "^9.2.0",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"commander": "^10.0.1",
|
"commander": "^10.0.1",
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Command } from '@commander-js/extra-typings';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { Logger } from '../../../core/logger';
|
||||||
|
import { cloneAuthenticatedRepo } from '../../../core/git';
|
||||||
|
import { updateTrunkChangelog, updateReleaseBranchChangelogs } from './lib';
|
||||||
|
import { Options } from './types';
|
||||||
|
|
||||||
|
export const changelogCommand = new Command( 'changelog' )
|
||||||
|
.description( 'Create a new release branch' )
|
||||||
|
.option(
|
||||||
|
'-o --owner <owner>',
|
||||||
|
'Repository owner. Default: woocommerce',
|
||||||
|
'woocommerce'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-n --name <name>',
|
||||||
|
'Repository name. Default: woocommerce',
|
||||||
|
'woocommerce'
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-d --dev-repo-path <devRepoPath>',
|
||||||
|
'Path to existing repo. Use this option to avoid cloning a fresh repo for development purposes. Note that using this option assumes dependencies are already installed.'
|
||||||
|
)
|
||||||
|
.requiredOption( '-v, --version <version>', 'Version to bump to' )
|
||||||
|
.action( async ( options: Options ) => {
|
||||||
|
const { owner, name, version, devRepoPath } = options;
|
||||||
|
Logger.startTask(
|
||||||
|
`Making a temporary clone of '${ owner }/${ name }'`
|
||||||
|
);
|
||||||
|
// Use a supplied path, otherwise do a full clone of the repo, including history so that changelogs can be created with links to PRs.
|
||||||
|
const tmpRepoPath = devRepoPath
|
||||||
|
? devRepoPath
|
||||||
|
: await cloneAuthenticatedRepo( options, false );
|
||||||
|
|
||||||
|
Logger.endTask();
|
||||||
|
|
||||||
|
Logger.notice(
|
||||||
|
`Temporary clone of '${ owner }/${ name }' created at ${ tmpRepoPath }`
|
||||||
|
);
|
||||||
|
|
||||||
|
// When a devRepoPath is provided, assume that the dependencies are already installed.
|
||||||
|
if ( ! devRepoPath ) {
|
||||||
|
Logger.notice( `Installing dependencies in ${ tmpRepoPath }` );
|
||||||
|
execSync( 'pnpm install --filter woocommerce', {
|
||||||
|
cwd: tmpRepoPath,
|
||||||
|
stdio: 'inherit',
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseBranch = `release/${ version }`;
|
||||||
|
|
||||||
|
// Update the release branch.
|
||||||
|
const releaseBranchChanges = await updateReleaseBranchChangelogs(
|
||||||
|
options,
|
||||||
|
tmpRepoPath,
|
||||||
|
releaseBranch
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update trunk.
|
||||||
|
await updateTrunkChangelog(
|
||||||
|
options,
|
||||||
|
tmpRepoPath,
|
||||||
|
releaseBranch,
|
||||||
|
releaseBranchChanges
|
||||||
|
);
|
||||||
|
} );
|
|
@ -0,0 +1,153 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import simpleGit from 'simple-git';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { Logger } from '../../../../core/logger';
|
||||||
|
import { checkoutRemoteBranch } from '../../../../core/git';
|
||||||
|
import { createPullRequest } from '../../../../core/github/repo';
|
||||||
|
import { Options } from '../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform changelog operations on release branch by submitting a pull request. The release branch is a remote branch.
|
||||||
|
*
|
||||||
|
* @param {Object} options CLI options
|
||||||
|
* @param {string} tmpRepoPath temp repo path
|
||||||
|
* @param {string} releaseBranch release branch name. The release branch is a remote branch on Github.
|
||||||
|
* @return {Object} update data
|
||||||
|
*/
|
||||||
|
export const updateReleaseBranchChangelogs = async (
|
||||||
|
options: Options,
|
||||||
|
tmpRepoPath: string,
|
||||||
|
releaseBranch: string
|
||||||
|
): Promise< { deletionCommitHash: string; prNumber: number } > => {
|
||||||
|
const { owner, name, version } = options;
|
||||||
|
try {
|
||||||
|
await checkoutRemoteBranch( tmpRepoPath, releaseBranch );
|
||||||
|
} catch ( e ) {
|
||||||
|
if ( e.message.includes( "couldn't find remote ref" ) ) {
|
||||||
|
Logger.error(
|
||||||
|
`${ releaseBranch } does not exist on ${ owner }/${ name }.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Logger.error( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
const git = simpleGit( {
|
||||||
|
baseDir: tmpRepoPath,
|
||||||
|
config: [ 'core.hooksPath=/dev/null' ],
|
||||||
|
} );
|
||||||
|
|
||||||
|
const branch = `update/${ version }-changelog`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await git.checkout( {
|
||||||
|
'-b': null,
|
||||||
|
[ branch ]: null,
|
||||||
|
} );
|
||||||
|
|
||||||
|
Logger.notice( `Running the changelog script in ${ tmpRepoPath }` );
|
||||||
|
execSync(
|
||||||
|
`pnpm --filter=woocommerce run changelog write --add-pr-num -n -vvv --use-version ${ version }`,
|
||||||
|
{
|
||||||
|
cwd: tmpRepoPath,
|
||||||
|
stdio: 'inherit',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Logger.notice( `Committing deleted files in ${ tmpRepoPath }` );
|
||||||
|
//Checkout pnpm-lock.yaml to prevent issues in case of an out of date lockfile.
|
||||||
|
await git.checkout( 'pnpm-lock.yaml' );
|
||||||
|
await git.add( 'plugins/woocommerce/changelog/' );
|
||||||
|
await git.commit( `Delete changelog files from ${ version } release` );
|
||||||
|
const deletionCommitHash = await git.raw( [ 'rev-parse', 'HEAD' ] );
|
||||||
|
Logger.notice( `git deletion hash: ${ deletionCommitHash }` );
|
||||||
|
|
||||||
|
Logger.notice( `Updating readme.txt in ${ tmpRepoPath }` );
|
||||||
|
execSync( 'php .github/workflows/scripts/release-changelog.php', {
|
||||||
|
cwd: tmpRepoPath,
|
||||||
|
stdio: 'inherit',
|
||||||
|
} );
|
||||||
|
|
||||||
|
Logger.notice(
|
||||||
|
`Committing readme.txt changes in ${ branch } on ${ tmpRepoPath }`
|
||||||
|
);
|
||||||
|
await git.add( 'plugins/woocommerce/readme.txt' );
|
||||||
|
await git.commit(
|
||||||
|
`Update the readme files for the ${ version } release`
|
||||||
|
);
|
||||||
|
await git.push( 'origin', branch );
|
||||||
|
await git.checkout( '.' );
|
||||||
|
|
||||||
|
Logger.notice( `Creating PR for ${ branch }` );
|
||||||
|
const pullRequest = await createPullRequest( {
|
||||||
|
owner,
|
||||||
|
name,
|
||||||
|
title: `Release: Prepare the changelog for ${ version }`,
|
||||||
|
body: `This pull request was automatically generated during the code freeze to prepare the changelog for ${ version }`,
|
||||||
|
head: branch,
|
||||||
|
base: releaseBranch,
|
||||||
|
} );
|
||||||
|
Logger.notice( `Pull request created: ${ pullRequest.html_url }` );
|
||||||
|
return {
|
||||||
|
deletionCommitHash: deletionCommitHash.trim(),
|
||||||
|
prNumber: pullRequest.number,
|
||||||
|
};
|
||||||
|
} catch ( e ) {
|
||||||
|
Logger.error( e );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform changelog operations on trunk by submitting a pull request.
|
||||||
|
*
|
||||||
|
* @param {Object} options CLI options
|
||||||
|
* @param {string} tmpRepoPath temp repo path
|
||||||
|
* @param {string} releaseBranch release branch name
|
||||||
|
* @param {Object} releaseBranchChanges update data from updateReleaseBranchChangelogs
|
||||||
|
* @param {Object} releaseBranchChanges.deletionCommitHash commit from the changelog deletions in updateReleaseBranchChangelogs
|
||||||
|
* @param {Object} releaseBranchChanges.prNumber pr number created in updateReleaseBranchChangelogs
|
||||||
|
*/
|
||||||
|
export const updateTrunkChangelog = async (
|
||||||
|
options: Options,
|
||||||
|
tmpRepoPath: string,
|
||||||
|
releaseBranch: string,
|
||||||
|
releaseBranchChanges: { deletionCommitHash: string; prNumber: number }
|
||||||
|
): Promise< void > => {
|
||||||
|
const { owner, name, version } = options;
|
||||||
|
const { deletionCommitHash, prNumber } = releaseBranchChanges;
|
||||||
|
Logger.notice( `Deleting changelogs from trunk ${ tmpRepoPath }` );
|
||||||
|
const git = simpleGit( {
|
||||||
|
baseDir: tmpRepoPath,
|
||||||
|
config: [ 'core.hooksPath=/dev/null' ],
|
||||||
|
} );
|
||||||
|
|
||||||
|
try {
|
||||||
|
await git.checkout( 'trunk' );
|
||||||
|
const branch = `delete/${ version }-changelog`;
|
||||||
|
Logger.notice(
|
||||||
|
`Committing deletions in ${ branch } on ${ tmpRepoPath }`
|
||||||
|
);
|
||||||
|
await git.checkout( {
|
||||||
|
'-b': null,
|
||||||
|
[ branch ]: null,
|
||||||
|
} );
|
||||||
|
await git.raw( [ 'cherry-pick', deletionCommitHash ] );
|
||||||
|
await git.push( 'origin', branch );
|
||||||
|
Logger.notice( `Creating PR for ${ branch }` );
|
||||||
|
const pullRequest = await createPullRequest( {
|
||||||
|
owner,
|
||||||
|
name,
|
||||||
|
title: `Release: Remove ${ version } change files`,
|
||||||
|
body: `This pull request was automatically generated during the code freeze to remove the changefiles from ${ version } that are compiled into the \`${ releaseBranch }\` branch via #${ prNumber }`,
|
||||||
|
head: branch,
|
||||||
|
base: 'trunk',
|
||||||
|
} );
|
||||||
|
Logger.notice( `Pull request created: ${ pullRequest.html_url }` );
|
||||||
|
} catch ( e ) {
|
||||||
|
Logger.error( e );
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
export type Options = {
|
||||||
|
owner: string;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
devRepoPath?: string;
|
||||||
|
};
|
|
@ -10,12 +10,14 @@ import { verifyDayCommand } from './verify-day';
|
||||||
import { milestoneCommand } from './milestone';
|
import { milestoneCommand } from './milestone';
|
||||||
import { branchCommand } from './branch';
|
import { branchCommand } from './branch';
|
||||||
import { versionBumpCommand } from './version-bump';
|
import { versionBumpCommand } from './version-bump';
|
||||||
|
import { changelogCommand } from './changelog';
|
||||||
|
|
||||||
const program = new Command( 'code-freeze' )
|
const program = new Command( 'code-freeze' )
|
||||||
.description( 'Code freeze utilities' )
|
.description( 'Code freeze utilities' )
|
||||||
.addCommand( verifyDayCommand )
|
.addCommand( verifyDayCommand )
|
||||||
.addCommand( milestoneCommand )
|
.addCommand( milestoneCommand )
|
||||||
.addCommand( branchCommand )
|
.addCommand( branchCommand )
|
||||||
.addCommand( versionBumpCommand );
|
.addCommand( versionBumpCommand )
|
||||||
|
.addCommand( changelogCommand );
|
||||||
|
|
||||||
export default program;
|
export default program;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import simpleGit from 'simple-git';
|
||||||
*/
|
*/
|
||||||
import { Logger } from '../../../core/logger';
|
import { Logger } from '../../../core/logger';
|
||||||
import { sparseCheckoutRepoShallow } from '../../../core/git';
|
import { sparseCheckoutRepoShallow } from '../../../core/git';
|
||||||
import { octokitWithAuth } from '../../../core/github/api';
|
import { createPullRequest } from '../../../core/github/repo';
|
||||||
import { getEnvVar } from '../../../core/environment';
|
import { getEnvVar } from '../../../core/environment';
|
||||||
import { getMajorMinor } from '../../../core/version';
|
import { getMajorMinor } from '../../../core/version';
|
||||||
import { bumpFiles } from './bump';
|
import { bumpFiles } from './bump';
|
||||||
|
@ -101,18 +101,16 @@ export const versionBumpCommand = new Command( 'version-bump' )
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Logger.startTask( 'Creating a pull request' );
|
Logger.startTask( 'Creating a pull request' );
|
||||||
const pr = await octokitWithAuth.request(
|
|
||||||
'POST /repos/{owner}/{repo}/pulls',
|
const pullRequest = await createPullRequest( {
|
||||||
{
|
owner,
|
||||||
owner,
|
name,
|
||||||
repo: name,
|
title: `Prep trunk for ${ majorMinor } cycle`,
|
||||||
title: `Prep trunk for ${ majorMinor } cycle`,
|
body: `This PR updates the versions in trunk to ${ version } for next development cycle.`,
|
||||||
body: `This PR updates the versions in trunk to ${ version } for next development cycle.`,
|
head: branch,
|
||||||
head: branch,
|
base,
|
||||||
base,
|
} );
|
||||||
}
|
Logger.notice( `Pull request created: ${ pullRequest.html_url }` );
|
||||||
);
|
|
||||||
Logger.notice( `Pull request created: ${ pr.data.html_url }` );
|
|
||||||
Logger.endTask();
|
Logger.endTask();
|
||||||
} catch ( e ) {
|
} catch ( e ) {
|
||||||
Logger.error( e );
|
Logger.error( e );
|
||||||
|
|
|
@ -10,6 +10,11 @@ import { v4 } from 'uuid';
|
||||||
import { mkdir, rm } from 'fs/promises';
|
import { mkdir, rm } from 'fs/promises';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { getEnvVar } from './environment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get filename from patch
|
* Get filename from patch
|
||||||
*
|
*
|
||||||
|
@ -110,6 +115,29 @@ export const cloneRepoShallow = async ( repoPath: string ) => {
|
||||||
return await cloneRepo( repoPath, { '--depth': 1 } );
|
return await cloneRepo( repoPath, { '--depth': 1 } );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone a repo using the authenticated token `GITHUB_TOKEN`. This allows the script to push branches to origin.
|
||||||
|
*
|
||||||
|
* @param {Object} options CLI options
|
||||||
|
* @param {string} options.owner repo owner
|
||||||
|
* @param {string} options.name repo name
|
||||||
|
* @param {boolean} isShallow whether to do a shallow clone or not.
|
||||||
|
* @return {string} temporary repo path
|
||||||
|
*/
|
||||||
|
export const cloneAuthenticatedRepo = async (
|
||||||
|
options: { owner: string; name: string },
|
||||||
|
isShallow = true
|
||||||
|
): Promise< string > => {
|
||||||
|
const { owner, name } = options;
|
||||||
|
const source = `github.com/${ owner }/${ name }`;
|
||||||
|
const token = getEnvVar( 'GITHUB_TOKEN' );
|
||||||
|
const remote = `https://${ owner }:${ token }@${ source }`;
|
||||||
|
|
||||||
|
return isShallow
|
||||||
|
? await cloneRepoShallow( remote )
|
||||||
|
: await cloneRepo( remote );
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do a minimal sparse checkout of a github repo.
|
* Do a minimal sparse checkout of a github repo.
|
||||||
*
|
*
|
||||||
|
@ -391,3 +419,23 @@ export const generateDiff = async (
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} tmpRepoPath path to temporary repo
|
||||||
|
* @param {string} branch remote branch to checkout
|
||||||
|
*/
|
||||||
|
export const checkoutRemoteBranch = async (
|
||||||
|
tmpRepoPath: string,
|
||||||
|
branch: string
|
||||||
|
): Promise< void > => {
|
||||||
|
const git = simpleGit( {
|
||||||
|
baseDir: tmpRepoPath,
|
||||||
|
config: [ 'core.hooksPath=/dev/null' ],
|
||||||
|
} );
|
||||||
|
|
||||||
|
// 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 ] );
|
||||||
|
await git.raw( [ 'checkout', '-b', branch, `origin/${ branch }` ] );
|
||||||
|
};
|
||||||
|
|
|
@ -4,12 +4,17 @@
|
||||||
import { graphql } from '@octokit/graphql';
|
import { graphql } from '@octokit/graphql';
|
||||||
import { Octokit } from 'octokit';
|
import { Octokit } from 'octokit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { getEnvVar } from '../environment';
|
||||||
|
|
||||||
export const graphqlWithAuth = graphql.defaults( {
|
export const graphqlWithAuth = graphql.defaults( {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${ process.env.GITHUB_TOKEN }`,
|
authorization: `Bearer ${ getEnvVar( 'GITHUB_TOKEN', true ) }`,
|
||||||
},
|
},
|
||||||
} );
|
} );
|
||||||
|
|
||||||
export const octokitWithAuth = new Octokit( {
|
export const octokitWithAuth = new Octokit( {
|
||||||
auth: process.env.GITHUB_TOKEN,
|
auth: getEnvVar( 'GITHUB_TOKEN', true ),
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { Repository } from '@octokit/graphql-schema';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { graphqlWithAuth, octokitWithAuth } from './api';
|
import { graphqlWithAuth, octokitWithAuth } from './api';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
import { PullRequestEndpointResponse } from './types';
|
||||||
|
|
||||||
export const getLatestGithubReleaseVersion = async ( options: {
|
export const getLatestGithubReleaseVersion = async ( options: {
|
||||||
owner?: string;
|
owner?: string;
|
||||||
|
@ -128,3 +130,39 @@ export const deleteGithubBranch = async (
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a pull request from branches on Github.
|
||||||
|
*
|
||||||
|
* @param {Object} options pull request options.
|
||||||
|
* @param {string} options.head branch name containing the changes you want to merge.
|
||||||
|
* @param {string} options.base branch name you want the changes pulled into.
|
||||||
|
* @param {string} options.owner repository owner.
|
||||||
|
* @param {string} options.name repository name.
|
||||||
|
* @param {string} options.title pull request title.
|
||||||
|
* @param {string} options.body pull request body.
|
||||||
|
* @return {Promise<object>} pull request data.
|
||||||
|
*/
|
||||||
|
export const createPullRequest = async ( options: {
|
||||||
|
head: string;
|
||||||
|
base: string;
|
||||||
|
owner: string;
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
} ): Promise< PullRequestEndpointResponse[ 'data' ] > => {
|
||||||
|
const { head, base, owner, name, title, body } = options;
|
||||||
|
const pullRequest = await octokitWithAuth.request(
|
||||||
|
'POST /repos/{owner}/{repo}/pulls',
|
||||||
|
{
|
||||||
|
owner,
|
||||||
|
repo: name,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
head,
|
||||||
|
base,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
//@ts-ignore There is a type mismatch between the graphql schema and the response. pullRequest.data.head.repo.has_discussions is a boolean, but the graphql schema doesn't have that field.
|
||||||
|
return pullRequest.data;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Endpoints } from '@octokit/types';
|
||||||
|
|
||||||
|
export type PullRequestEndpointResponse =
|
||||||
|
Endpoints[ 'POST /repos/{owner}/{repo}/pulls' ][ 'response' ];
|
Loading…
Reference in New Issue