Add PR links to release post template output, and allow blog ID to be specified. (#36026)
* Update release template to be editable * Correct issues in README * Update to allow edits; refactor auth * Update templates to add PR link * Remove commented test code * Update tools/code-analyzer/src/lib/scan-changes.ts Co-authored-by: Sam Seay <samueljseay@gmail.com> * Address typescript issues; prettier * Resolve more typescript issues Co-authored-by: Sam Seay <samueljseay@gmail.com>
This commit is contained in:
parent
d5a679c3f2
commit
f8d8a42fd7
|
@ -20,6 +20,20 @@ export const getFilename = ( str: string ): string => {
|
|||
return str.replace( /^a(.*)\s.*/, '$1' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get starting line number from patch
|
||||
*
|
||||
* @param {string} str String to extract starting line number from.
|
||||
* @return {number} line number.
|
||||
*/
|
||||
export const getStartingLineNumber = ( str: string ): number => {
|
||||
const lineNumber = str.replace( /^@@ -\d+,\d+ \+(\d+),\d+ @@.*?$/, '$1' );
|
||||
if ( ! lineNumber.match( /^\d+$/ ) ) {
|
||||
throw new Error( 'Unable to parse line number from patch' );
|
||||
}
|
||||
return parseInt( lineNumber, 10 );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get patches
|
||||
*
|
||||
|
@ -190,6 +204,79 @@ export const getCommitHash = async ( baseDir: string, ref: string ) => {
|
|||
return ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the commit hash for the last change to a line within a specific file.
|
||||
*
|
||||
* @param {string} baseDir - the dir of the git repo to get the hash from.
|
||||
* @param {string} filePath - the relative path to the file to check the commit hash of.
|
||||
* @param {number} lineNumber - the line number from which to get the hash of the last commit.
|
||||
* @return {string} - the commit hash of the last change to filePath at lineNumber.
|
||||
*/
|
||||
export const getLineCommitHash = async (
|
||||
baseDir: string,
|
||||
filePath: string,
|
||||
lineNumber: number
|
||||
) => {
|
||||
// Remove leading slash, if it exists.
|
||||
const adjustedFilePath = filePath.replace( /^\//, '' );
|
||||
try {
|
||||
const git = await simpleGit( { baseDir } );
|
||||
const blame = await git.raw( [
|
||||
'blame',
|
||||
`-L${ lineNumber },${ lineNumber }`,
|
||||
adjustedFilePath,
|
||||
] );
|
||||
const hash = blame.match( /^([a-f0-9]+)\s+/ );
|
||||
if ( ! hash ) {
|
||||
throw new Error(
|
||||
`Unable to git blame ${ adjustedFilePath }:${ lineNumber }`
|
||||
);
|
||||
}
|
||||
return hash[ 1 ];
|
||||
} catch ( e ) {
|
||||
throw new Error(
|
||||
`Unable to git blame ${ adjustedFilePath }:${ lineNumber }`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the commit hash for the last change to a line within a specific file.
|
||||
*
|
||||
* @param {string} baseDir - the dir of the git repo to get the PR number from.
|
||||
* @param {string} hash - the hash to get the PR number from.
|
||||
* @return {number} - the pull request number from the given inputs.
|
||||
*/
|
||||
export const getPullRequestNumberFromHash = async (
|
||||
baseDir: string,
|
||||
hash: string
|
||||
) => {
|
||||
try {
|
||||
const git = await simpleGit( { baseDir } );
|
||||
const formerHead = await git.revparse( 'HEAD' );
|
||||
await git.checkout( hash );
|
||||
const cmdOutput = await git.raw( [
|
||||
'log',
|
||||
'-1',
|
||||
'--first-parent',
|
||||
'--format=%cI\n%s',
|
||||
] );
|
||||
const cmdLines = cmdOutput.split( '\n' );
|
||||
await git.checkout( formerHead );
|
||||
const prNumber = cmdLines[ 1 ]
|
||||
.trim()
|
||||
.match( /(?:^Merge pull request #(\d+))|(?:\(#(\d+)\)$)/ );
|
||||
if ( prNumber ) {
|
||||
return prNumber[ 1 ]
|
||||
? parseInt( prNumber[ 1 ], 10 )
|
||||
: parseInt( prNumber[ 2 ], 10 );
|
||||
}
|
||||
throw new Error( `Unable to get PR number from hash ${ hash }.` );
|
||||
} catch ( e ) {
|
||||
throw new Error( `Unable to get PR number from hash ${ hash }.` );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* generateDiff generates a diff for a given repo and 2 hashes or branch names.
|
||||
*
|
||||
|
|
|
@ -22,11 +22,19 @@ export const scanForChanges = async (
|
|||
skipSchemaCheck: boolean,
|
||||
source: string,
|
||||
base: string,
|
||||
outputStyle: string
|
||||
outputStyle: string,
|
||||
clonedPath?: string
|
||||
) => {
|
||||
Logger.startTask( `Making temporary clone of ${ source }...` );
|
||||
const tmpRepoPath = await cloneRepo( source );
|
||||
Logger.endTask();
|
||||
|
||||
const tmpRepoPath =
|
||||
typeof clonedPath !== 'undefined'
|
||||
? clonedPath
|
||||
: await cloneRepo( source );
|
||||
|
||||
Logger.notice(
|
||||
`Temporary clone of ${ source } created at ${ tmpRepoPath }`
|
||||
);
|
||||
|
||||
Logger.notice(
|
||||
`Temporary clone of ${ source } created at ${ tmpRepoPath }`
|
||||
|
@ -58,7 +66,11 @@ export const scanForChanges = async (
|
|||
Logger.endTask();
|
||||
|
||||
Logger.startTask( 'Detecting template changes...' );
|
||||
const templateChanges = scanForTemplateChanges( diff, sinceVersion );
|
||||
const templateChanges = await scanForTemplateChanges(
|
||||
diff,
|
||||
sinceVersion,
|
||||
tmpRepoPath
|
||||
);
|
||||
Logger.endTask();
|
||||
|
||||
Logger.startTask( 'Detecting DB changes...' );
|
||||
|
|
|
@ -1,16 +1,28 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getFilename, getPatches } from 'cli-core/src/git';
|
||||
import {
|
||||
getFilename,
|
||||
getStartingLineNumber,
|
||||
getPullRequestNumberFromHash,
|
||||
getPatches,
|
||||
getLineCommitHash,
|
||||
} from 'cli-core/src/git';
|
||||
import { Logger } from 'cli-core/src/logger';
|
||||
|
||||
export type TemplateChangeDescription = {
|
||||
filePath: string;
|
||||
code: string;
|
||||
// We could probably move message out into linter later
|
||||
message: string;
|
||||
pullRequests: number[];
|
||||
};
|
||||
|
||||
export const scanForTemplateChanges = ( content: string, version: string ) => {
|
||||
export const scanForTemplateChanges = async (
|
||||
content: string,
|
||||
version: string,
|
||||
repositoryPath?: string
|
||||
) => {
|
||||
const changes: Map< string, TemplateChangeDescription > = new Map();
|
||||
|
||||
if ( ! content.match( /diff --git a\/(.+)\/templates\/(.+)\.php/g ) ) {
|
||||
|
@ -30,7 +42,9 @@ export const scanForTemplateChanges = ( content: string, version: string ) => {
|
|||
const patch = patches[ p ];
|
||||
const lines = patch.split( '\n' );
|
||||
const filePath = getFilename( lines[ 0 ] );
|
||||
const pullRequests = [];
|
||||
|
||||
let lineNumber = 1;
|
||||
let code = 'warning';
|
||||
let message = 'This template may require a version bump!';
|
||||
|
||||
|
@ -41,9 +55,48 @@ export const scanForTemplateChanges = ( content: string, version: string ) => {
|
|||
code = 'notice';
|
||||
message = 'Version bump found';
|
||||
}
|
||||
|
||||
if ( repositoryPath ) {
|
||||
// Don't parse the headers for the patch.
|
||||
if ( parseInt( l, 10 ) < 4 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( line.match( /^@@/ ) ) {
|
||||
// If we reach a chunk, update the line number, and then continue.
|
||||
lineNumber = getStartingLineNumber( line );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( line.match( /^\+/ ) ) {
|
||||
try {
|
||||
const commitHash = await getLineCommitHash(
|
||||
repositoryPath,
|
||||
filePath,
|
||||
lineNumber
|
||||
);
|
||||
const prNumber = await getPullRequestNumberFromHash(
|
||||
repositoryPath,
|
||||
commitHash
|
||||
);
|
||||
if ( -1 === pullRequests.indexOf( prNumber ) ) {
|
||||
pullRequests.push( prNumber );
|
||||
}
|
||||
} catch ( e: unknown ) {
|
||||
Logger.notice(
|
||||
`Unable to get PR number in ${ filePath }:${ lineNumber }`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// We shouldn't increment line numbers for the a-side of the patch.
|
||||
if ( ! line.match( /^-/ ) ) {
|
||||
lineNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changes.set( filePath, { code, message, filePath } );
|
||||
changes.set( filePath, { code, message, filePath, pullRequests } );
|
||||
}
|
||||
|
||||
return changes;
|
||||
|
|
|
@ -4,7 +4,7 @@ This is a cli tool designed to generate draft release posts for WooCommerce.
|
|||
Posts generated via the tool will be draft posted to https://developer.woocommerce.com.
|
||||
|
||||
You can also generate an HTML representation of the post if you
|
||||
don't have access to a wc.com auth token.
|
||||
don't have access to a WordPress.com auth token.
|
||||
|
||||
### Setup
|
||||
|
||||
|
@ -18,7 +18,7 @@ the `GITHUB_ACCESS_TOKEN` is required. If you need help generating a token see [
|
|||
|
||||
This tool will publish draft posts to `https://developer.woocommerce.com` for you if you omit the `--outputOnly` flag. There is some minimal first time setup for this though:
|
||||
|
||||
1. Create an app on Wordpress.com [here](https://developer.wordpress.com/apps/).
|
||||
1. Create an app on WordPress.com [here](https://developer.wordpress.com/apps/).
|
||||
2. Recommended settings:
|
||||
|
||||
* Name can be anything
|
||||
|
@ -48,7 +48,7 @@ If you can't run anything on your localhost port 3000 you may want to override t
|
|||
Steps:
|
||||
|
||||
1. Add your preferred redirect URI to the `WPCOM_OAUTH_REDIRECT_URI` variable in `.env`. e.g. `http://localhost:4321/oauth`
|
||||
2. When creating your app on [Wordpress.com](https://developer.wordpress.com/apps/) make sure the redirect URL you set matches the one set in `.env`
|
||||
2. When creating your app on [WordPress.com](https://developer.wordpress.com/apps/) make sure the redirect URL you set matches the one set in `.env`
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import semver from 'semver';
|
|||
import { writeFile } from 'fs/promises';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'path';
|
||||
import { cloneRepo, getCommitHash } from 'cli-core/src/git';
|
||||
import { Logger } from 'cli-core/src/logger';
|
||||
import { Command } from '@commander-js/extra-typings';
|
||||
import dotenv from 'dotenv';
|
||||
|
@ -14,11 +15,20 @@ import dotenv from 'dotenv';
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { renderTemplate } from '../../lib/render-template';
|
||||
import { createWpComDraftPost } from '../../lib/draft-post';
|
||||
import {
|
||||
createWpComDraftPost,
|
||||
fetchWpComPost,
|
||||
editWpComPostContent,
|
||||
} from '../../lib/draft-post';
|
||||
import { getWordpressComAuthToken } from '../../lib/oauth-helper';
|
||||
import { getEnvVar } from '../../lib/environment';
|
||||
import { generateContributors } from '../../lib/contributors';
|
||||
import { editPostHTML } from '../../lib/edit-post';
|
||||
|
||||
const DEVELOPER_WOOCOMMERCE_SITE_ID = '96396764';
|
||||
|
||||
const SOURCE_REPO = 'https://github.com/woocommerce/woocommerce.git';
|
||||
|
||||
const VERSION_VALIDATION_REGEX =
|
||||
/^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/;
|
||||
|
||||
|
@ -30,117 +40,222 @@ const program = new Command()
|
|||
.description( 'CLI to automate generation of a release post.' )
|
||||
.argument(
|
||||
'<currentVersion>',
|
||||
'The version of the plugin to generate a post for, please use the tag version from Github.'
|
||||
'The current version in x.y.z or x.y.z-stage.n format. Ex: 7.1.0, 7.1.0-rc.1'
|
||||
)
|
||||
.argument(
|
||||
'<previousVersion>',
|
||||
'The previous version in x.y.z format. Ex: 7.0.0'
|
||||
)
|
||||
.option( '--outputOnly', 'Only output the post, do not publish it' )
|
||||
.option(
|
||||
'--previousVersion <previousVersion>',
|
||||
'If you would like to compare against a version other than last minor you can provide a tag version from Github.'
|
||||
)
|
||||
.option( '--editPostId <postId>', 'Updates an existing post' )
|
||||
.option(
|
||||
'--tags <tags>',
|
||||
'Comma separated list of tags to add to the post.',
|
||||
'Releases,WooCommerce Core'
|
||||
)
|
||||
.action( async ( currentVersion, options ) => {
|
||||
const tags = options.tags.split( ',' ).map( ( tag ) => tag.trim() );
|
||||
.option(
|
||||
'--siteId <siteId>',
|
||||
'For posting to a non-default site (for testing)'
|
||||
)
|
||||
.action( async ( currentVersion, previousVersion, options ) => {
|
||||
const siteId = options.siteId || DEVELOPER_WOOCOMMERCE_SITE_ID;
|
||||
const tags = ( options.tags &&
|
||||
options.tags.split( ',' ).map( ( tag ) => tag.trim() ) ) || [
|
||||
'WooCommerce Core',
|
||||
'Releases',
|
||||
];
|
||||
const isOutputOnly = !! options.outputOnly;
|
||||
|
||||
const previousVersion = options.previousVersion
|
||||
? semver.parse( options.previousVersion )
|
||||
: semver.parse( currentVersion );
|
||||
|
||||
if ( ! options.previousVersion && previousVersion ) {
|
||||
// e.g 6.8.0 -> 6.7.0
|
||||
previousVersion.major =
|
||||
previousVersion.minor === 0
|
||||
? previousVersion.major - 1
|
||||
: previousVersion.major;
|
||||
|
||||
previousVersion.minor =
|
||||
previousVersion.minor === 0 ? 9 : previousVersion.minor - 1;
|
||||
|
||||
previousVersion.format();
|
||||
if ( ! VERSION_VALIDATION_REGEX.test( currentVersion ) ) {
|
||||
throw new Error(
|
||||
`Invalid current version: ${ currentVersion }. Provide current version in x.y.z or x.y.z-stage.n format.`
|
||||
);
|
||||
}
|
||||
|
||||
if ( previousVersion && previousVersion.major ) {
|
||||
const isOutputOnly = !! options.outputOnly;
|
||||
if ( ! VERSION_VALIDATION_REGEX.test( previousVersion ) ) {
|
||||
throw new Error(
|
||||
`Invalid previous version: ${ previousVersion }. Provide previous version in x.y.z format.`
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! VERSION_VALIDATION_REGEX.test( previousVersion.raw ) ) {
|
||||
const clientId = getEnvVar( 'WPCOM_OAUTH_CLIENT_ID', true );
|
||||
const clientSecret = getEnvVar( 'WPCOM_OAUTH_CLIENT_SECRET', true );
|
||||
const redirectUri =
|
||||
getEnvVar( 'WPCOM_OAUTH_REDIRECT_URI' ) ||
|
||||
'http://localhost:3000/oauth';
|
||||
const authToken =
|
||||
isOutputOnly ||
|
||||
( await getWordpressComAuthToken(
|
||||
clientId,
|
||||
clientSecret,
|
||||
siteId,
|
||||
redirectUri,
|
||||
'posts'
|
||||
) );
|
||||
|
||||
if ( ! authToken ) {
|
||||
throw new Error(
|
||||
'Error getting auth token, check your env settings are correct.'
|
||||
);
|
||||
}
|
||||
|
||||
Logger.startTask( `Making temporary clone of ${ SOURCE_REPO }...` );
|
||||
const currentParsed = semver.parse( currentVersion );
|
||||
const previousParsed = semver.parse( previousVersion );
|
||||
const tmpRepoPath = await cloneRepo( SOURCE_REPO );
|
||||
Logger.endTask();
|
||||
let currentBranch;
|
||||
let previousBranch;
|
||||
let currentVersionRef;
|
||||
let previousVersionRef;
|
||||
|
||||
try {
|
||||
if ( ! currentParsed ) {
|
||||
throw new Error( 'Unable to parse current version' );
|
||||
}
|
||||
currentBranch = `release/${ currentParsed.major }.${ currentParsed.minor }`;
|
||||
currentVersionRef = await getCommitHash(
|
||||
tmpRepoPath,
|
||||
`remotes/origin/${ currentBranch }`
|
||||
);
|
||||
} catch ( error: unknown ) {
|
||||
Logger.notice(
|
||||
`Unable to find '${ currentBranch }', using 'trunk'.`
|
||||
);
|
||||
currentBranch = 'trunk';
|
||||
currentVersionRef = await getCommitHash(
|
||||
tmpRepoPath,
|
||||
'remotes/origin/trunk'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if ( ! previousParsed ) {
|
||||
throw new Error( 'Unable to parse previous version' );
|
||||
}
|
||||
previousBranch = `release/${ previousParsed.major }.${ previousParsed.minor }`;
|
||||
previousVersionRef = await getCommitHash(
|
||||
tmpRepoPath,
|
||||
`remotes/origin/${ previousBranch }`
|
||||
);
|
||||
} catch ( error: unknown ) {
|
||||
throw new Error(
|
||||
`Unable to find '${ previousBranch }'. Branch for previous version must exist.`
|
||||
);
|
||||
}
|
||||
|
||||
Logger.notice(
|
||||
`Using ${ currentBranch }(${ currentVersionRef }) for current and ${ previousBranch }(${ previousVersionRef }) for previous.`
|
||||
);
|
||||
|
||||
let postContent;
|
||||
|
||||
if ( 'undefined' !== typeof options.editPostId ) {
|
||||
try {
|
||||
const prevPost = await fetchWpComPost(
|
||||
siteId,
|
||||
options.editPostId,
|
||||
authToken
|
||||
);
|
||||
postContent = prevPost.content;
|
||||
} catch ( error: unknown ) {
|
||||
throw new Error(
|
||||
`Invalid previous version: ${ previousVersion.raw }`
|
||||
);
|
||||
}
|
||||
if ( ! VERSION_VALIDATION_REGEX.test( currentVersion ) ) {
|
||||
throw new Error(
|
||||
`Invalid current version: ${ currentVersion }`
|
||||
`Unable to fetch existing post with ID: ${ options.editPostId }`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const changes = await scanForChanges(
|
||||
currentVersion,
|
||||
currentVersion,
|
||||
false,
|
||||
'https://github.com/woocommerce/woocommerce.git',
|
||||
previousVersion.toString(),
|
||||
'cli'
|
||||
const changes = await scanForChanges(
|
||||
currentVersionRef,
|
||||
`${ previousParsed.major }.${ previousParsed.minor }.${ previousParsed.patch }`,
|
||||
//false,
|
||||
true,
|
||||
SOURCE_REPO,
|
||||
previousVersionRef,
|
||||
'cli',
|
||||
tmpRepoPath
|
||||
);
|
||||
|
||||
const schemaChanges = changes.schema.filter( ( s ) => ! s.areEqual );
|
||||
|
||||
Logger.startTask( 'Finding contributors' );
|
||||
const title = `WooCommerce ${ currentVersion } Released`;
|
||||
|
||||
const contributors = await generateContributors(
|
||||
currentVersion,
|
||||
previousVersion.toString()
|
||||
);
|
||||
|
||||
const postVariables = {
|
||||
contributors,
|
||||
title,
|
||||
changes: {
|
||||
...changes,
|
||||
schema: schemaChanges,
|
||||
},
|
||||
displayVersion: currentVersion,
|
||||
};
|
||||
|
||||
const html =
|
||||
'undefined' !== typeof options.editPostId
|
||||
? editPostHTML( postContent, {
|
||||
hooks: await renderTemplate(
|
||||
'hooks.ejs',
|
||||
postVariables
|
||||
),
|
||||
database: await renderTemplate(
|
||||
'database.ejs',
|
||||
postVariables
|
||||
),
|
||||
templates: await renderTemplate(
|
||||
'templates.ejs',
|
||||
postVariables
|
||||
),
|
||||
contributors: await renderTemplate(
|
||||
'contributors.ejs',
|
||||
postVariables
|
||||
),
|
||||
} )
|
||||
: await renderTemplate( 'release.ejs', postVariables );
|
||||
|
||||
Logger.endTask();
|
||||
|
||||
if ( isOutputOnly ) {
|
||||
const tmpFile = join(
|
||||
tmpdir(),
|
||||
`release-${ currentVersion }.html`
|
||||
);
|
||||
|
||||
const schemaChanges = changes.schema.filter(
|
||||
( s ) => ! s.areEqual
|
||||
);
|
||||
await writeFile( tmpFile, html );
|
||||
|
||||
Logger.startTask( 'Finding contributors' );
|
||||
const title = `WooCommerce ${ currentVersion } Released`;
|
||||
Logger.notice( `Output written to ${ tmpFile }` );
|
||||
} else {
|
||||
Logger.startTask( 'Publishing draft release post' );
|
||||
|
||||
const contributors = await generateContributors(
|
||||
currentVersion,
|
||||
previousVersion.toString()
|
||||
);
|
||||
try {
|
||||
const { URL } =
|
||||
'undefined' !== typeof options.editPostId
|
||||
? await editWpComPostContent(
|
||||
siteId,
|
||||
options.editPostId,
|
||||
html,
|
||||
authToken
|
||||
)
|
||||
: await createWpComDraftPost(
|
||||
siteId,
|
||||
title,
|
||||
html,
|
||||
tags,
|
||||
authToken
|
||||
);
|
||||
|
||||
const html = await renderTemplate( 'release.ejs', {
|
||||
contributors,
|
||||
title,
|
||||
changes: {
|
||||
...changes,
|
||||
schema: schemaChanges,
|
||||
},
|
||||
displayVersion: currentVersion,
|
||||
} );
|
||||
|
||||
Logger.endTask();
|
||||
|
||||
if ( isOutputOnly ) {
|
||||
const tmpFile = join(
|
||||
tmpdir(),
|
||||
`release-${ currentVersion }.html`
|
||||
);
|
||||
|
||||
await writeFile( tmpFile, html );
|
||||
|
||||
Logger.notice( `Output written to ${ tmpFile }` );
|
||||
} else {
|
||||
Logger.startTask( 'Publishing draft release post' );
|
||||
|
||||
try {
|
||||
const { URL } = await createWpComDraftPost(
|
||||
DEVELOPER_WOOCOMMERCE_SITE_ID,
|
||||
title,
|
||||
html,
|
||||
tags
|
||||
);
|
||||
|
||||
Logger.notice( `Published draft release post at ${ URL }` );
|
||||
Logger.endTask();
|
||||
} catch ( error: unknown ) {
|
||||
if ( error instanceof Error ) {
|
||||
Logger.error( error.message );
|
||||
}
|
||||
Logger.notice( `Published draft release post at ${ URL }` );
|
||||
Logger.endTask();
|
||||
} catch ( error: unknown ) {
|
||||
if ( error instanceof Error ) {
|
||||
Logger.error( error.message );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`Could not find previous version for ${ currentVersion }`
|
||||
);
|
||||
}
|
||||
} );
|
||||
|
||||
|
|
|
@ -5,10 +5,84 @@ import fetch from 'node-fetch';
|
|||
import { Logger } from 'cli-core/src/logger';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
* Fetch a post from WordPress.com
|
||||
*
|
||||
* @param {string} siteId - The site to fetch from.
|
||||
* @param {string} postId - The id of the post to fetch.
|
||||
* @param {string} authToken - WordPress.com auth token.
|
||||
* @return {Promise} - A promise that resolves to the JSON API response.
|
||||
*/
|
||||
import { getWordpressComAuthToken } from './oauth-helper';
|
||||
import { getEnvVar } from './environment';
|
||||
export const fetchWpComPost = async (
|
||||
siteId: string,
|
||||
postId: string,
|
||||
authToken: string
|
||||
) => {
|
||||
try {
|
||||
const post = await fetch(
|
||||
`https://public-api.wordpress.com/rest/v1.1/sites/${ siteId }/posts/${ postId }`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${ authToken }`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if ( post.status !== 200 ) {
|
||||
const text = await post.text();
|
||||
throw new Error( `Error creating draft post: ${ text }` );
|
||||
}
|
||||
|
||||
return post.json();
|
||||
} catch ( e: unknown ) {
|
||||
if ( e instanceof Error ) {
|
||||
Logger.error( e.message );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Edit a post on wordpress.com
|
||||
*
|
||||
* @param {string} siteId - The site to post to.
|
||||
* @param {string} postId - The post to edit.
|
||||
* @param {string} postContent - Post content.
|
||||
* @param {string} authToken - WordPress.com auth token.
|
||||
* @return {Promise} - A promise that resolves to the JSON API response.
|
||||
*/
|
||||
export const editWpComPostContent = async (
|
||||
siteId: string,
|
||||
postId: string,
|
||||
postContent: string,
|
||||
authToken: string
|
||||
) => {
|
||||
try {
|
||||
const post = await fetch(
|
||||
`https://public-api.wordpress.com/rest/v1.2/sites/${ siteId }/posts/${ postId }`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${ authToken }`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify( {
|
||||
content: postContent,
|
||||
} ),
|
||||
}
|
||||
);
|
||||
|
||||
if ( post.status !== 200 ) {
|
||||
const text = await post.text();
|
||||
throw new Error( `Error creating draft post: ${ text }` );
|
||||
}
|
||||
|
||||
return post.json();
|
||||
} catch ( e: unknown ) {
|
||||
if ( e instanceof Error ) {
|
||||
Logger.error( e.message );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a draft of a post on wordpress.com
|
||||
|
@ -16,35 +90,17 @@ import { getEnvVar } from './environment';
|
|||
* @param {string} siteId - The site to post to.
|
||||
* @param {string} postTitle - Post title.
|
||||
* @param {string} postContent - Post content.
|
||||
* @param {string} authToken - WordPress.com auth token.
|
||||
* @return {Promise} - A promise that resolves to the JSON API response.
|
||||
*/
|
||||
export const createWpComDraftPost = async (
|
||||
siteId: string,
|
||||
postTitle: string,
|
||||
postContent: string,
|
||||
tags: string[]
|
||||
tags: string[],
|
||||
authToken: string
|
||||
) => {
|
||||
const clientId = getEnvVar( 'WPCOM_OAUTH_CLIENT_ID', true );
|
||||
const clientSecret = getEnvVar( 'WPCOM_OAUTH_CLIENT_SECRET', true );
|
||||
const redirectUri =
|
||||
getEnvVar( 'WPCOM_OAUTH_REDIRECT_URI' ) ||
|
||||
'http://localhost:3000/oauth';
|
||||
|
||||
try {
|
||||
const authToken = await getWordpressComAuthToken(
|
||||
clientId,
|
||||
clientSecret,
|
||||
siteId,
|
||||
redirectUri,
|
||||
'posts'
|
||||
);
|
||||
|
||||
if ( ! authToken ) {
|
||||
throw new Error(
|
||||
'Error getting auth token, check your env settings are correct.'
|
||||
);
|
||||
}
|
||||
|
||||
const post = await fetch(
|
||||
`https://public-api.wordpress.com/rest/v1.2/sites/${ siteId }/posts/new`,
|
||||
{
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
export type EditPostVariables = {
|
||||
hooks?: string;
|
||||
database?: string;
|
||||
templates?: string;
|
||||
contributors?: string;
|
||||
};
|
||||
|
||||
export const editPostHTML = ( postContent: string, postVariables: EditPostVariables ) => {
|
||||
return postContent.replaceAll( /<!-- release:([a-z]+) -->.*?<!-- \/release:\1 -->/gm, ( match, key: string ) => {
|
||||
return `<!-- release:${ key } -->${ postVariables[ key as keyof EditPostVariables ] || '' }<!-- /release:${ key } -->`;
|
||||
} );
|
||||
};
|
|
@ -49,9 +49,9 @@
|
|||
<!-- /wp:paragraph -->
|
||||
|
||||
|
||||
<%- include( 'hooks' ) %>
|
||||
<%- include( 'database') %>
|
||||
<%- include( 'templates' ) %>
|
||||
<!-- release:hooks --><%- include( 'hooks' ) %><!-- /release:hooks -->
|
||||
<!-- release:database --><%- include( 'database' ) %><!-- /release:database -->
|
||||
<!-- release:templates --><%- include( 'templates' ) %><!-- /release:templates -->
|
||||
|
||||
<!-- wp:heading -->
|
||||
<h2 id="deprecations">Deprecations</h2>
|
||||
|
@ -60,5 +60,5 @@
|
|||
<p>There are no deprecations in this release.</p>
|
||||
<!-- /wp:paragraph -->
|
||||
|
||||
<%- include('contributors') %>
|
||||
<!-- release:contributors--><%- include('contributors') %><!-- /release:contributors -->
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<th>
|
||||
<strong>Template</strong>
|
||||
</th>
|
||||
<th>
|
||||
<strong>GitHub Link</strong>
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
@ -17,6 +20,11 @@
|
|||
<% changes.templates.forEach((change) => { %>
|
||||
<tr>
|
||||
<td><%= change.filePath %></td>
|
||||
<td>
|
||||
<% change.pullRequests.forEach( ( pullRequest ) => { %>
|
||||
<a href="https://github.com/woocommerce/woocommerce/pull/<%= pullRequest %>">#<%= pullRequest %></a>
|
||||
<% }) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
|
|
Loading…
Reference in New Issue