@ckeditor/ckeditor5-dev-release-tools 37.0.1 → 38.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.
- 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 +106 -0
- package/lib/tasks/generatechangelogformonorepository.js +3 -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/changelog.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, verbosity: 'silent' } );
|
|
26
|
+
await tools.shExec( `git commit --message "Release: v${ version }."`, { cwd: normalizedCwd, async: true, verbosity: 'silent' } );
|
|
27
|
+
await tools.shExec( `git tag v${ version }`, { cwd: normalizedCwd, async: true, verbosity: 'silent' } );
|
|
28
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
const { getRepositoryUrl } = require( '../utils/transformcommitutils' );
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a GitHub release.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} options
|
|
16
|
+
* @param {String} options.token Token used to authenticate with GitHub.
|
|
17
|
+
* @param {String} options.version Name of tag connected with the release.
|
|
18
|
+
* @param {String} options.description Description of the release.
|
|
19
|
+
* @param {String} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved.
|
|
20
|
+
* @returns {Promise.<String>}
|
|
21
|
+
*/
|
|
22
|
+
module.exports = async function createGithubRelease( options ) {
|
|
23
|
+
const {
|
|
24
|
+
token,
|
|
25
|
+
version,
|
|
26
|
+
description,
|
|
27
|
+
cwd = process.cwd()
|
|
28
|
+
} = options;
|
|
29
|
+
|
|
30
|
+
const github = new Octokit( {
|
|
31
|
+
version: '3.0.0',
|
|
32
|
+
auth: `token ${ token }`
|
|
33
|
+
} );
|
|
34
|
+
|
|
35
|
+
const repositoryUrl = getRepositoryUrl( cwd );
|
|
36
|
+
const [ repositoryName, repositoryOwner ] = repositoryUrl.split( '/' ).reverse();
|
|
37
|
+
|
|
38
|
+
if ( await shouldCreateRelease( github, repositoryOwner, repositoryName, version ) ) {
|
|
39
|
+
await github.repos.createRelease( {
|
|
40
|
+
tag_name: `v${ version }`,
|
|
41
|
+
owner: repositoryOwner,
|
|
42
|
+
repo: repositoryName,
|
|
43
|
+
body: description,
|
|
44
|
+
prerelease: getVersionTag( version ) !== 'latest'
|
|
45
|
+
} );
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return `https://github.com/${ repositoryOwner }/${ repositoryName }/releases/tag/v${ version }`;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns an npm tag based on the specified release version.
|
|
53
|
+
*
|
|
54
|
+
* @param {String} version
|
|
55
|
+
* @returns {String}
|
|
56
|
+
*/
|
|
57
|
+
function getVersionTag( version ) {
|
|
58
|
+
const [ versionTag ] = semver.prerelease( version ) || [ 'latest' ];
|
|
59
|
+
|
|
60
|
+
return versionTag;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolves a promise containing a flag if the GitHub contains the release page for given version.
|
|
65
|
+
*
|
|
66
|
+
* @param {Octokit} github
|
|
67
|
+
* @param {String} repositoryOwner
|
|
68
|
+
* @param {String} repositoryName
|
|
69
|
+
* @param {String} version
|
|
70
|
+
* @returns {Promise.<boolean>}
|
|
71
|
+
*/
|
|
72
|
+
async function shouldCreateRelease( github, repositoryOwner, repositoryName, version ) {
|
|
73
|
+
const releaseDetails = await getLastRelease( github, repositoryOwner, repositoryName );
|
|
74
|
+
|
|
75
|
+
// It can be `null` if there is no releases on GitHub.
|
|
76
|
+
let githubVersion = releaseDetails.data.tag_name;
|
|
77
|
+
|
|
78
|
+
if ( githubVersion ) {
|
|
79
|
+
githubVersion = releaseDetails.data.tag_name.replace( /^v/, '' );
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// If versions are different, we are ready to create a new release.
|
|
83
|
+
return githubVersion !== version;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getLastRelease( github, repositoryOwner, repositoryName ) {
|
|
87
|
+
const requestParams = {
|
|
88
|
+
owner: repositoryOwner,
|
|
89
|
+
repo: repositoryName
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return github.repos.getLatestRelease( requestParams )
|
|
93
|
+
.catch( err => {
|
|
94
|
+
// If the "last release" returned the 404 error page, it means that this release
|
|
95
|
+
// will be the first one for specified `repositoryOwner/repositoryName` package.
|
|
96
|
+
if ( err.status == 404 ) {
|
|
97
|
+
return Promise.resolve( {
|
|
98
|
+
data: {
|
|
99
|
+
tag_name: null
|
|
100
|
+
}
|
|
101
|
+
} );
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Promise.reject( err );
|
|
105
|
+
} );
|
|
106
|
+
}
|
|
@@ -477,6 +477,9 @@ module.exports = async function generateChangelogForMonoRepository( options ) {
|
|
|
477
477
|
// Save the changelog.
|
|
478
478
|
changelogUtils.saveChangelog( newChangelog );
|
|
479
479
|
|
|
480
|
+
// Truncate the changelog to keep the latest five release entries.
|
|
481
|
+
changelogUtils.truncateChangelog( 5 );
|
|
482
|
+
|
|
480
483
|
logInfo( 'Saved.', { indentLevel: 1 } );
|
|
481
484
|
}
|
|
482
485
|
|
|
@@ -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
|
+
};
|