@ckeditor/ckeditor5-dev-release-tools 37.0.0 → 38.0.0-alpha.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.
@@ -9,23 +9,28 @@ const { tools } = require( '@ckeditor/ckeditor5-dev-utils' );
9
9
 
10
10
  /**
11
11
  * @param {Object} options
12
- * @param {String} options.version Version of the current release.
12
+ * @param {String|null} options.version Version of the current release.
13
13
  * @param {String} options.changes Changelog entries for the current release.
14
14
  * @param {Boolean} [options.ignoreBranchCheck=false] If set on true, branch checking will be skipped.
15
15
  * @param {String} [options.branch='master'] A name of the branch that should be used for releasing packages.
16
- * @returns {Array.<String>}
16
+ * @returns {Promise.<Array.<String>>}
17
17
  */
18
- module.exports = function validatePackageToRelease( options ) {
18
+ module.exports = async function validateRepositoryToRelease( options ) {
19
+ const {
20
+ version,
21
+ changes,
22
+ ignoreBranchCheck = false,
23
+ branch = 'master'
24
+ } = options;
19
25
  const errors = [];
20
- const branch = options.branch || 'master';
21
26
 
22
27
  // Check whether the repository is ready for the release.
23
- const status = exec( 'git status -sb', { verbosity: 'error' } ).trim();
28
+ const status = ( await exec( 'git status -sb' ) ).trim();
24
29
 
25
- if ( !options.ignoreBranchCheck ) {
30
+ if ( !ignoreBranchCheck ) {
26
31
  // Check whether current branch is "master".
27
- if ( !status.startsWith( `## ${ branch }...origin/${ branch }` ) ) {
28
- errors.push( `Not on ${ branch } branch.` );
32
+ if ( !status.startsWith( `## ${ branch }` ) ) {
33
+ errors.push( `Not on the "#${ branch }" branch.` );
29
34
  }
30
35
  }
31
36
 
@@ -42,13 +47,13 @@ module.exports = function validatePackageToRelease( options ) {
42
47
  }
43
48
 
44
49
  // Check whether the changelog entries are correct.
45
- if ( !options.changes ) {
46
- errors.push( `Cannot find changelog entries for version "${ options.version }".` );
50
+ if ( !changes ) {
51
+ errors.push( `Cannot find changelog entries for version "${ version }".` );
47
52
  }
48
53
 
49
54
  return errors;
50
55
 
51
- function exec( command ) {
52
- return tools.shExec( command, { verbosity: 'error' } );
56
+ async function exec( command ) {
57
+ return tools.shExec( command, { verbosity: 'error', async: true } );
53
58
  }
54
59
  };
package/package.json CHANGED
@@ -1,38 +1,31 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-dev-release-tools",
3
- "version": "37.0.0",
3
+ "version": "38.0.0-alpha.0",
4
4
  "description": "Tools used for releasing CKEditor 5 and related packages.",
5
5
  "keywords": [],
6
6
  "main": "lib/index.js",
7
7
  "dependencies": {
8
- "@ckeditor/ckeditor5-dev-utils": "^37.0.0",
8
+ "@ckeditor/ckeditor5-dev-utils": "^38.0.0-alpha.0",
9
9
  "@octokit/rest": "^17.9.2",
10
10
  "chalk": "^4.0.0",
11
11
  "cli-table": "^0.3.1",
12
+ "cli-columns": "^4.0.0",
12
13
  "compare-func": "^2.0.0",
13
14
  "concat-stream": "^2.0.0",
14
15
  "conventional-changelog-writer": "^4.0.16",
15
16
  "conventional-commits-filter": "^2.0.6",
16
17
  "conventional-commits-parser": "^3.1.0",
17
- "fs-extra": "^9.1.0",
18
18
  "diff": "^5.0.0",
19
+ "fs-extra": "^9.1.0",
19
20
  "git-raw-commits": "^2.0.7",
20
- "glob": "^7.1.6",
21
+ "glob": "^10.2.5",
21
22
  "inquirer": "^7.1.0",
22
23
  "lodash": "^4.17.15",
23
24
  "minimatch": "^3.0.4",
24
25
  "mkdirp": "^1.0.4",
25
26
  "parse-github-url": "^1.0.2",
26
- "semver": "^7.3.2"
27
- },
28
- "devDependencies": {
29
- "chai": "^4.2.0",
30
- "handlebars": "^4.7.6",
31
- "mockery": "^2.1.0",
32
- "mock-fs": "^5.1.2",
33
- "proxyquire": "^2.1.3",
34
- "sinon": "^9.2.4",
35
- "strip-ansi": "^6.0.0"
27
+ "semver": "^7.3.2",
28
+ "upath": "^2.0.1"
36
29
  },
37
30
  "engines": {
38
31
  "node": ">=16.0.0",
@@ -1,354 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, 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 chalk = require( 'chalk' );
10
- const { tools, logger } = require( '@ckeditor/ckeditor5-dev-utils' );
11
- const cli = require( '../utils/cli' );
12
- const versions = require( '../utils/versions' );
13
- const changelog = require( '../utils/changelog' );
14
- const displaySkippedPackages = require( '../utils/displayskippedpackages' );
15
- const executeOnPackages = require( '../utils/executeonpackages' );
16
- const getPackageJson = require( '../utils/getpackagejson' );
17
- const getPackagesToRelease = require( '../utils/getpackagestorelease' );
18
- const getPackagesPaths = require( '../utils/getpackagespaths' );
19
- const updateDependenciesVersions = require( '../utils/updatedependenciesversions' );
20
- const validatePackageToRelease = require( '../utils/validatepackagetorelease' );
21
-
22
- const BREAK_RELEASE_MESSAGE = 'You aborted updating versions. Why? Oh why?!';
23
-
24
- /**
25
- * Updates version of all subpackages found in specified path.
26
- *
27
- * This task does:
28
- * - finds paths to subpackages,
29
- * - updates versions of all dependencies (even if some packages will not be released, its version will be updated in released packages),
30
- * - bumps version of all packages.
31
- *
32
- * @param {String} options.cwd Current working directory (packages) from which all paths will be resolved.
33
- * @param {String|null} options.packages Where to look for other packages (dependencies). If `null`, only repository specified under
34
- * `options.cwd` will be used in the task.
35
- * @param {Array.<String>} [options.skipPackages=[]] Name of packages which won't be touched.
36
- * @param {Boolean} [options.dryRun=false] If set on true, all changes will be printed on the screen. Changes produced by commands like
37
- * `npm version` will be reverted. Every called command will be displayed.
38
- * @param {Boolean} [options.skipMainRepository=false] If set on true, package found in "cwd" will be skipped.
39
- * @param {String} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages.
40
- * @param {String} [options.changelogDirectory] An absolute path to the directory where the `CHANGELOG.md` file is saved. If not specified,
41
- * the `options.cwd` value will be used instead.
42
- * @param {Boolean} [options.skipUpdatingDependencies=false] Whether to skip updating version of dependencies between updated packages.
43
- * @returns {Promise} A collection with packages that were updated.
44
- */
45
- module.exports = async function bumpVersions( options ) {
46
- const cwd = process.cwd();
47
- const log = logger();
48
-
49
- const dryRun = Boolean( options.dryRun );
50
-
51
- const pathsCollection = getPackagesPaths( {
52
- cwd: options.cwd,
53
- packages: options.packages,
54
- skipPackages: options.skipPackages || [],
55
- skipMainRepository: options.skipMainRepository
56
- } );
57
-
58
- const changelogDirectory = options.changelogDirectory || options.cwd;
59
-
60
- const mainRepositoryVersion = versions.getLastFromChangelog( changelogDirectory );
61
- const mainChangelog = changelog.getChangesForVersion( mainRepositoryVersion, changelogDirectory );
62
- const releaseBranch = options.releaseBranch || 'master';
63
-
64
- logDryRun( '⚠️ DRY RUN mode ⚠️' );
65
- logDryRun( 'All changes made by this script will be reverted automatically.' );
66
- logProcess( 'Collecting packages which versions should be updated...' );
67
-
68
- // In order to avoid setting global variables, every function passes `packages` variable to another function.
69
-
70
- return getPackagesToRelease( pathsCollection.matched, { changes: mainChangelog, version: mainRepositoryVersion } )
71
- .then( packages => isAnythingForRelease( packages ) )
72
- .then( packages => confirmUpdate( packages ) )
73
- .then( packages => prepareDependenciesVersions( packages ) )
74
- .then( ( { packages, dependencies } ) => filterPackagesThatWillNotBeReleased( packages, dependencies ) )
75
- .then( ( { packages, dependencies } ) => updateDependenciesOfPackages( packages, dependencies ) )
76
- .then( packages => updateLatestChangesForMainRepository( packages, mainRepositoryVersion ) )
77
- .then( packages => validateRepository( packages ) )
78
- .then( packages => bumpVersion( packages ) )
79
- .then( () => {
80
- process.chdir( cwd );
81
-
82
- logProcess( `Finished updating versions of ${ chalk.underline( pathsCollection.matched.size ) } package(s).` );
83
- logDryRun( 'Because of the DRY RUN mode, nothing has been changed. All changes were reverted.' );
84
-
85
- return Promise.resolve( pathsCollection.matched );
86
- } )
87
- .catch( err => {
88
- process.chdir( cwd );
89
-
90
- // A user did not confirm the release process.
91
- if ( err instanceof Error && err.message === BREAK_RELEASE_MESSAGE ) {
92
- logProcess( 'Updating has been aborted.' );
93
-
94
- return Promise.resolve();
95
- }
96
-
97
- log.error( err.message );
98
-
99
- process.exitCode = -1;
100
- } );
101
-
102
- // Displays packages that won't match to specified criteria and checks whether there is at least one package
103
- // that should update its version.
104
- //
105
- // @params {Map.<String, ReleaseDetails>} packages
106
- // @returns {Map.<String, ReleaseDetails>}
107
- function isAnythingForRelease( packages ) {
108
- logProcess( 'Checking whether is there anything for updating...' );
109
-
110
- displaySkippedPackages( pathsCollection.skipped );
111
-
112
- if ( packages.size === 0 ) {
113
- throw new Error( 'None of the packages contains any changes since its last release. Aborting.' );
114
- }
115
-
116
- return packages;
117
- }
118
-
119
- // Asks a user whether the process should be continued.
120
- //
121
- // @params {Map.<String, ReleaseDetails>} packages
122
- // @returns {Promise.<Map.<String, ReleaseDetails>>}
123
- function confirmUpdate( packages ) {
124
- logProcess( 'Should we continue?' );
125
-
126
- return cli.confirmUpdatingVersions( packages )
127
- .then( isConfirmed => {
128
- if ( !isConfirmed ) {
129
- throw new Error( BREAK_RELEASE_MESSAGE );
130
- }
131
-
132
- return packages;
133
- } );
134
- }
135
-
136
- // Prepare a map that contains new versions of packages. It will be used for updating dependencies' version during the release.
137
- //
138
- // This map contains new versions of all packages that will be released and current version of all packages which won't match to
139
- // specified criteria or won't be released (no changes between current and previous release).
140
- //
141
- // @params {Map.<String, ReleaseDetails>} packages
142
- // @returns {Object}
143
- function prepareDependenciesVersions( packages ) {
144
- logProcess( 'Preparing to updating versions of dependencies...' );
145
-
146
- const dependencies = new Map();
147
-
148
- // For every package that will be released, save its name and the latest version.
149
- for ( const [ packageName, { version } ] of packages ) {
150
- dependencies.set( packageName, version );
151
- }
152
-
153
- // Packages which won't match to specified criteria won't be released but we want to update their
154
- // versions in packages where skipped packages are defined as dependencies.
155
- for ( const packagePath of pathsCollection.skipped ) {
156
- const packageJson = getPackageJson( packagePath );
157
-
158
- dependencies.set( packageJson.name, packageJson.version );
159
- }
160
-
161
- // Even if some packages match to specified criteria but won't be releases because of no changes,
162
- // we want to update their version in packages where they are defined as dependency.
163
- for ( const packagePath of pathsCollection.matched ) {
164
- const packageJson = getPackageJson( packagePath );
165
-
166
- if ( !dependencies.has( packageJson.name ) ) {
167
- dependencies.set( packageJson.name, packageJson.version );
168
- }
169
- }
170
-
171
- return { packages, dependencies };
172
- }
173
-
174
- // Filter out packages which won't be released.
175
- //
176
- // @params {Map.<String, ReleaseDetails>} packages
177
- // @params {Map} dependencies
178
- // @returns {Object}
179
- function filterPackagesThatWillNotBeReleased( packages, dependencies ) {
180
- logDryRun( 'Filtering out packages that will not be updated...' );
181
-
182
- for ( const pathToPackage of pathsCollection.matched ) {
183
- const packageName = getPackageJson( pathToPackage ).name;
184
-
185
- if ( !packages.has( packageName ) ) {
186
- pathsCollection.matched.delete( pathToPackage );
187
- }
188
- }
189
-
190
- return { packages, dependencies };
191
- }
192
-
193
- // Update versions of all dependencies.
194
- //
195
- // @params {Map.<String, ReleaseDetails>} packages
196
- // @params {Map} dependencies
197
- // @returns {Promise.<Map.<String, ReleaseDetails>>}
198
- function updateDependenciesOfPackages( packages, dependencies ) {
199
- if ( options.skipUpdatingDependencies ) {
200
- logProcess( 'Skipping updating dependencies...' );
201
-
202
- return Promise.resolve( packages );
203
- }
204
-
205
- logProcess( 'Updating dependencies for packages that will be released...' );
206
- let hasUpdatedAnyPackage = false;
207
-
208
- return executeOnPackages( pathsCollection.matched, repositoryPath => {
209
- process.chdir( repositoryPath );
210
-
211
- const packageJson = getPackageJson( repositoryPath );
212
-
213
- log.info( `\nUpdating dependencies for "${ chalk.underline( packageJson.name ) }"...` );
214
-
215
- updateDependenciesVersions( dependencies, path.join( repositoryPath, 'package.json' ) );
216
-
217
- if ( exec( 'git diff --name-only package.json' ).trim().length ) {
218
- if ( dryRun ) {
219
- logDryRun( 'These changes would be committed.' );
220
-
221
- log.info( chalk.grey( exec( 'git diff --word-diff package.json' ) ) );
222
- exec( 'git checkout package.json' );
223
- } else {
224
- hasUpdatedAnyPackage = true;
225
-
226
- exec( 'git add package.json' );
227
- }
228
- }
229
-
230
- return Promise.resolve();
231
- } ).then( () => {
232
- process.chdir( cwd );
233
-
234
- if ( hasUpdatedAnyPackage ) {
235
- exec( 'git commit -m "Internal: Updated dependencies. [skip ci]"' );
236
- }
237
-
238
- return packages;
239
- } );
240
- }
241
-
242
- // Gather descriptions of the release for all packages.
243
- //
244
- // @params {Map.<String, ReleaseDetails>} packages
245
- // @returns {<Map.<String, ReleaseDetails>}
246
- function updateLatestChangesForMainRepository( packages, changes ) {
247
- logProcess( 'Updating changes for the main repository...' );
248
-
249
- const packageJson = getPackageJson( options.cwd );
250
- const releaseDetails = packages.get( packageJson.name );
251
-
252
- releaseDetails.changes = changes;
253
-
254
- return packages;
255
- }
256
-
257
- // Validate the main repository.
258
- //
259
- // @params {Map.<String, ReleaseDetails>} packages
260
- // @returns {Map.<String, ReleaseDetails>}
261
- function validateRepository( packages ) {
262
- logProcess( 'Validating the main repository...' );
263
-
264
- const mainPackageJson = getPackageJson( options.cwd );
265
- const releaseDetails = packages.get( mainPackageJson.name );
266
-
267
- const errors = validatePackageToRelease( {
268
- branch: releaseBranch,
269
- changes: releaseDetails.changes,
270
- version: releaseDetails.version
271
- } );
272
-
273
- if ( errors.length ) {
274
- log.error( `‼️ ${ chalk.underline( mainPackageJson.name ) }` );
275
- errors.forEach( err => {
276
- log.error( '* ' + err );
277
- } );
278
-
279
- throw new Error( 'Updating has been aborted due to errors.' );
280
- }
281
-
282
- return packages;
283
- }
284
-
285
- // Updates versions of packages.
286
- //
287
- // @params {Map.<String, ReleaseDetails>} packages
288
- // @returns {Promise.<Map.<String, ReleaseDetails>>}
289
- function bumpVersion( packages ) {
290
- logProcess( 'Tagging new versions of packages...' );
291
-
292
- let numberOfCommitsBeforeVersioning;
293
-
294
- // Based on number of commits before and after executing `npm version`, we will be able to revert all changes
295
- // that have been done. It allows reverting changes done by npm `preversion` and/or `postversion` hooks.
296
- if ( dryRun ) {
297
- numberOfCommitsBeforeVersioning = Number( exec( 'git rev-list --count HEAD' ) );
298
- }
299
-
300
- return executeOnPackages( pathsCollection.matched, bumpVersionForSinglePackage )
301
- .then( () => {
302
- process.chdir( options.cwd );
303
-
304
- const packageJson = getPackageJson( options.cwd );
305
- const releaseDetails = packages.get( packageJson.name );
306
-
307
- log.info( '\nCommitting and tagging changes...' );
308
-
309
- exec( `git commit --message "Release: v${ releaseDetails.version }."` );
310
- exec( `git tag v${ releaseDetails.version }` );
311
-
312
- if ( dryRun ) {
313
- const numberOfCommitsAfterVersioning = Number( exec( 'git rev-list --count HEAD' ) );
314
- const commitsToRevert = numberOfCommitsAfterVersioning - numberOfCommitsBeforeVersioning;
315
-
316
- logDryRun( `Reverting changes made by "npm version". Removing a tag and ${ commitsToRevert } commit(s).` );
317
- exec( `git reset --hard HEAD~${ commitsToRevert }` );
318
- exec( `git tag -d v${ releaseDetails.version }` );
319
- }
320
-
321
- return packages;
322
- } );
323
-
324
- function bumpVersionForSinglePackage( repositoryPath ) {
325
- process.chdir( repositoryPath );
326
-
327
- const packageJson = getPackageJson( repositoryPath );
328
- const releaseDetails = packages.get( packageJson.name );
329
-
330
- log.info( `\nBumping version for "${ chalk.underline( packageJson.name ) }"...` );
331
-
332
- exec( `npm version ${ releaseDetails.version } --no-git-tag-version --no-workspaces-update` );
333
- exec( 'git add .' );
334
- }
335
- }
336
-
337
- function exec( command ) {
338
- if ( dryRun ) {
339
- log.info( `⚠️ ${ chalk.grey( 'Execute:' ) } "${ chalk.cyan( command ) }" in "${ chalk.grey.italic( process.cwd() ) }".` );
340
- }
341
-
342
- return tools.shExec( command, { verbosity: 'error' } );
343
- }
344
-
345
- function logProcess( message ) {
346
- log.info( '\n📍 ' + chalk.cyan( message ) );
347
- }
348
-
349
- function logDryRun( message ) {
350
- if ( dryRun ) {
351
- log.info( 'ℹ️ ' + chalk.yellow( message ) );
352
- }
353
- }
354
- };