Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CI: use build_meta.json instead of build_meta.txt. #73747

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 18 additions & 3 deletions .teamcity/_self/projects/WPComPlugins.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ object CalypsoApps: BuildType({

# Install dependencies
yarn

# Set execution permission for additional scripts.
chmod +x .teamcity/scripts/WPComPlugins/
"""
}

Expand All @@ -106,6 +103,24 @@ object CalypsoApps: BuildType({
yarn workspaces foreach --verbose --parallel --include '{happy-blocks,@automattic/notifications}' run build-ci
"""
}

bashNodeScript {
name = "Process artifact"
scriptContent = """
export tc_auth="%system.teamcity.auth.userId%:%system.teamcity.auth.password%"
export git_branch="%teamcity.build.branch%"
export build_id="%teamcity.build.id%"
export GH_TOKEN="%matticbot_oauth_token%"
export is_default_branch="%teamcity.build.branch.is_default%"
export skip_build_diff="%skip_release_diff%"
export mc_auth_secret="%mc_auth_secret%"
export commit_sha="%build.vcs.number%"
export mc_post_root="%mc_post_root%"
export tc_sever_url="%teamcity.serverUrl%"

node ./bin/process-calypso-app-artifacts.mjs
"""
}
}
})

Expand Down
2 changes: 2 additions & 0 deletions apps/editing-toolkit/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

const path = require( 'path' );
const BuildMetaPlugin = require( '@automattic/calypso-apps-builder/build-meta-webpack-plugin.cjs' );
const getBaseWebpackConfig = require( '@automattic/calypso-build/webpack.config.js' );
const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
const ReadableJsAssetsWebpackPlugin = require( '@wordpress/readable-js-assets-webpack-plugin' );
Expand Down Expand Up @@ -79,6 +80,7 @@ function getWebpackConfig( env = { source: '' }, argv = {} ) {
concatenateModules: false,
},
plugins: [
BuildMetaPlugin( { outputPath } ),
...webpackConfig.plugins.filter(
( plugin ) => plugin.constructor.name !== 'DependencyExtractionWebpackPlugin'
),
Expand Down
11 changes: 6 additions & 5 deletions apps/happy-blocks/bin/prepArtifactForCI.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/bin/bash

set -x
set -o errexit
set -o nounset
set -o pipefail

# Copy dist files to the release directory.
mkdir ../release-files
cp -r ../dist ../release-files/dist/
mkdir ./release-files
cp -r ./dist ./release-files/dist/

# Add the index.php file
cp ../index.php ../release-files/
cp ./index.php ./release-files/

printf "Finished configuration of the @automattic/happy-blocks plugin artifacts directory."
11 changes: 9 additions & 2 deletions apps/happy-blocks/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/**
*WARNING: No ES6 modules here. Not transpiled! ****
*/

const path = require( 'path' );
const BuildMetaPlugin = require( '@automattic/calypso-apps-builder/build-meta-webpack-plugin.cjs' );
const getBaseWebpackConfig = require( '@automattic/calypso-build/webpack.config.js' );
const ReadableJsAssetsWebpackPlugin = require( '@wordpress/readable-js-assets-webpack-plugin' );

const outputPath = path.join( __dirname, 'release-files' );

function getWebpackConfig( env, argv ) {
const webpackConfig = getBaseWebpackConfig( { ...env, WP: true }, argv );

Expand All @@ -18,7 +21,11 @@ function getWebpackConfig( env, argv ) {
...webpackConfig.output,
filename: '[name].min.js',
},
plugins: [ ...webpackConfig.plugins, new ReadableJsAssetsWebpackPlugin() ],
plugins: [
...webpackConfig.plugins,
BuildMetaPlugin( { outputPath } ),
new ReadableJsAssetsWebpackPlugin(),
],
};
}

