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

Framework: Add package-lock precommit check for latest NPM #21306

Merged
merged 9 commits into from Apr 8, 2020
117 changes: 117 additions & 0 deletions bin/check-latest-npm.js
@@ -0,0 +1,117 @@
#!/usr/bin/env node

/**
* External dependencies
*/
const { green, red, yellow } = require( 'chalk' );
const { get } = require( 'https' );
const { spawn } = require( 'child_process' );

/**
* Returns a promise resolving with the version number of the latest available
* version of NPM.
*
* @return {Promise<string>} Promise resolving with latest NPM version.
*/
async function getLatestNPMVersion() {
return new Promise( ( resolve, reject ) => {
get(
'https://registry.npmjs.org/npm',
{
headers: {
// By passing a specialized `Accept` header, the registry
// will return an abbreviated form of the package data which
// includes enough detail to determine the latest version.
//
// See: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
Accept: 'application/vnd.npm.install-v1+json',
},
},
async ( response ) => {
if ( response.statusCode !== 200 ) {
return reject(
new Error( 'Package data for NPM not found' )
);
}

let body = '';
for await ( const chunk of response ) {
body += chunk.toString();
}

let data;
try {
data = JSON.parse( body );
} catch {
return reject(
new Error(
'Package data for NPM returned invalid response body'
)
);
}
aduth marked this conversation as resolved.
Show resolved Hide resolved

resolve( data[ 'dist-tags' ].latest );
}
).on( 'error', ( error ) => {
if (
/** @type {NodeJS.ErrnoException} */ ( error ).code ===
'ENOTFOUND'
) {
error = new Error( `Could not contact the NPM registry to determine latest version.

This could be due to an intermittent outage of the service, or because you are not connected to the internet.

Because it is important that \`package-lock.json\` files only be committed while running the latest version of NPM, this commit has been blocked.

If you are certain of your changes and desire to commit anyways, you should either connect to the internet or bypass commit verification using ${ yellow(
'git commit --no-verify'
) } .` );
}

reject( error );
} );
} );
}

/**
* Returns a promise resolving with the version number of the local installed
* version of NPM.
*
* @return {Promise<string>} Promise resolving with local installed NPM version.
*/
async function getLocalNPMVersion() {
return new Promise( async ( resolve ) => {
const childProcess = spawn( 'npm', [ '-v' ] );

let output = '';
for await ( const chunk of childProcess.stdout ) {
output += chunk.toString();
}

resolve( output.trim() );
} );
}

Promise.all( [ getLatestNPMVersion(), getLocalNPMVersion() ] )
.then( ( [ latest, local ] ) => {
if ( latest !== local ) {
throw new Error(
`The local NPM version does not match the expected latest version. Expected ${ green(
latest
) }, found ${ red( local ) }.

It is required that you have the latest version of NPM installed in order to commit a change to the package-lock.json file.

Run ${ yellow( 'npm install --global npm@latest' ) }, then try again.`
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 expand the message with the recommendation to run npm install again after installing the latest version of npm?

Copy link
Member Author

@aduth aduth Apr 9, 2020

Choose a reason for hiding this comment

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

Should we expand the message with the recommendation to run npm install again after installing the latest version of npm?

That's an excellent point, considering it's primarily the reason we're asking people to update 😅 (so that package-lock.json changes are accurate, which are adjusted only after npm install).

I can put together a small follow-up pull request.

);
}
} )
.catch( ( error ) => {
// Disable reason: A failure should log to the terminal.

// eslint-disable-next-line no-console
console.error(
'Latest NPM check failed!\n\n' + error.toString() + '\n'
);
process.exitCode = 1;
} );
5 changes: 4 additions & 1 deletion bin/tsconfig.json
Expand Up @@ -13,5 +13,8 @@
// ignored `.cache` file to hide tsbuildinfo and d.ts files.
"outDir": ".cache"
},
"files": [ "./api-docs/update-api-docs.js" ]
"files": [
"./api-docs/update-api-docs.js",
"./check-latest-npm.js"
]
}