@ckeditor/ckeditor5-dev-release-tools 32.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/LICENSE.md +16 -0
  2. package/README.md +89 -0
  3. package/lib/index.js +28 -0
  4. package/lib/tasks/bumpversions.js +354 -0
  5. package/lib/tasks/generatechangelogformonorepository.js +723 -0
  6. package/lib/tasks/generatechangelogforsinglepackage.js +202 -0
  7. package/lib/tasks/releasesubrepositories.js +929 -0
  8. package/lib/tasks/updateckeditor5dependencies.js +392 -0
  9. package/lib/templates/commit.hbs +29 -0
  10. package/lib/templates/footer.hbs +10 -0
  11. package/lib/templates/header.hbs +23 -0
  12. package/lib/templates/release-package.json +12 -0
  13. package/lib/templates/template.hbs +37 -0
  14. package/lib/utils/changelog.js +67 -0
  15. package/lib/utils/cli.js +324 -0
  16. package/lib/utils/creategithubrelease.js +35 -0
  17. package/lib/utils/displaycommits.js +105 -0
  18. package/lib/utils/displayskippedpackages.js +32 -0
  19. package/lib/utils/executeonpackages.js +26 -0
  20. package/lib/utils/generatechangelog.js +121 -0
  21. package/lib/utils/getchangedfilesforcommit.js +33 -0
  22. package/lib/utils/getcommits.js +104 -0
  23. package/lib/utils/getnewversiontype.js +53 -0
  24. package/lib/utils/getpackagejson.js +24 -0
  25. package/lib/utils/getpackagespaths.js +90 -0
  26. package/lib/utils/getpackagestorelease.js +152 -0
  27. package/lib/utils/getwriteroptions.js +33 -0
  28. package/lib/utils/parseroptions.js +26 -0
  29. package/lib/utils/transformcommitfactory.js +492 -0
  30. package/lib/utils/transformcommitutils.js +163 -0
  31. package/lib/utils/updatedependenciesversions.js +32 -0
  32. package/lib/utils/validatepackagetorelease.js +54 -0
  33. package/lib/utils/versions.js +59 -0
  34. package/package.json +52 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const conventionalCommitsParser = require( 'conventional-commits-parser' );
