Skip to content

Commit

Permalink
Add the backport-js-packages task that updates all the JavaScript pac…
Browse files Browse the repository at this point in the history
…kages to their required versions and runs the build process
  • Loading branch information
adamziel committed Apr 22, 2022
1 parent 14d9ca8 commit 58aaa15
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 1 deletion.
19 changes: 19 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/* globals Set */
var webpackConfig = require( './webpack.config' );
var installChanged = require( 'install-changed' );
const { spawnSync: spawn } = require( "child_process" );

module.exports = function(grunt) {
var path = require('path'),
Expand Down Expand Up @@ -1216,6 +1217,13 @@ module.exports = function(grunt) {
'qunit:compiled'
] );

grunt.registerTask( 'backport-js-packages', [
'packages:update',
'packages:refresh-deps',
'build:dev',
'build'
] );

grunt.renameTask( 'watch', '_watch' );

grunt.registerTask( 'watch', function() {
Expand Down Expand Up @@ -1637,6 +1645,17 @@ module.exports = function(grunt) {
} );
} );

grunt.registerTask( 'packages:update', 'Update WordPress packages', function() {
const distTag = grunt.option('dist-tag') || 'latest';
grunt.log.writeln( `Updating WordPress packages (--dist-tag=${distTag})` );
spawn( 'npm', [ 'wp-packages-update', `--dist-tag=${distTag}` ] );
} );
grunt.registerTask( 'packages:refresh-deps', 'Update version of dependencies in package.json to match the ones listed in the latest WordPress packages', function() {
const distTag = grunt.option('dist-tag') || 'latest';
grunt.log.writeln( `Updating versions of dependencies listed in package.json (--dist-tag=${distTag})` );
spawn( 'npm', [ 'wp-packages-refresh-deps', `--dist-tag=${distTag}` ] );
} );

// Patch task.
grunt.renameTask('patch_wordpress', 'patch');

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@
"test:php": "node ./tools/local-env/scripts/docker.js run -T php composer update -W && node ./tools/local-env/scripts/docker.js run php ./vendor/bin/phpunit",
"test:e2e": "node ./tests/e2e/run-tests.js",
"test:visual": "node ./tests/visual-regression/run-tests.js",
"wp-packages-update": "wp-scripts packages-update"
"wp-packages-update": "wp-scripts packages-update",
"wp-packages-refresh-deps": "node ./refresh-packages.js",
"wp-backport-js-packages": "grunt backport-js-packages"
}
}
175 changes: 175 additions & 0 deletions tools/release/refresh-packages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/* eslint-disable no-console */
/**
* External dependencies
*/
const fs = require( 'fs' );
const spawn = require( 'cross-spawn' );
const { zip, uniq, identity, groupBy } = require( 'lodash' );

/**
* Constants
*/
const WORDPRESS_PACKAGES_PREFIX = '@wordpress/';
const rootDir = __dirname + '/../..';
const { getArgFromCLI } = require( `${ rootDir }/node_modules/@wordpress/scripts/utils` );
const distTag = getArgFromCLI( '--dist-tag' ) || 'latest';

/**
* The main function of this task.
*
* It installs any missing WordPress packages, and updates the
* mismatched dependencies versions, e.g. it would detect that Gutenberg
* updated react from 16.0.4 to 17.0.2 and install the latter.
*/
function refreshDependencies() {
const initialPackageJSON = readJSONFile( `${ rootDir }/package.json` );

// Install any missing WordPress packages:
const missingWordPressPackages = getMissingWordPressPackages();
if ( missingWordPressPackages.length ) {
console.log( "The following @wordpress dependencies are missing: " );
console.log( missingWordPressPackages );
console.log( "Installing via npm..." );
installPackages( missingWordPressPackages.map( name => [name, distTag] ) );
}

// Update any outdated non-WordPress packages:
const versionMismatches = getMismatchedNonWordPressDependencies();
if ( versionMismatches.length ) {
console.log( "The following dependencies are outdated: " );
console.log( versionMismatches );
console.log( "Updating via npm..." );
const requiredPackages = versionMismatches.map( ( { name, required } ) => [name, required] );
installPackages( requiredPackages );
}

const finalPackageJSON = readJSONFile( `${ rootDir }/package.json` );
outputPackageDiffReport(
getPackageVersionDiff( initialPackageJSON, finalPackageJSON ),
);
process.exit( 0 );
}

