Skip to content

Commit

Permalink
createGithubRelease() will not create the same release twice.
Browse files Browse the repository at this point in the history
  • Loading branch information
pomek committed May 18, 2023
1 parent 6fb5174 commit 7e658e8
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
'use strict';

const { Octokit } = require( '@octokit/rest' );
const semver = require( 'semver' );

/**
* Create a Github release.
* Create a GitHub release.
*
* @param {Object} options
* @param {String} options.token Token used to authenticate with GitHub.
Expand All @@ -18,7 +19,7 @@ const { Octokit } = require( '@octokit/rest' );
* @param {String} options.description Description of the release.
* @returns {Promise.<String>}
*/
module.exports = function createGithubRelease( options ) {
module.exports = async function createGithubRelease( options ) {
const {
token,
version,
Expand All @@ -27,25 +28,77 @@ module.exports = function createGithubRelease( options ) {
description
} = options;

const isPrerelease = false; // TODO: Detect it from a version.

const github = new Octokit( {
version: '3.0.0',
auth: `token ${ token }`
} );

const releaseParams = {
tag_name: `v${ version }`,
if ( await shouldCreateRelease( github, repositoryOwner, repositoryName, version ) ) {
await github.repos.createRelease( {
tag_name: `v${ version }`,
owner: repositoryOwner,
repo: repositoryName,
body: description,
prerelease: getVersionTag( version ) !== 'latest'
} );
}

return `https://github.com/${ repositoryOwner }/${ repositoryName }/releases/tag/v${ version }`;
};

/**
* 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;
}

/**
* Resolves a promise containing a flag if the GitHub contains the release page for given version.
*
* @param {Octokit} github
* @param {String} repositoryOwner
* @param {String} repositoryName
* @param {String} version
* @returns {Promise.<boolean>}
*/
async function shouldCreateRelease( github, repositoryOwner, repositoryName, version ) {
const releaseDetails = await getLastRelease( github, repositoryOwner, repositoryName );

// It can be `null` if there is no releases on GitHub.
let githubVersion = releaseDetails.data.tag_name;

if ( githubVersion ) {
githubVersion = releaseDetails.data.tag_name.replace( /^v/, '' );
}

// If versions are different, we are ready to create a new release.
return githubVersion !== version;
}

function getLastRelease( github, repositoryOwner, repositoryName ) {
const requestParams = {
owner: repositoryOwner,
repo: repositoryName,
body: description,
prerelease: isPrerelease
repo: repositoryName
};

// TODO: Tests.
return github.repos.getLatestRelease( requestParams )
.catch( err => {
// If the "last release" returned the 404 error page, it means that this release
// will be the first one for specified `repositoryOwner/repositoryName` package.
if ( err.status == 404 ) {
return Promise.resolve( {
data: {
tag_name: null
}
} );
}

return github.repos.createRelease( releaseParams )
.then( () => {
return `https://github.com/${ repositoryOwner }/${ repositoryName }/releases/tag/v${ version }`;
return Promise.reject( err );
} );
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,19 @@ describe( 'dev-release-tools/tasks', () => {
options = {
token: 'abc123',
version: '1.3.5',
repositoryOwner: 'mrSmith',
repositoryName: 'epic-project',
description: 'Very important release.',
isPrerelease: false
repositoryOwner: 'ckeditor',
repositoryName: 'ckeditor5-example',
description: 'Very important release.'
};

stubs = {
octokit: {
repos: {
createRelease: sinon.stub().resolves()
createRelease: sinon.stub().resolves(),
getLatestRelease: sinon.stub().rejects( {
status: 404
} )
}
},
console: {
log: sinon.stub( console, 'log' )
}
};

Expand Down Expand Up @@ -72,40 +71,64 @@ describe( 'dev-release-tools/tasks', () => {
} );
} );

it( 'creates the release with correct arguments', async () => {
it( 'resolves a url to the created page', async () => {
const url = await createGithubRelease( options );

expect( url ).to.equal( 'https://github.com/ckeditor/ckeditor5-example/releases/tag/v1.3.5' );
} );

it( 'creates a non-prerelease page when passing a major.minor.patch version', async () => {
await createGithubRelease( options );

expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 1 );
expect( stubs.octokit.repos.createRelease.getCall( 0 ).args.length ).to.equal( 1 );
expect( stubs.octokit.repos.createRelease.getCall( 0 ).args[ 0 ] ).to.deep.equal( {
tag_name: 'v1.3.5',
owner: 'mrSmith',
repo: 'epic-project',
owner: 'ckeditor',
repo: 'ckeditor5-example',
body: 'Very important release.',
prerelease: false
} );
} );

it( 'logs the information about success', async () => {
it( 'creates a prerelease page when passing a major.minor.patch-prerelease version', async () => {
options.version = '1.3.5-alpha.0';
await createGithubRelease( options );

expect( stubs.console.log.callCount ).to.equal( 1 );
expect( stubs.console.log.getCall( 0 ).args.length ).to.equal( 1 );
expect( stubs.console.log.getCall( 0 ).args[ 0 ] ).to.equal(
'Created the release on GitHub: https://github.com/mrSmith/epic-project/releases/tag/v1.3.5'
);
expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 1 );
expect( stubs.octokit.repos.createRelease.getCall( 0 ).args.length ).to.equal( 1 );
expect( stubs.octokit.repos.createRelease.getCall( 0 ).args[ 0 ] ).to.deep.equal( {
tag_name: 'v1.3.5-alpha.0',
owner: 'ckeditor',
repo: 'ckeditor5-example',
body: 'Very important release.',
prerelease: true
} );
} );

it( 'logs the information about failure', async () => {
stubs.octokit.repos.createRelease.rejects( { message: 'Release error.' } );
it( 'creates a new release if the previous release version are different', async () => {
stubs.octokit.repos.getLatestRelease.resolves( {
data: {
tag_name: 'v1.3.4'
}
} );

await createGithubRelease( options );

expect( stubs.console.log.callCount ).to.equal( 2 );
expect( stubs.console.log.getCall( 0 ).args.length ).to.equal( 1 );
expect( stubs.console.log.getCall( 0 ).args[ 0 ] ).to.equal( 'An error occurred while creating the release on GitHub:' );
expect( stubs.console.log.getCall( 1 ).args.length ).to.equal( 1 );
expect( stubs.console.log.getCall( 1 ).args[ 0 ] ).to.equal( 'Release error.' );
expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 1 );
} );

it( 'does not create a new release if the previous release version are the same', async () => {
stubs.octokit.repos.getLatestRelease.resolves( {
data: {
tag_name: 'v1.3.5'
}
} );

const url = await createGithubRelease( options );

expect( url ).to.equal( 'https://github.com/ckeditor/ckeditor5-example/releases/tag/v1.3.5' );
expect( stubs.octokit.repos.createRelease.callCount ).to.equal( 0 );
} );
} );
} );

0 comments on commit 7e658e8

Please sign in to comment.