Expand Down
2 changes: 1 addition & 1 deletion apps/notifications/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"start": "yarn run clean && yarn run build:notifications && yarn run dev-server",
"dev": "yarn run calypso-apps-builder --localPath dist --remotePath /home/wpcom/public_html/widgets.wp.com/notifications",
"build": "NODE_ENV=production yarn dev",
"build-ci": "yarn build && ./bin/prepArtifactForCI.sh"
"build-ci": "yarn build"
},
"dependencies": {
"@automattic/calypso-color-schemes": "workspace:^",
Expand Down
10 changes: 9 additions & 1 deletion apps/o2-blocks/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
*WARNING: No ES6 modules here. Not transpiled! ****
*/

const path = require( 'path' );
const BuildMetaPlugin = require( '@automattic/calypso-apps-builder/build-meta-webpack-plugin.cjs' );
const getBaseWebpackConfig = require( '@automattic/calypso-build/webpack.config.js' );
const ReadableJsAssetsWebpackPlugin = require( '@wordpress/readable-js-assets-webpack-plugin' );

const outputPath = path.join( __dirname, 'release-files' );

function getWebpackConfig( env, argv ) {
const webpackConfig = getBaseWebpackConfig( { ...env, WP: true }, argv );

Expand All @@ -18,7 +22,11 @@ function getWebpackConfig( env, argv ) {
...webpackConfig.output,
filename: '[name].min.js',
},
plugins: [ ...webpackConfig.plugins, new ReadableJsAssetsWebpackPlugin() ],
plugins: [
...webpackConfig.plugins,
BuildMetaPlugin( { outputPath } ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we base this on webpackConfig.output instead of having a separate variable? (same for happy-blocks)

new ReadableJsAssetsWebpackPlugin(),
],
};
}

Expand Down
45 changes: 45 additions & 0 deletions bin/did-calypso-app-change.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { exec as _exec } from 'node:child_process';
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { finished } from 'node:stream/promises';
import util from 'node:util';
const exec = util.promisify( _exec );

export default async function didCalypsoAppChange( { slug, dir, newReleaseDir, customNormalize } ) {
await downloadPrevBuild( slug, dir );
await customNormalize?.();
try {
await exec(
`diff -rq --exclude="*.js.map" --exclude="*.asset.php" --exclude="build_meta.json" --exclude="README.md" ${ newReleaseDir } ${ dir }/prev-release/`,
{ encoding: 'UTF-8', cwd: dir, stdio: 'inherit' }
);
return false;
} catch ( { code, stdout, stderr } ) {
if ( code === 1 ) {
console.info( `The build for ${ slug } changed. Cause:` );
console.info( stdout );
return true;
}
throw new Error( `Unexpected error code ${ code } while diffing ${ slug } build: ${ stderr }` );
}
}

async function downloadPrevBuild( appSlug, dir ) {
const prevBuildZip = `${ dir }/prev-archive-download.zip`;
const stream = createWriteStream( prevBuildZip );

const prevBuildUrl = `${ process.env.tc_sever_url }/repository/download/calypso_calypso_WPComPlugins_Build_Plugins/${ appSlug }-release-build.tcbuildtag/${ appSlug }.zip?guest=1&branch=try-parallel-app-processing`;
console.info( `Fetching previous release build for ${ appSlug } from ${ prevBuildUrl }` );

const { body, status } = await fetch( prevBuildUrl );
if ( status !== 200 ) {
throw new Error( `Could not fetch previous build! Response code ${ status }.` );
}

console.info( `Extracting downloaded archive for ${ appSlug }...` );
await finished( Readable.fromWeb( body ).pipe( stream ) );
await exec( `unzip -q ${ prevBuildZip } -d ${ dir }/prev-release`, {
encoding: 'UTF-8',
stdio: 'inherit',
} );
}
165 changes: 165 additions & 0 deletions bin/process-calypso-app-artifacts.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { exec as _exec } from 'node:child_process';
import { createHmac } from 'node:crypto';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import util from 'node:util';
import didCalypsoAppChange from './did-calypso-app-change.mjs';

checkEnvVars();

const exec = util.promisify( _exec );
const SKIP_BUILD_DIFF = process.env.skip_build_diff === 'true';
const IS_DEFAULT_BRANCH = process.env.is_default_branch === 'true';
const dirname = fileURLToPath( new URL( '.', import.meta.url ) );
const appRoot = path.resolve( dirname, '../apps' );

const apps = [
{
slug: 'happy-blocks',
dir: path.resolve( appRoot, 'happy-blocks' ),
newReleaseDir: path.resolve( appRoot, 'happy-blocks/release-files' ),
slackNotify: true,
},
];

// STEP 1: Check if any apps have changed. If skipping the diff, continue as if all apps have changed.
const changedApps = SKIP_BUILD_DIFF
? apps
: (
await Promise.all(
apps.map( async ( app ) => ( ( await didCalypsoAppChange( app ) ) ? app : null ) )
)
).filter( Boolean );

if ( changedApps.length ) {
console.info(
'The following apps changed: ',
changedApps.map( ( { slug } ) => slug ).join( ', ' )
);
} else {
console.info( 'No apps changed.' );
}

// STEP 2: Tag the build in TeamCity. This will let future builds identify the previous release.
const finalTasks = [];
if ( changedApps.length ) {
console.info( 'Tagging build...' );
finalTasks.push( tagBuild( changedApps ) );
}

// STEP 3: Notify the author. On trunk, send a Slack notification. On a PR, a GitHub commnent.
if ( ! IS_DEFAULT_BRANCH ) {
console.info( 'Running GitHub comment...' );
finalTasks.push( addGitHubComment( changedApps ) );
} else {
console.info( 'Running Slack notification...' );
finalTasks.push( sendSlackNotification( changedApps ) );
}

await Promise.all( finalTasks );
console.log( 'Success!' );

async function tagBuild( _changedApps ) {
const tags = _changedApps.map( ( app ) => `${ app.slug }-release-build` );

const tagurl = `https://teamcity.a8c.com/httpAuth/app/rest/builds/id:${ process.env.build_id }/tags/`;
console.info( `Adding tags (${ tags }) to current build at URL ${ tagurl }` );

const jsonTags = JSON.stringify( {
count: tags.length,
tag: tags.map( ( tag ) => ( {
name: tag,
} ) ),
} );

const res = await fetch( tagurl, {
method: 'POST',
headers: new Headers( {
'Content-Type': 'application/json',
Authorization: `Basic ${ Buffer.from( process.env.tc_auth ).toString( 'base64' ) }`,
} ),
body: jsonTags,
} );
if ( res.status !== 200 ) {
console.error( 'Tagging the build failed!' );
}
}

async function addGitHubComment( _changedApps ) {
const notifyApps = _changedApps.filter( ( { ghNotify = true } ) => ghNotify );

const commentWatermark = 'calypso-app-artifacts';
const ghCommentCmd = `./bin/add-pr-comment.sh ${ process.env.git_branch } ${ commentWatermark }`;

if ( ! notifyApps.length ) {
console.info( 'No apps to notify about. Deleting existing comment if exists.' );
// Delete the existing comment, since there are no apps to notify about.
return await exec( `${ ghCommentCmd } delete <<< "" || true`, {
encoding: 'UTF-8',
stdio: 'inherit',
} );
}

const header = '**This PR modifies the release build for the following Calypso Apps:**';
const docsMsg = '_For info about this notification, see here: PCYsg-OT6-p2_';
const changedAppsMsg = notifyApps.map( ( { slug } ) => `* ${ slug }` ).join( '\n' );
const testMsg = `To test WordPress.com changes, run "install-plugin.sh $pluginSlug ${ process.env.git_branch }" on your sandbox.`;

const appMsg = `${ header }\n\n${ docsMsg }\n\n${ changedAppsMsg }\n\n${ testMsg }`;

await exec( `${ ghCommentCmd } <<- EOF || true\n${ appMsg }\nEOF`, {
encoding: 'UTF-8',
} );
}

async function sendSlackNotification( _changedApps ) {
const notifyApps = _changedApps.filter( ( { slackNotify = false } ) => slackNotify );

if ( ! notifyApps.length ) {
console.info( 'No apps to notify about. Skipping Slack notification.' );
return;
}

// TODO: move from one to multiple plugins!
const body = `commit=${ process.env.commit_sha }&plugin=$pluginSlug`;

const signature = createHmac( 'sha256', process.env.mc_auth_secret )
.update( body )
.digest( 'hex' );

console.log( `Sending data to slack endpoint: ${ body }` );
const res = await fetch( `${ process.env.mc_post_root }?plugin-deploy-reminder`, {
method: 'POST',
headers: new Headers( {
'Content-Type': 'application/x-www-form-urlencoded',
'TEAMCITY-SIGNATURE': signature,
} ),
body,
} );
if ( res.status !== 200 ) {
console.error( 'Slack notification failed!' );
console.error( 'Details: ', await res.text() );
}
}

function checkEnvVars() {
const requiredVars = [
'tc_auth',
'git_branch',
'build_id',
'GH_TOKEN',
'is_default_branch',
'skip_build_diff',
'mc_auth_secret',
'commit_sha',
'mc_post_root',
'tc_sever_url',
];

// Undefined and empty strings will be detected.
const missingVars = requiredVars.filter( ( varName ) => ! process.env[ varName ] );
if ( missingVars.length > 0 ) {
console.error( `Missing required environment variables: ${ missingVars.join( ', ' ) }` );
process.exit( 1 );
}
}