refreshDependencies();

function readJSONFile( fileName ) {
const data = fs.readFileSync( fileName, 'utf8' );
return JSON.parse( data );
}

function installPackages( packages ) {
const packagesWithVersion = packages.map(
( [packageName, version] ) => `${ packageName }@${ version }`,
);
return spawn.sync( 'npm', ['install', ...packagesWithVersion, '--save'], {
stdio: 'inherit',
} );
}

function getMissingWordPressPackages() {
const perPackageDeps = getPerPackageDeps();
const currentPackages = perPackageDeps.map( ( [name] ) => name );

const requiredWpPackages = uniq( perPackageDeps
// Capture the @wordpress dependencies of our dependencies into a flat list.
.flatMap( ( [, dependencies] ) => getWordPressPackages( { dependencies } ) )
.sort(),
);

return requiredWpPackages.filter(
packageName => !currentPackages.includes( packageName ) );
}

function getMismatchedNonWordPressDependencies() {
// Get the installed dependencies from package-lock.json
const currentPackageJSON = readJSONFile( `${ rootDir }/package.json` );
const currentPackages = getWordPressPackages( currentPackageJSON );

const packageLock = readJSONFile( `${ rootDir }/package-lock.json` );
const versionConflicts = Object.entries( packageLock.dependencies )
.filter( ( [packageName] ) => currentPackages.includes( packageName ) )
.flatMap( ( [, { dependencies }] ) => Object.entries( dependencies || {} ) )
.filter( identity )
.map( ( [name, { version }] ) => ( {
name,
required: version,
actual: packageLock.dependencies[ name ].version,
} ) )
.filter( ( { required, actual } ) => required !== actual )
;

// Ensure that all the conflicts can be resolved with the same version
const unresolvableConflicts = Object.entries( groupBy( versionConflicts, ( [name] ) => name ) )
.map( ( [name, group] ) => [name, group.map( ( [, { required }] ) => required )] )
.filter( ( [, group] ) => group.length > 1 );
if ( unresolvableConflicts.length > 0 ) {
console.error( "Can't resolve some conflicts automatically." );
console.error( "Multiple required versions of the following packages were detected:" );
console.error( unresolvableConflicts );
process.exit( 1 );
}
return versionConflicts;
}

function getPerPackageDeps() {
// Get the dependencies currently listed in the wordpress-develop package.json
const currentPackageJSON = readJSONFile( 'package.json' );
const currentPackages = getWordPressPackages( currentPackageJSON );

// Get the dependencies that the above dependencies list in their package.json.
const deps = currentPackages
.map( ( packageName ) => `node_modules/${ packageName }/package.json` )
.map( ( jsonPath ) => readJSONFile( jsonPath ).dependencies );
return zip( currentPackages, deps );
}

function getWordPressPackages( { dependencies = {} } ) {
return Object.keys( dependencies )
.filter( isWordPressPackage );
}

function isWordPressPackage( packageName ) {
return packageName.startsWith( WORDPRESS_PACKAGES_PREFIX );
}

function getPackageVersionDiff( initialPackageJSON, finalPackageJSON
) {
const diff = ['dependencies', 'devDependencies'].reduce(
( result, keyPackageJSON ) => {
return Object.keys(
finalPackageJSON[ keyPackageJSON ] || {},
).reduce( ( _result, dependency ) => {
const initial =
initialPackageJSON[ keyPackageJSON ][ dependency ];
const final = finalPackageJSON[ keyPackageJSON ][ dependency ];
if ( initial !== final ) {
_result.push( { dependency, initial, final } );
}
return _result;
}, result );
},
[],
);
return diff.sort( ( a, b ) => a.dependency.localeCompare( b.dependency ) );
}

function outputPackageDiffReport( packageDiff ) {
const readableDiff =
packageDiff
.map( ( { dependency, initial, final } ) => {
return `${ dependency }: ${ initial } -> ${ final }`;
} )
.filter( identity );
if ( !readableDiff.length ) {
console.log( 'No changes detected' );
return;
}
console.log(
[
'The following package versions were changed:',
...readableDiff,
].join( '\n' ),
);
}

/* eslint-enable no-console */

0 comments on commit 58aaa15

Please sign in to comment.