@ckeditor/ckeditor5-dev-release-tools 37.0.1 → 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.
- package/lib/index.js +22 -10
- package/lib/tasks/cleanuppackages.js +175 -0
- package/lib/tasks/commitandtag.js +28 -0
- package/lib/tasks/creategithubrelease.js +104 -0
- package/lib/tasks/preparerepository.js +149 -0
- package/lib/tasks/publishpackages.js +69 -0
- package/lib/tasks/push.js +29 -0
- package/lib/tasks/reassignnpmtags.js +93 -0
- package/lib/tasks/updatedependencies.js +84 -0
- package/lib/tasks/updateversions.js +117 -0
- package/lib/utils/assertfilestopublish.js +88 -0
- package/lib/utils/assertnpmauthorization.js +26 -0
- package/lib/utils/assertnpmtag.js +60 -0
- package/lib/utils/assertpackages.js +34 -0
- package/lib/utils/executeinparallel.js +167 -0
- package/lib/utils/getpackagejson.js +8 -4
- package/lib/utils/parallelworker.js +25 -0
- package/lib/utils/publishpackagesonnpm.js +35 -0
- package/lib/utils/{validatepackagetorelease.js → validaterepositorytorelease.js} +17 -12
- package/package.json +7 -14
- package/lib/tasks/bumpversions.js +0 -354
- package/lib/tasks/preparepackages.js +0 -257
- package/lib/tasks/releasesubrepositories.js +0 -1001
- package/lib/tasks/updateckeditor5dependencies.js +0 -392
- package/lib/utils/creategithubrelease.js +0 -37
- package/lib/utils/executeonpackages.js +0 -26
- package/lib/utils/getpackagestorelease.js +0 -152
- package/lib/utils/updatedependenciesversions.js +0 -32
package/lib/index.js
CHANGED
|
@@ -5,28 +5,40 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const releaseSubRepositories = require( './tasks/releasesubrepositories' );
|
|
9
|
-
const preparePackages = require( './tasks/preparepackages' );
|
|
10
|
-
const bumpVersions = require( './tasks/bumpversions' );
|
|
11
8
|
const generateChangelogForSinglePackage = require( './tasks/generatechangelogforsinglepackage' );
|
|
12
9
|
const generateChangelogForMonoRepository = require( './tasks/generatechangelogformonorepository' );
|
|
13
|
-
const
|
|
14
|
-
const
|
|
10
|
+
const updateDependencies = require( './tasks/updatedependencies' );
|
|
11
|
+
const commitAndTag = require( './tasks/commitandtag' );
|
|
12
|
+
const createGithubRelease = require( './tasks/creategithubrelease' );
|
|
13
|
+
const reassignNpmTags = require( './tasks/reassignnpmtags' );
|
|
14
|
+
const prepareRepository = require( './tasks/preparerepository' );
|
|
15
|
+
const push = require( './tasks/push' );
|
|
16
|
+
const publishPackages = require( './tasks/publishpackages' );
|
|
17
|
+
const updateVersions = require( './tasks/updateversions' );
|
|
18
|
+
const cleanUpPackages = require( './tasks/cleanuppackages' );
|
|
15
19
|
const { getLastFromChangelog, getCurrent, getLastTagFromGit } = require( './utils/versions' );
|
|
16
20
|
const { getChangesForVersion, getChangelog, saveChangelog } = require( './utils/changelog' );
|
|
21
|
+
const executeInParallel = require( './utils/executeinparallel' );
|
|
22
|
+
const validateRepositoryToRelease = require( './utils/validaterepositorytorelease' );
|
|
17
23
|
|
|
18
24
|
module.exports = {
|
|
19
|
-
releaseSubRepositories,
|
|
20
|
-
preparePackages,
|
|
21
|
-
bumpVersions,
|
|
22
25
|
generateChangelogForSinglePackage,
|
|
23
26
|
generateChangelogForMonoRepository,
|
|
24
|
-
|
|
27
|
+
updateDependencies,
|
|
28
|
+
updateVersions,
|
|
29
|
+
prepareRepository,
|
|
30
|
+
commitAndTag,
|
|
31
|
+
createGithubRelease,
|
|
32
|
+
push,
|
|
33
|
+
cleanUpPackages,
|
|
34
|
+
publishPackages,
|
|
35
|
+
reassignNpmTags,
|
|
36
|
+
executeInParallel,
|
|
25
37
|
getLastFromChangelog,
|
|
26
38
|
getCurrent,
|
|
27
39
|
getLastTagFromGit,
|
|
28
40
|
getChangesForVersion,
|
|
29
41
|
getChangelog,
|
|
30
42
|
saveChangelog,
|
|
31
|
-
|
|
43
|
+
validateRepositoryToRelease
|
|
32
44
|
};
|
|
@@ -0,0 +1,175 @@
|
|
|
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 fs = require( 'fs-extra' );
|
|
9
|
+
const upath = require( 'upath' );
|
|
10
|
+
const { glob } = require( 'glob' );
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The purpose of the script is to clean all packages prepared for the release. The cleaning consists of two stages:
|
|
14
|
+
*
|
|
15
|
+
* - Removes unnecessary files and empty directories from the package directory. Unnecessary files are those not matched by any entry from
|
|
16
|
+
* the `files` field in `package.json`. Some files are never removed, even if they are not matched by the `files` patterns:
|
|
17
|
+
* - `package.json`,
|
|
18
|
+
* - `LICENSE.md`
|
|
19
|
+
* - `README.md`
|
|
20
|
+
* - file pointed by the `main` field from `package.json`
|
|
21
|
+
* - file pointed by the `types` field from `package.json`
|
|
22
|
+
* - Removes unnecessary fields from the `package.json` file.
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} options
|
|
25
|
+
* @param {String} options.packagesDirectory Relative path to a location of packages to be cleaned up.
|
|
26
|
+
* @param {Array.<String>} [options.packageJsonFieldsToRemove] Fields to remove from `package.json`. If not set, a predefined list is used.
|
|
27
|
+
* @param {String} [options.cwd] Current working directory from which all paths will be resolved.
|
|
28
|
+
* @returns {Promise}
|
|
29
|
+
*/
|
|
30
|
+
module.exports = async function cleanUpPackages( options ) {
|
|
31
|
+
const { packagesDirectory, packageJsonFieldsToRemove, cwd } = parseOptions( options );
|
|
32
|
+
|
|
33
|
+
const packageJsonPaths = await glob( '*/package.json', {
|
|
34
|
+
cwd: upath.join( cwd, packagesDirectory ),
|
|
35
|
+
nodir: true,
|
|
36
|
+
absolute: true
|
|
37
|
+
} );
|
|
38
|
+
|
|
39
|
+
for ( const packageJsonPath of packageJsonPaths ) {
|
|
40
|
+
const packagePath = upath.dirname( packageJsonPath );
|
|
41
|
+
const packageJson = await fs.readJson( packageJsonPath );
|
|
42
|
+
|
|
43
|
+
await cleanUpPackageDirectory( packageJson, packagePath );
|
|
44
|
+
cleanUpPackageJson( packageJson, packageJsonFieldsToRemove );
|
|
45
|
+
|
|
46
|
+
await fs.writeJson( packageJsonPath, packageJson, { spaces: 2 } );
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Prepares the configuration options for the script.
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} options
|
|
54
|
+
* @param {String} options.packagesDirectory
|
|
55
|
+
* @param {Array.<String>} [options.packageJsonFieldsToRemove=['devDependencies','depcheckIgnore','scripts','private']]
|
|
56
|
+
* @param {String} [options.cwd=process.cwd()]
|
|
57
|
+
* @returns {Object}
|
|
58
|
+
*/
|
|
59
|
+
function parseOptions( options ) {
|
|
60
|
+
const {
|
|
61
|
+
packagesDirectory,
|
|
62
|
+
packageJsonFieldsToRemove = [ 'devDependencies', 'depcheckIgnore', 'scripts', 'private' ],
|
|
63
|
+
cwd = process.cwd()
|
|
64
|
+
} = options;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
packagesDirectory: upath.normalizeTrim( packagesDirectory ),
|
|
68
|
+
packageJsonFieldsToRemove,
|
|
69
|
+
cwd: upath.normalizeTrim( cwd )
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Removes unnecessary files and directories from the package directory.
|
|
75
|
+
*
|
|
76
|
+
* @param {Object} packageJson
|
|
77
|
+
* @param {String} packagePath
|
|
78
|
+
* @returns {Promise}
|
|
79
|
+
*/
|
|
80
|
+
async function cleanUpPackageDirectory( packageJson, packagePath ) {
|
|
81
|
+
if ( packageJson.files ) {
|
|
82
|
+
// Find and remove files that don't match the `files` field in the `package.json`.
|
|
83
|
+
const files = await glob( '**', {
|
|
84
|
+
cwd: packagePath,
|
|
85
|
+
absolute: true,
|
|
86
|
+
nodir: true,
|
|
87
|
+
dot: true,
|
|
88
|
+
ignore: [
|
|
89
|
+
'README.md',
|
|
90
|
+
'LICENSE.md',
|
|
91
|
+
'package.json',
|
|
92
|
+
...getIgnoredFilePatterns( packageJson )
|
|
93
|
+
]
|
|
94
|
+
} );
|
|
95
|
+
|
|
96
|
+
for ( const file of files ) {
|
|
97
|
+
await fs.remove( file );
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Find and remove empty directories in the package directory.
|
|
102
|
+
const globResults = await glob( '**/', {
|
|
103
|
+
cwd: packagePath,
|
|
104
|
+
absolute: true,
|
|
105
|
+
dot: true
|
|
106
|
+
} );
|
|
107
|
+
const directories = globResults
|
|
108
|
+
.map( path => upath.normalize( path ) )
|
|
109
|
+
.sort( sortPathsFromDeepestFirst );
|
|
110
|
+
|
|
111
|
+
for ( const directory of directories ) {
|
|
112
|
+
const isEmpty = ( await fs.readdir( directory ) ).length === 0;
|
|
113
|
+
|
|
114
|
+
if ( isEmpty ) {
|
|
115
|
+
await fs.remove( directory );
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Remove `node_modules`.
|
|
120
|
+
await fs.remove( upath.join( packagePath, 'node_modules' ) );
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Creates an array of patterns to ignore for the `glob` calls.
|
|
125
|
+
*
|
|
126
|
+
* @param {Object} packageJson
|
|
127
|
+
* @returns {Array.<String>}
|
|
128
|
+
*/
|
|
129
|
+
function getIgnoredFilePatterns( packageJson ) {
|
|
130
|
+
// The patterns supported by `package.json` in the `files` field do not correspond 1:1 to the patterns expected by the `glob`.
|
|
131
|
+
// For this reason, we always treat each pattern from `files` as if it was the beginning of a path to match - not just a final path.
|
|
132
|
+
//
|
|
133
|
+
// Example: for the entry `src` we prepare the `src/**` pattern for `glob`.
|
|
134
|
+
//
|
|
135
|
+
// If the globstar pattern (`**`) is alone in a path portion, then it matches zero or more directories and subdirectories.
|
|
136
|
+
const patterns = packageJson.files.map( pattern => pattern + '/**' );
|
|
137
|
+
|
|
138
|
+
if ( packageJson.main ) {
|
|
139
|
+
patterns.push( packageJson.main );
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if ( packageJson.types ) {
|
|
143
|
+
patterns.push( packageJson.types );
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return patterns;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Removes unnecessary fields from the `package.json`.
|
|
151
|
+
*
|
|
152
|
+
* @param {Object} packageJson
|
|
153
|
+
* @param {Array.<String>} packageJsonFieldsToRemove
|
|
154
|
+
*/
|
|
155
|
+
function cleanUpPackageJson( packageJson, packageJsonFieldsToRemove ) {
|
|
156
|
+
for ( const key of Object.keys( packageJson ) ) {
|
|
157
|
+
if ( packageJsonFieldsToRemove.includes( key ) ) {
|
|
158
|
+
delete packageJson[ key ];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Sort function that defines the order of the paths. It sorts paths from the most nested ones first.
|
|
165
|
+
*
|
|
166
|
+
* @param {String} firstPath
|
|
167
|
+
* @param {String} secondPath
|
|
168
|
+
* @returns {Number}
|
|
169
|
+
*/
|
|
170
|
+
function sortPathsFromDeepestFirst( firstPath, secondPath ) {
|
|
171
|
+
const firstPathSegments = firstPath.split( '/' ).length;
|
|
172
|
+
const secondPathSegments = secondPath.split( '/' ).length;
|
|
173
|
+
|
|
174
|
+
return secondPathSegments - firstPathSegments;
|
|
175
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { tools } = require( '@ckeditor/ckeditor5-dev-utils' );
|
|
9
|
+
const { toUnix } = require( 'upath' );
|
|
10
|
+
const { glob } = require( 'glob' );
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a commit and a tag for specified version.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} options
|
|
16
|
+
* @param {String} options.version The commit will contain this param in its message and the tag will have a `v` prefix.
|
|
17
|
+
* @param {Array.<String>} options.files Array of glob patterns for files to be added to the release commit.
|
|
18
|
+
* @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved.
|
|
19
|
+
* @returns {Promise}
|
|
20
|
+
*/
|
|
21
|
+
module.exports = async function commitAndTag( { version, files, cwd = process.cwd() } ) {
|
|
22
|
+
const normalizedCwd = toUnix( cwd );
|
|
23
|
+
const filePathsToAdd = await glob( files, { cwd: normalizedCwd, absolute: true, nodir: true } );
|
|
24
|
+
|
|
25
|
+
await tools.shExec( `git add ${ filePathsToAdd.join( ' ' ) }`, { cwd: normalizedCwd, async: true } );
|
|
26
|
+
await tools.shExec( `git commit --message "Release: v${ version }."`, { cwd: normalizedCwd, async: true } );
|
|
27
|
+
await tools.shExec( `git tag v${ version }`, { cwd: normalizedCwd, async: true } );
|
|
28
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
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 { Octokit } = require( '@octokit/rest' );
|
|
9
|
+
const semver = require( 'semver' );
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a GitHub release.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} options
|
|
15
|
+
* @param {String} options.token Token used to authenticate with GitHub.
|
|
16
|
+
* @param {String} options.version Name of tag connected with the release.
|
|
17
|
+
* @param {String} options.repositoryOwner Owner of the repository.
|
|
18
|
+
* @param {String} options.repositoryName Repository name.
|
|
19
|
+
* @param {String} options.description Description of the release.
|
|
20
|
+
* @returns {Promise.<String>}
|
|
21
|
+
*/
|
|
22
|
+
module.exports = async function createGithubRelease( options ) {
|
|
23
|
+
const {
|
|
24
|
+
token,
|
|
25
|
+
version,
|
|
26
|
+
repositoryOwner,
|
|
27
|
+
repositoryName,
|
|
28
|
+
description
|
|
29
|
+
} = options;
|
|
30
|
+
|
|
31
|
+
const github = new Octokit( {
|
|
32
|
+
version: '3.0.0',
|
|
33
|
+
auth: `token ${ token }`
|
|
34
|
+
} );
|
|
35
|
+
|
|
36
|
+
if ( await shouldCreateRelease( github, repositoryOwner, repositoryName, version ) ) {
|
|
37
|
+
await github.repos.createRelease( {
|
|
38
|
+
tag_name: `v${ version }`,
|
|
39
|
+
owner: repositoryOwner,
|
|
40
|
+
repo: repositoryName,
|
|
41
|
+
body: description,
|
|
42
|
+
prerelease: getVersionTag( version ) !== 'latest'
|
|
43
|
+
} );
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return `https://github.com/${ repositoryOwner }/${ repositoryName }/releases/tag/v${ version }`;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns an npm tag based on the specified release version.
|
|
51
|
+
*
|
|
52
|
+
* @param {String} version
|
|
53
|
+
* @returns {String}
|
|
54
|
+
*/
|
|
55
|
+
function getVersionTag( version ) {
|
|
56
|
+
const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ];
|
|
57
|
+
|
|
58
|
+
return versionTag;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolves a promise containing a flag if the GitHub contains the release page for given version.
|
|
63
|
+
*
|
|
64
|
+
* @param {Octokit} github
|
|
65
|
+
* @param {String} repositoryOwner
|
|
66
|
+
* @param {String} repositoryName
|
|
67
|
+
* @param {String} version
|
|
68
|
+
* @returns {Promise.<boolean>}
|
|
69
|
+
*/
|
|
70
|
+
async function shouldCreateRelease( github, repositoryOwner, repositoryName, version ) {
|
|
71
|
+
const releaseDetails = await getLastRelease( github, repositoryOwner, repositoryName );
|
|
72
|
+
|
|
73
|
+
// It can be `null` if there is no releases on GitHub.
|
|
74
|
+
let githubVersion = releaseDetails.data.tag_name;
|
|
75
|
+
|
|
76
|
+
if ( githubVersion ) {
|
|
77
|
+
githubVersion = releaseDetails.data.tag_name.replace( /^v/, '' );
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If versions are different, we are ready to create a new release.
|
|
81
|
+
return githubVersion !== version;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getLastRelease( github, repositoryOwner, repositoryName ) {
|
|
85
|
+
const requestParams = {
|
|
86
|
+
owner: repositoryOwner,
|
|
87
|
+
repo: repositoryName
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return github.repos.getLatestRelease( requestParams )
|
|
91
|
+
.catch( err => {
|
|
92
|
+
// If the "last release" returned the 404 error page, it means that this release
|
|
93
|
+
// will be the first one for specified `repositoryOwner/repositoryName` package.
|
|
94
|
+
if ( err.status == 404 ) {
|
|
95
|
+
return Promise.resolve( {
|
|
96
|
+
data: {
|
|
97
|
+
tag_name: null
|
|
98
|
+
}
|
|
99
|
+
} );
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return Promise.reject( err );
|
|
103
|
+
} );
|
|
104
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
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 fs = require( 'fs-extra' );
|
|
9
|
+
const glob = require( 'glob' );
|
|
10
|
+
const upath = require( 'upath' );
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The goal is to prepare the release directory containing the packages we want to publish.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} options
|
|
16
|
+
* @param {String} options.outputDirectory Relative path to the destination directory where packages will be stored.
|
|
17
|
+
* @param {String} [options.cwd] Root of the repository to prepare. `process.cwd()` by default.
|
|
18
|
+
* @param {String} [options.packagesDirectory] Relative path to a location of packages.
|
|
19
|
+
* If specified, all of the found packages will be copied.
|
|
20
|
+
* @param {Array.<String>} [options.packagesToCopy] List of packages that should be processed.
|
|
21
|
+
* If not specified, all packages found in `packagesDirectory` are considered.
|
|
22
|
+
* @param {RootPackageJson} [options.rootPackageJson] Object containing values to use in the created the `package.json` file.
|
|
23
|
+
* If not specified, the root package will not be created.
|
|
24
|
+
* @returns {Promise}
|
|
25
|
+
*/
|
|
26
|
+
module.exports = async function prepareRepository( options ) {
|
|
27
|
+
const {
|
|
28
|
+
outputDirectory,
|
|
29
|
+
packagesDirectory,
|
|
30
|
+
packagesToCopy,
|
|
31
|
+
rootPackageJson,
|
|
32
|
+
cwd = process.cwd()
|
|
33
|
+
} = options;
|
|
34
|
+
|
|
35
|
+
if ( !rootPackageJson && !packagesDirectory ) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const outputDirectoryPath = upath.join( cwd, outputDirectory );
|
|
40
|
+
await fs.ensureDir( outputDirectoryPath );
|
|
41
|
+
const outputDirContent = await fs.readdir( outputDirectoryPath );
|
|
42
|
+
|
|
43
|
+
if ( outputDirContent.length ) {
|
|
44
|
+
throw new Error( `Output directory is not empty: "${ outputDirectoryPath }".` );
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const copyPromises = [];
|
|
48
|
+
|
|
49
|
+
if ( rootPackageJson ) {
|
|
50
|
+
validateRootPackage( rootPackageJson );
|
|
51
|
+
|
|
52
|
+
const copyRootItemsPromises = await processRootPackage( {
|
|
53
|
+
cwd,
|
|
54
|
+
rootPackageJson,
|
|
55
|
+
outputDirectoryPath
|
|
56
|
+
} );
|
|
57
|
+
|
|
58
|
+
copyPromises.push( ...copyRootItemsPromises );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ( packagesDirectory ) {
|
|
62
|
+
const copyPackagesPromises = await processMonorepoPackages( {
|
|
63
|
+
cwd,
|
|
64
|
+
packagesDirectory,
|
|
65
|
+
packagesToCopy,
|
|
66
|
+
outputDirectoryPath
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
copyPromises.push( ...copyPackagesPromises );
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return Promise.all( copyPromises );
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {Object} packageJson
|
|
77
|
+
* @param {String} [packageJson.name]
|
|
78
|
+
* @param {Array.<String>} [packageJson.files]
|
|
79
|
+
*/
|
|
80
|
+
function validateRootPackage( packageJson ) {
|
|
81
|
+
if ( !packageJson.name ) {
|
|
82
|
+
throw new Error( '"rootPackageJson" option object must have a "name" field.' );
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if ( !packageJson.files ) {
|
|
86
|
+
throw new Error( '"rootPackageJson" option object must have a "files" field.' );
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {Object} options
|
|
92
|
+
* @param {String} options.cwd
|
|
93
|
+
* @param {RootPackageJson} options.rootPackageJson
|
|
94
|
+
* @param {String} options.outputDirectoryPath
|
|
95
|
+
* @returns {Promise}
|
|
96
|
+
*/
|
|
97
|
+
async function processRootPackage( { cwd, rootPackageJson, outputDirectoryPath } ) {
|
|
98
|
+
const rootPackageOutputPath = upath.join( outputDirectoryPath, rootPackageJson.name );
|
|
99
|
+
const pkgJsonOutputPath = upath.join( rootPackageOutputPath, 'package.json' );
|
|
100
|
+
|
|
101
|
+
await fs.ensureDir( rootPackageOutputPath );
|
|
102
|
+
await fs.writeJson( pkgJsonOutputPath, rootPackageJson, { spaces: 2, EOL: '\n' } );
|
|
103
|
+
|
|
104
|
+
return glob.sync( rootPackageJson.files ).map( absoluteFilePath => {
|
|
105
|
+
const relativeFilePath = upath.relative( cwd, absoluteFilePath );
|
|
106
|
+
const absoluteFileOutputPath = upath.join( rootPackageOutputPath, relativeFilePath );
|
|
107
|
+
|
|
108
|
+
return fs.copy( absoluteFilePath, absoluteFileOutputPath );
|
|
109
|
+
} );
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {Object} options
|
|
114
|
+
* @param {String} options.cwd
|
|
115
|
+
* @param {String} options.packagesDirectory
|
|
116
|
+
* @param {String} options.outputDirectoryPath
|
|
117
|
+
* @param {Array.<String>} [options.packagesToCopy]
|
|
118
|
+
* @returns {Promise}
|
|
119
|
+
*/
|
|
120
|
+
async function processMonorepoPackages( { cwd, packagesDirectory, packagesToCopy, outputDirectoryPath } ) {
|
|
121
|
+
const packagesDirectoryPath = upath.join( cwd, packagesDirectory );
|
|
122
|
+
const packageDirs = packagesToCopy || await fs.readdir( packagesDirectoryPath );
|
|
123
|
+
|
|
124
|
+
return packageDirs.map( async packageDir => {
|
|
125
|
+
const packagePath = upath.join( packagesDirectoryPath, packageDir );
|
|
126
|
+
const isDir = ( await fs.lstat( packagePath ) ).isDirectory();
|
|
127
|
+
|
|
128
|
+
if ( !isDir ) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const pkgJsonPath = upath.join( packagePath, 'package.json' );
|
|
133
|
+
const hasPkgJson = await fs.exists( pkgJsonPath );
|
|
134
|
+
|
|
135
|
+
if ( !hasPkgJson ) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return fs.copy( packagePath, upath.join( outputDirectoryPath, packageDir ) );
|
|
140
|
+
} );
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @typedef {Object} RootPackageJson
|
|
145
|
+
*
|
|
146
|
+
* @param {String} options.rootPackageJson.name Name of the package. Required value.
|
|
147
|
+
*
|
|
148
|
+
* @param {Array.<String>} options.rootPackageJson.files Array containing a list of files or directories to copy. Required value.
|
|
149
|
+
*/
|
|
@@ -0,0 +1,69 @@
|
|
|
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 upath = require( 'upath' );
|
|
9
|
+
const { glob } = require( 'glob' );
|
|
10
|
+
const assertNpmAuthorization = require( '../utils/assertnpmauthorization' );
|
|
11
|
+
const assertPackages = require( '../utils/assertpackages' );
|
|
12
|
+
const assertNpmTag = require( '../utils/assertnpmtag' );
|
|
13
|
+
const assertFilesToPublish = require( '../utils/assertfilestopublish' );
|
|
14
|
+
const publishPackagesOnNpm = require( '../utils/publishpackagesonnpm' );
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The purpose of the script is to validate the packages prepared for the release and then release them on npm.
|
|
18
|
+
*
|
|
19
|
+
* The validation contains the following steps in each package:
|
|
20
|
+
* - User must be logged to npm on the specified account.
|
|
21
|
+
* - The package directory mmust contain `package.json` file.
|
|
22
|
+
* - All other files expected to be released must exist in the package directory.
|
|
23
|
+
* - The npm tag must match the tag calculated from the package version.
|
|
24
|
+
*
|
|
25
|
+
* When the validation for each package passes, packages are published on npm. Optional callback is called for confirmation whether to
|
|
26
|
+
* continue.
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} options
|
|
29
|
+
* @param {String} options.packagesDirectory Relative path to a location of packages to release.
|
|
30
|
+
* @param {String} options.npmOwner The account name on npm, which should be used to publish the packages.
|
|
31
|
+
* @param {ListrTaskObject} options.listrTask An instance of `ListrTask`.
|
|
32
|
+
* @param {String} [options.npmTag='staging'] The npm distribution tag.
|
|
33
|
+
* @param {Object.<String, Array.<String>>|null} [options.optionalEntries=null] Specifies which entries from the `files` field in the
|
|
34
|
+
* `package.json` are optional. The key is a package name, and its value is an array of optional entries from the `files` field, for which
|
|
35
|
+
* it is allowed not to match any file. The `options.optionalEntries` object may also contain the `default` key, which is used for all
|
|
36
|
+
* packages that do not have own definition.
|
|
37
|
+
* @param {String} [options.confirmationCallback=null] An callback whose response decides to continue the publishing packages. Synchronous
|
|
38
|
+
* and asynchronous callbacks are supported.
|
|
39
|
+
* @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved.
|
|
40
|
+
* @returns {Promise}
|
|
41
|
+
*/
|
|
42
|
+
module.exports = async function publishPackages( options ) {
|
|
43
|
+
const {
|
|
44
|
+
packagesDirectory,
|
|
45
|
+
npmOwner,
|
|
46
|
+
listrTask,
|
|
47
|
+
npmTag = 'staging',
|
|
48
|
+
optionalEntries = null,
|
|
49
|
+
confirmationCallback = null,
|
|
50
|
+
cwd = process.cwd()
|
|
51
|
+
} = options;
|
|
52
|
+
|
|
53
|
+
await assertNpmAuthorization( npmOwner );
|
|
54
|
+
|
|
55
|
+
const packagePaths = await glob( '*/', {
|
|
56
|
+
cwd: upath.join( cwd, packagesDirectory ),
|
|
57
|
+
absolute: true
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
await assertPackages( packagePaths );
|
|
61
|
+
await assertFilesToPublish( packagePaths, optionalEntries );
|
|
62
|
+
await assertNpmTag( packagePaths, npmTag );
|
|
63
|
+
|
|
64
|
+
const shouldPublishPackages = confirmationCallback ? await confirmationCallback() : true;
|
|
65
|
+
|
|
66
|
+
if ( shouldPublishPackages ) {
|
|
67
|
+
await publishPackagesOnNpm( packagePaths, npmTag, listrTask );
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
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 { tools } = require( '@ckeditor/ckeditor5-dev-utils' );
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Push the local changes to a remote server.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} options
|
|
14
|
+
* @param {String} options.releaseBranch A name of the branch that should be used for releasing packages.
|
|
15
|
+
* @param {String} options.version Name of tag connected with the release.
|
|
16
|
+
* @param {String} [options.cwd] Root of the repository to prepare. `process.cwd()` by default.
|
|
17
|
+
* @returns {Promise}
|
|
18
|
+
*/
|
|
19
|
+
module.exports = async function push( options ) {
|
|
20
|
+
const {
|
|
21
|
+
releaseBranch,
|
|
22
|
+
version,
|
|
23
|
+
cwd = process.cwd()
|
|
24
|
+
} = options;
|
|
25
|
+
|
|
26
|
+
const command = `git push origin ${ releaseBranch } v${ version }`;
|
|
27
|
+
|
|
28
|
+
return tools.shExec( command, { cwd, verbosity: 'error', async: true } );
|
|
29
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
|
5
|
+
* For licensing, see LICENSE.md.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* eslint-env node */
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const chalk = require( 'chalk' );
|
|
13
|
+
const columns = require( 'cli-columns' );
|
|
14
|
+
const { tools } = require( '@ckeditor/ckeditor5-dev-utils' );
|
|
15
|
+
const assertNpmAuthorization = require( '../utils/assertnpmauthorization' );
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Used to switch the tags from `staging` to `latest` for specified array of packages.
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} options
|
|
21
|
+
* @param {String} options.npmOwner User that is authorized to release packages.
|
|
22
|
+
* @param {String} options.version Specifies the version of packages to reassign the tags for.
|
|
23
|
+
* @param {Array.<String>} options.packages Array of packages' names to reassign tags for.
|
|
24
|
+
* @returns {Promise}
|
|
25
|
+
*/
|
|
26
|
+
module.exports = async function reassignNpmTags( { npmOwner, version, packages } ) {
|
|
27
|
+
const errors = [];
|
|
28
|
+
const packagesSkipped = [];
|
|
29
|
+
const packagesUpdated = [];
|
|
30
|
+
|
|
31
|
+
await assertNpmAuthorization( npmOwner );
|
|
32
|
+
|
|
33
|
+
const counter = tools.createSpinner( 'Reassigning npm tags...', { total: packages.length } );
|
|
34
|
+
counter.start();
|
|
35
|
+
|
|
36
|
+
for ( const packageName of packages ) {
|
|
37
|
+
const latestVersion = await exec( `npm show ${ packageName }@latest version` )
|
|
38
|
+
.then( version => version.trim() )
|
|
39
|
+
.catch( error => {
|
|
40
|
+
errors.push( trimErrorMessage( error.message ) );
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
if ( latestVersion === version ) {
|
|
44
|
+
packagesSkipped.push( packageName );
|
|
45
|
+
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await exec( `npm dist-tag add ${ packageName }@${ version } latest` )
|
|
50
|
+
.then( () => {
|
|
51
|
+
packagesUpdated.push( packageName );
|
|
52
|
+
} )
|
|
53
|
+
.catch( error => {
|
|
54
|
+
errors.push( trimErrorMessage( error.message ) );
|
|
55
|
+
} )
|
|
56
|
+
.finally( () => {
|
|
57
|
+
counter.increase();
|
|
58
|
+
} );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
counter.finish();
|
|
62
|
+
|
|
63
|
+
if ( packagesUpdated.length ) {
|
|
64
|
+
console.log( chalk.bold.green( '✨ Tags updated:' ) );
|
|
65
|
+
console.log( columns( packagesUpdated ) );
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ( packagesSkipped.length ) {
|
|
69
|
+
console.log( chalk.bold.yellow( '⬇️ Packages skipped:' ) );
|
|
70
|
+
console.log( columns( packagesSkipped ) );
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if ( errors.length ) {
|
|
74
|
+
console.log( chalk.bold.red( '🐛 Errors found:' ) );
|
|
75
|
+
errors.forEach( msg => console.log( `* ${ msg }` ) );
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {String} message
|
|
81
|
+
* @returns {String}
|
|
82
|
+
*/
|
|
83
|
+
function trimErrorMessage( message ) {
|
|
84
|
+
return message.replace( /npm ERR!.*\n/g, '' ).trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {String} command
|
|
89
|
+
* @returns {Promise.<String>}
|
|
90
|
+
*/
|
|
91
|
+
async function exec( command ) {
|
|
92
|
+
return tools.shExec( command, { verbosity: 'silent', async: true } );
|
|
93
|
+
}
|