Skip to content

Commit

Permalink
Feature (env): Support for collecting commits from additional mono-re…
Browse files Browse the repository at this point in the history
…positories in the `generateChangelogForMonoRepository()` function.
  • Loading branch information
mlewand authored Feb 1, 2021
2 parents 1726e5f + f13622d commit 3d7d5c2
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,29 +55,26 @@ const noteInfo = `[ℹ️](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-c
*
* @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages.
*
* @param {Array.<ExternalRepository>} [options.externalRepositories=[]] An array of object with additional repositories
* that the function takes into consideration while gathering commits. It assumes that those directories are also mono repositories.
*
* @returns {Promise}
*/
module.exports = function generateChangelogForMonoRepository( options ) {
const log = logger();
const cwd = process.cwd();
const pkgJson = getPackageJson( options.cwd );

const transformCommit = transformCommitFactory( {
useExplicitBreakingChangeGroups: true
} );
logProcess( 'Collecting paths to packages...' );

const pathsCollection = getPackagesPaths( {
const pathsCollection = gatherAllPackagesPaths( {
cwd: options.cwd,
packages: options.packages,
scope: options.scope || null,
skipPackages: options.skipPackages || [],
skipMainRepository: true
externalRepositories: options.externalRepositories || []
} );

// The main repository should be at the end of the list.
pathsCollection.skipped.delete( options.cwd );
pathsCollection.matched.add( options.cwd );

logProcess( 'Collecting all commits since the last release...' );

// Collection of all entries (real commits + additional "fake" commits extracted from descriptions).
Expand All @@ -99,15 +96,17 @@ module.exports = function generateChangelogForMonoRepository( options ) {
const packagesPaths = new Map();

const commitOptions = {
cwd: options.cwd,
from: options.from ? options.from : 'v' + pkgJson.version,
releaseBranch: options.releaseBranch
releaseBranch: options.releaseBranch || 'master',
externalRepositories: options.externalRepositories || []
};

return getCommits( transformCommit, commitOptions )
return gatherAllCommits( commitOptions )
.then( commits => {
allCommits = commits;

logInfo( `Found ${ commits.length } entries to parse.`, { indentLevel: 1 } );
logInfo( `Found ${ commits.length } entries to parse.`, { indentLevel: 1, startWithNewLine: true } );
} )
.then( () => typeNewVersionForAllPackages() )
.then( () => generateChangelogFromCommits() )
Expand Down Expand Up @@ -135,6 +134,116 @@ module.exports = function generateChangelogForMonoRepository( options ) {
console.log( err );
} );

/**
* Returns collections with packages found in the `options.cwd` directory and the external repositories.
*
* @param {Object} options
* @param {String} options.cwd Current working directory (packages) from which all paths will be resolved.
* @param {String} options.packages Where to look for packages.
* @param {String} options.scope Package names have to match to specified glob pattern in order to be processed.
* @param {Array.<String>} options.skipPackages Name of packages which won't be touched.
* @param {Array.<ExternalRepository>} options.externalRepositories An array of object with additional repositories
* that the function takes into consideration while gathering packages.
* @returns {PathsCollection}
*/
function gatherAllPackagesPaths( options ) {
logInfo( `Processing "${ options.cwd }"...`, { indentLevel: 1 } );

const pathsCollection = getPackagesPaths( {
cwd: options.cwd,
packages: options.packages,
scope: options.scope,
skipPackages: options.skipPackages,
skipMainRepository: true
} );

for ( const externalRepository of options.externalRepositories ) {
logInfo( `Processing "${ externalRepository.cwd }"...`, { indentLevel: 1 } );

const externalPackages = getPackagesPaths( {
cwd: externalRepository.cwd,
packages: externalRepository.packages,
scope: externalRepository.scope || null,
skipPackages: externalRepository.skipPackages || [],
skipMainRepository: true
} );

// The main package in an external repository is a private package.
externalPackages.skipped.delete( externalRepository.cwd );

// Merge results with the object that will be returned.
[ ...externalPackages.matched ].forEach( item => pathsCollection.matched.add( item ) );
[ ...externalPackages.skipped ].forEach( item => pathsCollection.skipped.add( item ) );
}

// The main repository should be at the end of the list.
pathsCollection.skipped.delete( options.cwd );
pathsCollection.matched.add( options.cwd );

return pathsCollection;
}

/**
* Returns a promise that resolves an array of commits since the last tag specified as `options.from`.
*
* @param {Object} options
* @param {String} options.cwd Current working directory (packages) from which all paths will be resolved.
* @param {String} options.from A commit or tag name that will be the first param of the range of commits to collect.
* @param {String} options.releaseBranch A name of the branch that should be used for releasing packages.
* @param {Array.<ExternalRepository>} options.externalRepositories An array of object with additional repositories
* that the function takes into consideration while gathering commits.
* @returns {Promise.<Array.<Commit>>}
*/
function gatherAllCommits( options ) {
logInfo( `Processing "${ options.cwd }"...`, { indentLevel: 1 } );

const transformCommit = transformCommitFactory( {
useExplicitBreakingChangeGroups: true
} );

const commitOptions = {
from: options.from,
releaseBranch: options.releaseBranch
};

let promise = getCommits( transformCommit, commitOptions )
.then( commits => {
logInfo( `Found ${ commits.length } entries in "${ options.cwd }".`, { indentLevel: 1 } );

return commits;
} );

for ( const externalRepository of options.externalRepositories ) {
promise = promise.then( commits => {
logInfo( `Processing "${ externalRepository.cwd }"...`, { indentLevel: 1, startWithNewLine: true } );
process.chdir( externalRepository.cwd );

const commitOptions = {
from: externalRepository.from || options.from,
releaseBranch: externalRepository.releaseBranch || options.releaseBranch
};

return getCommits( transformCommit, commitOptions )
.then( newCommits => {
logInfo( `Found ${ newCommits.length } entries in "${ externalRepository.cwd }".`, { indentLevel: 1 } );

for ( const singleCommit of newCommits ) {
singleCommit.skipCommitsLink = externalRepository.skipLinks || false;
}

// Merge arrays with the commits.
return [].concat( commits, newCommits );
} );
} );
}

return promise.then( commits => {
process.chdir( options.cwd );

return commits;
} );
}

/**
* Asks the user about the new version for all packages for the upcoming release.
*
Expand Down Expand Up @@ -592,3 +701,23 @@ module.exports = function generateChangelogForMonoRepository( options ) {
*
* @param {Boolean} next The next version defined during generating the changelog file.
*/

/**
* @typedef {Object} ExternalRepository
*
* @param {String} cwd An absolute path to the repository.
*
* @param {String} packages Subdirectory in a given `cwd` that should searched for packages. E.g. `'packages'`.
*
* @param {String} [scope] Glob pattern for package names to be processed.
*
* @param {Array.<String>} [skipPackages] Name of packages which won't be touched.
*
* @param {Boolean} [skipLinks] If set on `true`, a URL to commit (hash) will be omitted.
*
* @param {String} [from] A commit or tag name that will be the first param of the range of commits to collect. If not specified,
* the option will inherit its value from the function's `options` object.
*
* @param {String} [releaseBranch] A name of the branch that should be used for releasing packages. If not specified, the branch
* used for the main repository will be used.
*/
38 changes: 20 additions & 18 deletions packages/ckeditor5-dev-env/lib/release-tools/templates/commit.hbs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
* {{#if scope}}**{{~scope.[0]}}**: {{/if}}{{subject}}

{{~#unless @root.skipCommitsLink}}
{{~!-- commit link --}} {{#if @root.linkReferences~}}
([commit](
{{~#if @root.repository}}
{{~#if @root.host}}
{{~@root.host}}/
{{~/if}}
{{~#if @root.owner}}
{{~@root.owner}}/
{{~/if}}
{{~@root.repository}}
{{~else}}
{{~@root.repoUrl}}
{{~/if}}/
{{~@root.commit}}/{{hash}}))
{{~else}}
{{~hash}}
{{~/if}}
{{~#unless @root.skipCommitsLink }}
{{~#unless skipCommitsLink }}
{{~!-- commit link --}} {{#if @root.linkReferences~}}
([commit](
{{~#if @root.repository}}
{{~#if @root.host}}
{{~@root.host}}/
{{~/if}}
{{~#if @root.owner}}
{{~@root.owner}}/
{{~/if}}
{{~@root.repository}}
{{~else}}
{{~@root.repoUrl}}
{{~/if}}/
{{~@root.commit}}/{{hash}}))
{{~else}}
{{~hash}}
{{~/if}}
{{/unless}}
{{/unless}}
{{#if body}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,70 @@ describe( 'dev-env/release-tools/utils', () => {
);
} );
} );

it( 'allows removing a URL to commit per commit', () => {
const commits = [
{
type: 'Features',
header: 'Feature: (a) The first an amazing feature.',
subject: '(a) The first an amazing feature.',
hash: 'x'.repeat( 40 ),
notes: [],
skipCommitsLink: true
},
{
type: 'Features',
header: 'Feature: (b) The second an amazing feature.',
subject: '(b) The second an amazing feature.',
hash: 'z'.repeat( 40 ),
notes: []
},
{
type: 'Features',
header: 'Feature: (c) The last one an amazing feature.',
subject: '(c) The last one an amazing feature.',
hash: 'y'.repeat( 40 ),
notes: [],
skipCommitsLink: true
}
];

const context = {
version: '1.0.0',
repoUrl: url,
currentTag: 'v1.0.0',
commit: 'commit'
};

const options = getWriterOptions( {
hash: hash => hash.slice( 0, 7 )
} );

return generateChangelog( commits, context, options )
.then( changes => {
changes = replaceDates( changes );

const changesAsArray = changes.split( '\n' )
.map( line => line.trim() )
.filter( line => line.length );

expect( changesAsArray[ 0 ] ).to.equal(
'## [1.0.0](https://github.com/ckeditor/ckeditor5-package/tree/v1.0.0) (0000-00-00)'
);
expect( changesAsArray[ 1 ] ).to.equal(
'### Features'
);
expect( changesAsArray[ 2 ] ).to.equal(
'* (a) The first an amazing feature.'
);
expect( changesAsArray[ 3 ] ).to.equal(
'* (b) The second an amazing feature. ([commit](https://github.com/ckeditor/ckeditor5-package/commit/zzzzzzz))'
);
expect( changesAsArray[ 4 ] ).to.equal(
'* (c) The last one an amazing feature.'
);
} );
} );
} );

describe( 'non-initial changelog (with "previousTag")', () => {
Expand Down

0 comments on commit 3d7d5c2

Please sign in to comment.