Skip to content

Commit

Permalink
Merge pull request #974 from ckeditor/ci/3710-release-via-ci
Browse files Browse the repository at this point in the history
Feature (release-tools): Create a util for extracting an npm tag from the specified version.

Feature (release-tools): Add util to check if a given package and its version are available on npm.

Internal: Support for releasing the repository for prereleases.
  • Loading branch information
pomek authored Jul 16, 2024
2 parents 4e3e6c6 + a1e0992 commit 784ab36
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 32 deletions.
6 changes: 5 additions & 1 deletion packages/ckeditor5-dev-release-tools/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const executeInParallel = require( './utils/executeinparallel' );
const validateRepositoryToRelease = require( './utils/validaterepositorytorelease' );
const checkVersionAvailability = require( './utils/checkversionavailability' );
const verifyPackagesPublishedCorrectly = require( './tasks/verifypackagespublishedcorrectly' );
const getNpmTagFromVersion = require( './utils/getnpmtagfromversion' );
const isVersionPublishableForTag = require( './utils/isversionpublishablefortag' );

module.exports = {
generateChangelogForSinglePackage,
Expand All @@ -51,10 +53,12 @@ module.exports = {
getNextNightly,
getCurrent,
getLastTagFromGit,
getNpmTagFromVersion,
getChangesForVersion,
getChangelog,
saveChangelog,
validateRepositoryToRelease,
verifyPackagesPublishedCorrectly,
checkVersionAvailability
checkVersionAvailability,
isVersionPublishableForTag
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

'use strict';

const semver = require( 'semver' );

/**
* @param {String} version
* @returns {String}
*/
module.exports = function getNpmTagFromVersion( version ) {
const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ];

return versionTag;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

const { tools } = require( '@ckeditor/ckeditor5-dev-utils' );
const semver = require( 'semver' );

/**
* This util aims to verify if the given `packageName` can be published with the given `version` on the `npmTag`.
*
* @param {String} packageName
* @param {String} version
* @param {String} npmTag
* @return {Promise.<Boolean>}
*/
module.exports = async function isVersionPublishableForTag( packageName, version, npmTag ) {
const npmVersion = await tools.shExec( `npm view ${ packageName }@${ npmTag } version --silent`, { async: true, verbosity: 'silent' } )
.then( value => value.trim() )
// An `npmTag` does not exist.
.catch( () => null );

if ( npmVersion && semver.lte( version, npmVersion ) ) {
return false;
}

return true;
};
18 changes: 17 additions & 1 deletion packages/ckeditor5-dev-release-tools/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ describe( 'dev-release-tools/index', () => {
saveChangelog: sandbox.stub()
},
executeInParallel: sandbox.stub(),
validateRepositoryToRelease: sandbox.stub()
validateRepositoryToRelease: sandbox.stub(),
getNpmTagFromVersion: sandbox.stub(),
isVersionPublishableForTag: sandbox.stub()
}
};

Expand All @@ -71,9 +73,11 @@ describe( 'dev-release-tools/index', () => {
mockery.registerMock( './tasks/updateversions', stubs.release.updateVersions );
mockery.registerMock( './tasks/cleanuppackages', stubs.release.cleanUpPackages );
mockery.registerMock( './utils/versions', stubs.release.version );
mockery.registerMock( './utils/getnpmtagfromversion', stubs.release.getNpmTagFromVersion );
mockery.registerMock( './utils/changelog', stubs.release.changelog );
mockery.registerMock( './utils/executeinparallel', stubs.release.executeInParallel );
mockery.registerMock( './utils/validaterepositorytorelease', stubs.release.validateRepositoryToRelease );
mockery.registerMock( './utils/isversionpublishablefortag', stubs.release.isVersionPublishableForTag );

index = proxyquire( '../lib/index', {
'@ckeditor/ckeditor5-dev-utils': {
Expand Down Expand Up @@ -197,6 +201,12 @@ describe( 'dev-release-tools/index', () => {
} );
} );

describe( 'getNpmTagFromVersion()', () => {
it( 'should be a function', () => {
expect( index.getNpmTagFromVersion ).to.be.a( 'function' );
} );
} );

describe( 'getChangesForVersion()', () => {
it( 'should be a function', () => {
expect( index.getChangesForVersion ).to.be.a( 'function' );
Expand Down Expand Up @@ -232,4 +242,10 @@ describe( 'dev-release-tools/index', () => {
expect( index.checkVersionAvailability ).to.be.a( 'function' );
} );
} );

describe( 'isVersionPublishableForTag()', () => {
it( 'should be a function', () => {
expect( index.isVersionPublishableForTag ).to.be.a( 'function' );
} );
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @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/getNpmTagFromVersion', () => {
let stub, getNpmTagFromVersion;

beforeEach( () => {
stub = {
semver: {
prerelease: sinon.stub()
}
};

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

mockery.registerMock( 'semver', stub.semver );

getNpmTagFromVersion = require( '../../lib/utils/getnpmtagfromversion' );
} );

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

it( 'should return "latest" when processing a X.Y.Z version', () => {
expect( getNpmTagFromVersion( '1.0.0' ) ).to.equal( 'latest' );
expect( getNpmTagFromVersion( '2.1.0' ) ).to.equal( 'latest' );
expect( getNpmTagFromVersion( '3.2.1' ) ).to.equal( 'latest' );
} );

it( 'should return "alpha" when processing a X.Y.Z-alpha.X version', () => {
stub.semver.prerelease.returns( [ 'alpha', 0 ] );

expect( getNpmTagFromVersion( '1.0.0-alpha.0' ) ).to.equal( 'alpha' );
expect( getNpmTagFromVersion( '2.1.0-alpha.0' ) ).to.equal( 'alpha' );
expect( getNpmTagFromVersion( '3.2.1-alpha.0' ) ).to.equal( 'alpha' );
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @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/isVersionPublishableForTag', () => {
let stub, isVersionPublishableForTag;

beforeEach( () => {
stub = {
semver: {
lte: sinon.stub()
},
devUtils: {
tools: {
shExec: sinon.stub()
}
}
};

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

mockery.registerMock( 'semver', stub.semver );
mockery.registerMock( '@ckeditor/ckeditor5-dev-utils', stub.devUtils );

isVersionPublishableForTag = require( '../../lib/utils/isversionpublishablefortag' );
} );

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

it( 'should return true if given version is available', async () => {
stub.semver.lte.returns( false );
stub.devUtils.tools.shExec.resolves( '1.0.0\n' );

const result = await isVersionPublishableForTag( 'package-name', '1.0.1', 'latest' );

expect( result ).to.equal( true );
expect( stub.semver.lte.callCount ).to.equal( 1 );
expect( stub.semver.lte.firstCall.args ).to.deep.equal( [ '1.0.1', '1.0.0' ] );
expect( stub.devUtils.tools.shExec.callCount ).to.equal( 1 );
expect( stub.devUtils.tools.shExec.firstCall.firstArg ).to.equal( 'npm view package-name@latest version --silent' );
} );

it( 'should return false if given version is not available', async () => {
stub.semver.lte.returns( true );
stub.devUtils.tools.shExec.resolves( '1.0.0\n' );

const result = await isVersionPublishableForTag( 'package-name', '1.0.0', 'latest' );

expect( result ).to.equal( false );
expect( stub.semver.lte.callCount ).to.equal( 1 );
expect( stub.semver.lte.firstCall.args ).to.deep.equal( [ '1.0.0', '1.0.0' ] );
expect( stub.devUtils.tools.shExec.callCount ).to.equal( 1 );
expect( stub.devUtils.tools.shExec.firstCall.firstArg ).to.equal( 'npm view package-name@latest version --silent' );
} );

it( 'should return true if given npm tag is not published yet', async () => {
stub.devUtils.tools.shExec.rejects( 'E404' );

const result = await isVersionPublishableForTag( 'package-name', '1.0.0', 'alpha' );

expect( result ).to.equal( true );
expect( stub.semver.lte.callCount ).to.equal( 0 );
expect( stub.devUtils.tools.shExec.callCount ).to.equal( 1 );
expect( stub.devUtils.tools.shExec.firstCall.firstArg ).to.equal( 'npm view package-name@alpha version --silent' );
} );
} );
40 changes: 12 additions & 28 deletions scripts/ci/is-project-ready-to-release.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,19 @@

'use strict';

const { execSync } = require( 'child_process' );
const releaseTools = require( '@ckeditor/ckeditor5-dev-release-tools' );
const semver = require( 'semver' );
const { name: packageName } = require( '@ckeditor/ckeditor5-dev-release-tools/package.json' );

const latestPublishedVersion = execSync( 'npm view @ckeditor/ckeditor5-dev-release-tools@latest version', { encoding: 'utf-8' } ).trim();
const changelogVersion = releaseTools.getLastFromChangelog();
const npmTag = releaseTools.getNpmTagFromVersion( changelogVersion );

releaseTools.isVersionPublishableForTag( packageName, changelogVersion, npmTag )
.then( result => {
if ( !result ) {
console.error( `The proposed changelog (${ changelogVersion }) version is not higher than the already published one.` );
process.exit( 1 );
} else {
console.log( 'The project is ready to release.' );
}
} );

if ( getVersionTag( changelogVersion ) !== 'latest' ) {
console.log( `Aborting due non-latest changelog version (${ changelogVersion }).` );
process.exit( 1 );
}

if ( semver.lte( changelogVersion, latestPublishedVersion ) ) {
console.log(
`The proposed changelog (${ changelogVersion }) version is not greater than the published one (${ latestPublishedVersion }).`
);
process.exit( 1 );
}

console.log( 'The project is ready to release.' );

/**
* Returns an npm tag based on the specified release version.
*
* @param {String} version
* @returns {String}
*/
function getVersionTag( version ) {
const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ];

return versionTag;
}
4 changes: 4 additions & 0 deletions scripts/publishpackages.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const versionChangelog = releaseTools.getChangesForVersion( latestVersion );

let githubToken;

if ( !cliArguments.npmTag ) {
cliArguments.npmTag = releaseTools.getNpmTagFromVersion( latestVersion );
}

const tasks = new Listr( [
{
title: 'Publishing packages.',
Expand Down
4 changes: 2 additions & 2 deletions scripts/utils/parsearguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ module.exports = function parseArguments( cliArguments ) {
verbose: false,
'compile-only': false,
branch: 'master',
'npm-tag': 'latest'
'npm-tag': null
}
};

Expand Down Expand Up @@ -67,7 +67,7 @@ module.exports = function parseArguments( cliArguments ) {
*
* @property {Number} concurrency
*
* @property {String} [npmTag='latest']
* @property {String|null} [npmTag=null]
*
* @property {Array.<String>|null} packages
*
Expand Down

0 comments on commit 784ab36

Please sign in to comment.