9
+ const conventionalCommitsFilter = require( 'conventional-commits-filter' );
10
+ const gitRawCommits = require( 'git-raw-commits' );
11
+ const concat = require( 'concat-stream' );
12
+ const parserOptions = require( './parseroptions' );
13
+ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' );
14
+
15
+ /**
16
+ * Returns a promise that resolves an array of commits since the last tag specified as `options.from`.
17
+ *
18
+ * @param {Function} transformCommit
19
+ * @param {Object} options
20
+ * @param {String} [options.from] A commit or tag name that will be the first param of the range of commits to collect.
21
+ * @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages.
22
+ * @param {String} [options.mainBranch='master'] A name of the main branch in the repository.
23
+ * @returns {Promise.<Array.<Commit>>}
24
+ */
25
+ module.exports = function getCommits( transformCommit, options = {} ) {
26
+ const releaseBranch = options.releaseBranch || 'master';
27
+ const mainBranch = options.mainBranch || 'master';
28
+
29
+ const currentBranch = exec( 'git rev-parse --abbrev-ref HEAD' ).trim();
30
+
31
+ // Check whether current branch is the same as the release branch.
32
+ if ( currentBranch !== releaseBranch ) {
33
+ return Promise.reject( new Error(
34
+ `Expected to be checked out on the release branch ("${ releaseBranch }") instead of "${ currentBranch }". Aborting.`
35
+ ) );
36
+ }
37
+
38
+ // If the release branch is the same as the main branch, we can collect all commits directly from the branch.
39
+ if ( releaseBranch === mainBranch ) {
40
+ return findCommits( { from: options.from } );
41
+ } else {
42
+ // Otherwise, (release branch is other than the main branch) we need to merge arrays of commits.
43
+ // See: https://github.com/ckeditor/ckeditor5/issues/7492.
44
+ const baseCommit = exec( `git merge-base ${ releaseBranch } ${ mainBranch }` ).trim();
45
+
46
+ const commitPromises = [
47
+ // 1. Commits from the last release and to the point where the release branch was created (the merge-base commit).
48
+ findCommits( { from: options.from, to: baseCommit } ),
49
+ // 2. Commits from the merge-base commit to HEAD.
50
+ findCommits( { from: baseCommit } )
51
+ ];
52
+
53
+ return Promise.all( commitPromises )
54
+ .then( commits => [].concat( ...commits ) );
55
+ }
56
+
57
+ function findCommits( gitRawCommitsOptions ) {
58
+ const gitRawCommitsOpts = Object.assign( {}, gitRawCommitsOptions, {
59
+ format: '%B%n-hash-%n%H',
60
+ merges: undefined,
61
+ firstParent: true
62
+ } );
63
+
64
+ return new Promise( ( resolve, reject ) => {
65
+ const stream = gitRawCommits( gitRawCommitsOpts )
66
+ .on( 'error', err => {
67
+ /* istanbul ignore else */
68
+ if ( err.message.match( /'HEAD': unknown/ ) ) {
69
+ reject( new Error( 'Given repository is empty.' ) );
70
+ } else if ( err.message.match( new RegExp( `'${ options.from }\\.\\.HEAD': unknown` ) ) ) {
71
+ reject( new Error(
72
+ `Cannot find tag or commit "${ options.from }" in given repository.`
73
+ ) );
74
+ } else {
75
+ reject( err );
76
+ }
77
+ } );
78
+
79
+ stream.pipe( conventionalCommitsParser( parserOptions ) )
80
+ .pipe( concat( data => {
81
+ const commits = conventionalCommitsFilter( data )
82
+ .map( commit => transformCommit( commit ) )
83
+ .reduce( ( allCommits, commit ) => {
84
+ if ( Array.isArray( commit ) ) {
85
+ allCommits.push( ...commit );
86
+ } else {
87
+ allCommits.push( commit );
88
+ }
89
+
90
+ return allCommits;
91
+ }, [] )
92
+ .filter( commit => commit );
93
+
94
+ stream.destroy();
95
+
96
+ return resolve( commits );
97
+ } ) );
98
+ } );
99
+ }
100
+
101
+ function exec( command ) {
102
+ return tools.shExec( command, { verbosity: 'error' } );
103
+ }
104
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ /**
9
+ * Proposes new version based on commits.
10
+ *
11
+ * @param {Array.<Commit>} commits
12
+ * @returns {String|null}
13
+ */
14
+ module.exports = function getNewVersionType( commits ) {
15
+ // No commits = no changes.
16
+ if ( !commits.length ) {
17
+ return 'skip';
18
+ }
19
+
20
+ const publicCommits = commits.filter( commit => commit.isPublicCommit );
21
+
22
+ // No public commits = internal changes.
23
+ if ( !publicCommits.length ) {
24
+ return 'internal';
25
+ }
26
+
27
+ let newFeatures = false;
28
+ let minorBreakingChanges = false;
29
+
30
+ for ( const commit of publicCommits ) {
31
+ for ( const note of commit.notes ) {
32
+ if ( note.title === 'MAJOR BREAKING CHANGES' || note.title === 'BREAKING CHANGES' ) {
33
+ return 'major';
34
+ }
35
+
36
+ /* istanbul ignore else */
37
+ if ( note.title === 'MINOR BREAKING CHANGES' ) {
38
+ minorBreakingChanges = true;
39
+ }
40
+ }
41
+
42
+ if ( commit.rawType === 'Feature' ) {
43
+ newFeatures = true;
44
+ }
45
+ }
46
+
47
+ // Repository has new features or minor breaking changes.
48
+ if ( minorBreakingChanges || newFeatures ) {
49
+ return 'minor';
50
+ }
51
+
52
+ return 'patch';
53
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const fs = require( 'fs' );
9
+ const path = require( 'path' );
10
+
11
+ /**
12
+ * Returns object from `package.json`.
13
+ *
14
+ * This function is helpful for testing the whole process. Allows mocking the file
15
+ * instead of create the fixtures.
16
+ *
17
+ * @param {String} [cwd=process.cwd()] Where to look for package.json.
18
+ * @returns {Object}
19
+ */
20
+ module.exports = function getPackageJson( cwd = process.cwd() ) {
21
+ return JSON.parse(
22
+ fs.readFileSync( path.join( cwd, 'package.json' ), 'utf-8' )
23
+ );
24
+ };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require( 'path' );
9
+ const minimatch = require( 'minimatch' );
10
+ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' );
11
+ const getPackageJson = require( './getpackagejson' );
12
+
13
+ /**
14
+ * Returns an object with two collections of paths to packages which are located in single repository.
15
+ * Those packages must be defined as dependencies in the repository found in `options.cwd`.
16
+ *
17
+ * - The first one is marked as `matched` and means that packages specified in a path (which is a combination of values specified as
18
+ * `options.cwd` and `options.packages`) match to given criteria.
19
+ * - The second one is marked as `skipped` and means that packages should not be processed. They were listed as packages to skip
20
+ * (`options.skipPackages` or don't mach to `options.scope`).
21
+ *
22
+ * @param {Object} options
23
+ * @param {String} options.cwd Current work directory.
24
+ * @param {String|null} options.packages Name of directory where to look for packages. If `null`, only repository specified under
25
+ * `options.cwd` will be returned.
26
+ * @param {String|Array.<String>} options.skipPackages Glob pattern(s) which describes which packages should be skipped.
27
+ * @param {String} [options.scope] Package names have to match to specified glob pattern.
28
+ * @param {Boolean} [options.skipMainRepository=false] If set on true, package found in `options.cwd` will be skipped.
29
+ * @returns {PathsCollection}
30
+ */
31
+ module.exports = function getPackagesPaths( options ) {
32
+ const pathsCollection = {
33
+ matched: new Set(),
34
+ skipped: new Set()
35
+ };
36
+
37
+ if ( options.skipMainRepository ) {
38
+ pathsCollection.skipped.add( options.cwd );
39
+ } else {
40
+ pathsCollection.matched.add( options.cwd );
41
+ }
42
+
43
+ if ( !options.packages ) {
44
+ return pathsCollection;
45
+ }
46
+
47
+ const packagesPath = path.join( options.cwd, options.packages );
48
+ const skipPackages = Array.isArray( options.skipPackages ) ? options.skipPackages : [ options.skipPackages ];
49
+
50
+ for ( const directory of tools.getDirectories( packagesPath ) ) {
51
+ const dependencyPath = path.join( packagesPath, directory );
52
+
53
+ try {
54
+ const dependencyName = getPackageJson( dependencyPath ).name;
55
+
56
+ if ( isValidPackage( dependencyName ) ) {
57
+ pathsCollection.matched.add( dependencyPath );
58
+ } else {
59
+ pathsCollection.skipped.add( dependencyPath );
60
+ }
61
+ } catch ( err ) {
62
+ /* istanbul ignore next */
63
+ console.warn( `Missing "package.json file in "${ dependencyPath }". Skipping.` );
64
+ }
65
+ }
66
+
67
+ return pathsCollection;
68
+
69
+ function isValidPackage( packageName ) {
70
+ for ( const skipPackageGlob of skipPackages ) {
71
+ if ( minimatch( packageName, skipPackageGlob ) ) {
72
+ return false;
73
+ }
74
+ }
75
+
76
+ if ( options.scope ) {
77
+ return minimatch( packageName, options.scope );
78
+ }
79
+
80
+ return true;
81
+ }
82
+ };
83
+
84
+ /**
85
+ * @typedef {Object} PathsCollection
86
+ *
87
+ * @property {Set.<String>} matched Packages that match given criteria.
88
+ *
89
+ * @property {Set.<String>} skipped Packages that do not match given criteria.
90
+ */
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const executeOnPackages = require( './executeonpackages' );
9
+ const getPackageJson = require( './getpackagejson' );
10
+
11
+ /**
12
+ * Returns a list of packages which should be releases based on changes in a changelog file and tags created in Git repository.
13
+ *
14
+ * @param {Set} pathsToPackages A collection of paths to packages that should be checked.
15
+ * @param {Object} options
16
+ * @param {String} options.changes A description of changes for the main repository. It should contain a script-friendly list that provides
17
+ * new versions for parsed packages. If didn't find, `options.version` will be used instead.
18
+ * @param {String} options.version New version for the main repository.
19
+ * @returns {Promise.<Map.<String, ReleaseDetails>>}
20
+ */
21
+ module.exports = function getPackagesToRelease( pathsToPackages, options ) {
22
+ const cwd = process.cwd();
23
+ const packagesToRelease = new Map();
24
+
25
+ return executeOnPackages( pathsToPackages, filterPackagesToRelease )
26
+ .then( () => {
27
+ process.chdir( cwd );
28
+
29
+ return Promise.resolve( packagesToRelease );
30
+ } );
31
+
32
+ function filterPackagesToRelease( repositoryPath ) {
33
+ process.chdir( repositoryPath );
34
+
35
+ const packageJson = getPackageJson();
36
+ const repositoryName = packageJson.name;
37
+
38
+ let newVersion;
39
+
40
+ // For the main package/repository use the version specified as `options.cwd`.
41
+ // For the rest packages, find a version in the changelog file.
42
+ if ( repositoryPath === cwd ) {
43
+ newVersion = options.version;
44
+ } else {
45
+ newVersion = findVersionInChangelog( options.changes, repositoryName );
46
+ }
47
+
48
+ // If these versions aren't equal, it means that the package is ready to release
49
+ // because we assume that a version from `package.json` is the latest.
50
+ if ( newVersion && packageJson.version !== newVersion ) {
51
+ packagesToRelease.set( repositoryName, {
52
+ previousVersion: packageJson.version,
53
+ version: newVersion
54
+ } );
55
+ }
56
+
57
+ return Promise.resolve();
58
+ }
59
+ };
60
+
61
+ // Search for a new version for specified package in the main changelog description.
62
+ // It must be defined as following:
63
+ //
64
+ // [packageName](https://www.npmjs.com/package/packageName): v0.0.1 => v1.0.0
65
+ //
66
+ // where:
67
+ // `v0.0.1` is the current version (already published),
68
+ // `v1.0.0` is the new version what we're looking for.
69
+ //
70
+ // or:
71
+ //
72
+ // [packageName](https://www.npmjs.com/package/packageName): v0.0.1
73
+ //
74
+ // where:
75
+ // `v0.0.1` is the current version (not published yet).
76
+ //
77
+ // The function handles pre-release, so, e.g. `v1.0.0.alpha.0` is also a proper input.
78
+ //
79
+ // Keep in mind that the dash (`-`) is a separator of the pre-release. Other characters are not allowed
80
+ // and npm will not publish a package if its version does not contain the symbol.
81
+ //
82
+ // @param {String} changelog Changes.
83
+ // @param {String} packageName Package to look.
84
+ // @returns {String|null} `null` if the version was not found.
85
+ function findVersionInChangelog( changelog, packageName ) {
86
+ // Pick: `x.y.z` or `x.y.z-prerelease.n`, assumptions: `x, y, z, n = { 0, 1, 2, ... }`.
87
+ const semVer = '\\d+\\.\\d+\\.\\d+(.[a-z\\d.]+)?';
88
+
89
+ const existingPackageRegExp = new RegExp( `\\[${ packageName.replace( '/', '\\/' ) }\\].*v(${ semVer })+\\ (=>) v(${ semVer })` );
90
+ // Groups:
91
+ // 1. Previous version.
92
+ // 2. Previous version - pre-release part.
93
+ // 3. "=>" character that suggests that the package exists on npm.
94
+ // 4. New version.
95
+ // 5. New version - pre-release part.
96
+
97
+ let match = changelog.match( existingPackageRegExp );
98
+
99
+ // The package exists on npm.
100
+ if ( match ) {
101
+ // The version does not match to `x.y.z-prerelease.n`, npm will not allow publish it.
102
+ if ( match[ 5 ] && !match[ 5 ].startsWith( '-' ) ) {
103
+ return null;
104
+ }
105
+
106
+ return match[ 4 ];
107
+ }
108
+
109
+ // At this stage, we know that it will be the first release of `packageName`.
110
+ // If the specified version is correct, let's use it.
111
+ const newPackageRegExp = new RegExp( `\\[${ packageName.replace( '/', '\\/' ) }\\].*v(${ semVer })` );
112
+ // Groups:
113
+ // 1. New version.
114
+ // 2. New version - pre-release part.
115
+
116
+ match = changelog.match( newPackageRegExp );
117
+
118
+ if ( !match ) {
119
+ return null;
120
+ }
121
+
122
+ // The version does not match to `x.y.z-prerelease.n`, npm will not allow publish it.
123
+ if ( match[ 2 ] && !match[ 2 ].startsWith( '-' ) ) {
124
+ return null;
125
+ }
126
+
127
+ return match[ 1 ];
128
+ }
129
+
130
+ /**
131
+ * @typedef {Object} ReleaseDetails
132
+ *
133
+ * @property {String} version The latest version of the package.
134
+ *
135
+ * @property {String|null} [previousVersion] Previous version of the package.
136
+ *
137
+ * @property {String} [changes] Description of changes for specified `version`. Should be taken from the changelog file.
138
+ *
139
+ * @property {String} [repositoryOwner] A name of organization that publishes the repository.
140
+ * E.g. for "ckeditor/ckeditor5" it is "ckeditor".
141
+ *
142
+ * @property {String} [repositoryName] A name of the repository.
143
+ * E.g. for "ckeditor/ckeditor5" it is "ckeditor5".
144
+ *
145
+ * @property {String} [npmVersion] The latest version of package published on NPM.
146
+ *
147
+ * @property {Boolean} [shouldReleaseOnNpm] Whether the package should be published on NPM.
148
+ *
149
+ * @property {String} [githubVersion] The latest version of package published on Github.
150
+ *
151
+ * @property {Boolean} [shouldReleaseOnGithub] Whether the package should have created a release on GitHub..
152
+ */
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const fs = require( 'fs' );
9
+ const path = require( 'path' );
10
+ const templatePath = path.join( __dirname, '..', 'templates' );
11
+ const { getTypeOrder } = require( './transformcommitutils' );
12
+
13
+ /**
14
+ * @param {Function|Object} transform
15
+ * @returns {Object}
16
+ */
17
+ module.exports = function getWriterOptions( transform ) {
18
+ return {
19
+ transform,
20
+ groupBy: 'type',
21
+ commitGroupsSort: sortFunction,
22
+ commitsSort: [ 'subject' ],
23
+ noteGroupsSort: sortFunction,
24
+ mainTemplate: fs.readFileSync( path.join( templatePath, 'template.hbs' ), 'utf-8' ),
25
+ headerPartial: fs.readFileSync( path.join( templatePath, 'header.hbs' ), 'utf-8' ),
26
+ commitPartial: fs.readFileSync( path.join( templatePath, 'commit.hbs' ), 'utf-8' ),
27
+ footerPartial: fs.readFileSync( path.join( templatePath, 'footer.hbs' ), 'utf-8' )
28
+ };
29
+ };
30
+
31
+ function sortFunction( a, b ) {
32
+ return getTypeOrder( a.title ) - getTypeOrder( b.title );
33
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ module.exports = {
9
+ mergePattern: /^Merge .*$/,
10
+ headerPattern: /^([^:]+): (.*)$/,
11
+ headerCorrespondence: [
12
+ 'type',
13
+ 'subject'
14
+ ],
15
+ noteKeywords: [
16
+ 'MAJOR BREAKING CHANGES',
17
+ 'MAJOR BREAKING CHANGE',
18
+ 'MINOR BREAKING CHANGES',
19
+ 'MINOR BREAKING CHANGE',
20
+ 'BREAKING CHANGES', // It will be treated as "MAJOR BREAKING CHANGES"
21
+ 'BREAKING CHANGE' // An alias for "BREAKING CHANGES".
22
+ ],
23
+ revertPattern: /^Revert:\s([\s\S]*?)\s*This reverts commit (\w*)\./,
24
+ revertCorrespondence: [ 'header', 'hash' ],
25
+ referenceActions: []
26
+ };