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

Added new task to verify if package was published correctly to npm. #965

Merged
merged 7 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/ckeditor5-dev-release-tools/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const { getChangesForVersion, getChangelog, saveChangelog } = require( './utils/
const executeInParallel = require( './utils/executeinparallel' );
const validateRepositoryToRelease = require( './utils/validaterepositorytorelease' );
const checkVersionAvailability = require( './utils/checkversionavailability' );
const verifyPackagesPublishedCorrectly = require( './tasks/verifypackagespublishedcorrectly' );

module.exports = {
generateChangelogForSinglePackage,
Expand All @@ -54,5 +55,6 @@ module.exports = {
getChangelog,
saveChangelog,
validateRepositoryToRelease,
verifyPackagesPublishedCorrectly,
checkVersionAvailability
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

'use strict';

const upath = require( 'upath' );
const { glob } = require( 'glob' );
const fs = require( 'fs-extra' );
const { checkVersionAvailability } = require( '../utils/checkversionavailability' );

/**
* Npm sometimes throws incorrect error 409 while publishing, while the package uploads correctly.
* The purpose of the script is to validate if packages that threw 409 are uploaded correctly to npm.
*
* @param {Object} options
* @param {String} options.packagesDirectory Relative path to a location of packages to release.
* @param {String} options.version Version of the current release.
* @param {Function} options.onSuccess Callback fired when function is successful.
* @returns {Promise}
*/
module.exports = async function verifyPackagesPublishedCorrectly( options ) {
const { packagesDirectory, version, onSuccess } = options;
const packagesToVerify = await glob( upath.join( packagesDirectory, '*' ), { absolute: true } );
const errors = [];

if ( !packagesToVerify.length ) {
onSuccess( '✅ No packages found to check for upload error 409.' );

return;
}

for ( const packageToVerify of packagesToVerify ) {
const packageJson = await fs.readJson( upath.join( packageToVerify, 'package.json' ) );

try {
await checkVersionAvailability( version, packageJson.name );
Copy link
Contributor

Choose a reason for hiding this comment

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

checkVersionAvailability() returns a promise that resolves to true if the provided version does not exist or resolves the promise to false otherwise.

We should check the returned value here.

await fs.remove( packageToVerify );
} catch {
errors.push( packageJson.name );
}
}

if ( errors.length ) {
throw new Error( 'Packages that were uploaded incorrectly, and need manual verification:\n' + errors.join( '\n' ) );
}

onSuccess( '✅ All packages that returned 409 were uploaded correctly.' );
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,22 @@ module.exports = async function publishPackageOnNpmCallback( packagePath, taskOp
const upath = require( 'upath' );
const fs = require( 'fs-extra' );

await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, { cwd: packagePath, async: true, verbosity: 'error' } )
.catch( () => {
const result = await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, {
cwd: packagePath,
async: true,
verbosity: 'error'
} )
.catch( e => {
const packageName = upath.basename( packagePath );

if ( e.toString().includes( 'code E409' ) ) {
return { shouldKeepDirectory: true };
}

throw new Error( `Unable to publish "${ packageName }" package.` );
} );

await fs.remove( packagePath );
if ( !result || !result.shouldKeepDirectory ) {
await fs.remove( packagePath );
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

'use strict';

const expect = require( 'chai' ).expect;
const sinon = require( 'sinon' );
const mockery = require( 'mockery' );

describe( 'dev-release-tools/utils', () => {
describe( 'verifyPackagesPublishedCorrectly()', () => {
let verifyPackagesPublishedCorrectly, sandbox, stubs;

beforeEach( () => {
sandbox = sinon.createSandbox();

stubs = {
fs: {
remove: sandbox.stub().resolves(),
readJson: sandbox.stub().resolves()
},
devUtils: {
checkVersionAvailability: sandbox.stub().resolves()
},
glob: {
glob: sandbox.stub().resolves( [] )
}
};

mockery.enable( {
useCleanCache: true,
warnOnReplace: false,
warnOnUnregistered: false
} );

mockery.registerMock( 'fs-extra', stubs.fs );
mockery.registerMock( '../utils/checkversionavailability', stubs.devUtils );
mockery.registerMock( 'glob', stubs.glob );

verifyPackagesPublishedCorrectly = require( '../../lib/tasks/verifypackagespublishedcorrectly' );
} );

afterEach( () => {
mockery.deregisterAll();
mockery.disable();
sandbox.restore();
} );

it( 'should not verify packages if there are no packages in the release directory', async () => {
stubs.glob.glob.resolves( [] );

const packagesDirectory = '/workspace/ckeditor5/release/npm';
const version = 'latest';
const onSuccess = sandbox.stub();

await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } );

expect( onSuccess.firstCall.args[ 0 ] ).to.equal( '✅ No packages found to check for upload error 409.' );
expect( stubs.devUtils.checkVersionAvailability.callCount ).to.equal( 0 );
} );

it( 'should verify packages and remove them from the release directory on "npm show" command success', async () => {
stubs.glob.glob.resolves( [ 'package1', 'package2' ] );
stubs.fs.readJson
.onCall( 0 ).resolves( { name: '@namespace/package1' } )
.onCall( 1 ).resolves( { name: '@namespace/package2' } );

const packagesDirectory = '/workspace/ckeditor5/release/npm';
const version = 'latest';
const onSuccess = sandbox.stub();

await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } );

expect( stubs.devUtils.checkVersionAvailability.firstCall.args[ 0 ] ).to.equal( 'latest' );
expect( stubs.devUtils.checkVersionAvailability.firstCall.args[ 1 ] ).to.equal( '@namespace/package1' );
expect( stubs.fs.remove.firstCall.args[ 0 ] ).to.equal( 'package1' );

expect( stubs.devUtils.checkVersionAvailability.secondCall.args[ 0 ] ).to.equal( 'latest' );
expect( stubs.devUtils.checkVersionAvailability.secondCall.args[ 1 ] ).to.equal( '@namespace/package2' );
expect( stubs.fs.remove.secondCall.args[ 0 ] ).to.equal( 'package2' );

expect( onSuccess.firstCall.args[ 0 ] ).to.equal( '✅ All packages that returned 409 were uploaded correctly.' );
} );

it( 'should not remove package from release directory on on error', async () => {
stubs.glob.glob.resolves( [ 'package1', 'package2' ] );
stubs.fs.readJson
.onCall( 0 ).resolves( { name: '@namespace/package1' } )
.onCall( 1 ).resolves( { name: '@namespace/package2' } );
stubs.devUtils.checkVersionAvailability
.onCall( 0 ).rejects()
.onCall( 1 ).resolves();

const packagesDirectory = '/workspace/ckeditor5/release/npm';
const version = 'latest';
const onSuccess = sandbox.stub();

await verifyPackagesPublishedCorrectly( { packagesDirectory, version, onSuccess } )
.then(
() => {
throw new Error( 'this should not be thrown!' );
},
e => {
expect( e.message ).to.equal(
'Packages that were uploaded incorrectly, and need manual verification:\n@namespace/package1'
);
}
);

expect( stubs.fs.remove.callCount ).to.equal( 1 );
expect( stubs.fs.remove.firstCall.args[ 0 ] ).to.equal( 'package2' );
} );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,15 @@ describe( 'dev-release-tools/utils', () => {
}
);
} );

it( 'should not remove a package directory and not throw error when publishing on npm failed with code 409', async () => {
stubs.devUtils.tools.shExec.rejects( new Error( 'code E409' ) );

const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo';

await publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } );

expect( stubs.fs.remove.callCount ).to.equal( 0 );
} );
} );
} );