feat(scripts/gen-deps-list.js): should now be used only during release (#6234)
This commit is contained in:
parent
5b7228ed69
commit
8ec8a3b4d9
@ -21,23 +21,15 @@
|
|||||||
|
|
||||||
### Packages to release
|
### Packages to release
|
||||||
|
|
||||||
> Packages will be released in the order they are here, therefore, they should
|
> When modifying a package, add it here with its release type.
|
||||||
> be listed by inverse order of dependency.
|
|
||||||
>
|
>
|
||||||
> Rule of thumb: add packages on top.
|
> The format is the following: - `$packageName` `$releaseType`
|
||||||
>
|
>
|
||||||
> The format is the following: - `$packageName` `$version`
|
> Where `$releaseType` is
|
||||||
>
|
|
||||||
> Where `$version` is
|
|
||||||
>
|
>
|
||||||
> - patch: if the change is a bug fix or a simple code improvement
|
> - patch: if the change is a bug fix or a simple code improvement
|
||||||
> - minor: if the change is a new feature
|
> - minor: if the change is a new feature
|
||||||
> - major: if the change breaks compatibility
|
> - major: if the change breaks compatibility
|
||||||
>
|
|
||||||
> In case of conflict, the highest (lowest in previous list) `$version` wins.
|
|
||||||
>
|
|
||||||
> The `gen-deps-list` script can be used to generate this list of dependencies
|
|
||||||
> Run `scripts/gen-deps-list.js --help` for usage
|
|
||||||
|
|
||||||
<!--packages-start-->
|
<!--packages-start-->
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
"@babel/register": "^7.0.0",
|
"@babel/register": "^7.0.0",
|
||||||
"babel-jest": "^27.3.1",
|
"babel-jest": "^27.3.1",
|
||||||
"benchmark": "^2.1.4",
|
"benchmark": "^2.1.4",
|
||||||
"commander": "^9.2.0",
|
|
||||||
"deptree": "^1.0.0",
|
"deptree": "^1.0.0",
|
||||||
"eslint": "^8.7.0",
|
"eslint": "^8.7.0",
|
||||||
"eslint-config-prettier": "^8.1.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const { program, Argument } = require('commander')
|
|
||||||
const DepTree = require('deptree')
|
const DepTree = require('deptree')
|
||||||
const fs = require('fs').promises
|
const fs = require('fs').promises
|
||||||
const joinPath = require('path').join
|
const joinPath = require('path').join
|
||||||
@ -9,6 +8,7 @@ const semver = require('semver')
|
|||||||
const { getPackages } = require('./utils')
|
const { getPackages } = require('./utils')
|
||||||
const escapeRegExp = require('lodash/escapeRegExp')
|
const escapeRegExp = require('lodash/escapeRegExp')
|
||||||
const invert = require('lodash/invert')
|
const invert = require('lodash/invert')
|
||||||
|
const keyBy = require('lodash/keyBy')
|
||||||
|
|
||||||
const changelogConfig = {
|
const changelogConfig = {
|
||||||
path: joinPath(__dirname, '../CHANGELOG.unreleased.md'),
|
path: joinPath(__dirname, '../CHANGELOG.unreleased.md'),
|
||||||
@ -16,95 +16,98 @@ const changelogConfig = {
|
|||||||
endTag: '<!--packages-end-->',
|
endTag: '<!--packages-end-->',
|
||||||
}
|
}
|
||||||
|
|
||||||
program
|
|
||||||
.argument('<package-name>', 'The name of the package to release')
|
|
||||||
.addArgument(new Argument('<release-type>', 'The type of release to perform').choices(['patch', 'minor', 'major']))
|
|
||||||
.option('-r, --read-changelog', 'Import existing packages from the changelog')
|
|
||||||
.option('-w, --write-changelog', 'Write output to the changelog')
|
|
||||||
.option('--force', 'Required when using --write-changelog without --read-changelog')
|
|
||||||
.showHelpAfterError(true)
|
|
||||||
.showSuggestionAfterError(true)
|
|
||||||
.parse()
|
|
||||||
|
|
||||||
const [rootPackageName, rootReleaseType] = program.args
|
|
||||||
const { readChangelog, writeChangelog, force } = program.opts()
|
|
||||||
|
|
||||||
if (writeChangelog && !readChangelog && !force) {
|
|
||||||
// Stop the process to prevent unwanted changelog overwrite
|
|
||||||
program.showHelpAfterError(false).error(`
|
|
||||||
WARNING: Using --write-changelog without --read-changelog will remove existing packages list.
|
|
||||||
If you are sure you want to do this, add --force.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const RELEASE_WEIGHT = { PATCH: 1, MINOR: 2, MAJOR: 3 }
|
const RELEASE_WEIGHT = { PATCH: 1, MINOR: 2, MAJOR: 3 }
|
||||||
const RELEASE_TYPE = invert(RELEASE_WEIGHT)
|
const RELEASE_TYPE = invert(RELEASE_WEIGHT)
|
||||||
const rootReleaseWeight = releaseTypeToWeight(rootReleaseType)
|
|
||||||
|
|
||||||
/** @type {Map<string, int>} A mapping of package names to their release weight */
|
|
||||||
const packagesToRelease = new Map([[rootPackageName, rootReleaseWeight]])
|
|
||||||
|
|
||||||
const dependencyTree = new DepTree()
|
const dependencyTree = new DepTree()
|
||||||
|
/** @type {Map<string, int>} A mapping of package names to their release weight */
|
||||||
|
const packagesToRelease = new Map()
|
||||||
|
|
||||||
|
let allPackages
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
if (readChangelog) {
|
allPackages = keyBy(await getPackages(true), 'name')
|
||||||
await importPackagesFromChangelog()
|
const content = await fs.readFile(changelogConfig.path)
|
||||||
|
const changelogRegex = new RegExp(
|
||||||
|
`${escapeRegExp(changelogConfig.startTag)}(.*)${escapeRegExp(changelogConfig.endTag)}`,
|
||||||
|
's'
|
||||||
|
)
|
||||||
|
const block = changelogRegex.exec(content)?.[1].trim()
|
||||||
|
|
||||||
|
if (block === undefined) {
|
||||||
|
throw new Error(`Could not find changelog block in ${changelogConfig.path}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const packages = await getPackages(true)
|
block.split('\n').forEach(rawLine => {
|
||||||
const rootPackage = packages.find(pkg => pkg.name === rootPackageName)
|
const line = rawLine.trim()
|
||||||
|
|
||||||
if (!rootPackage) {
|
if (!line) {
|
||||||
program.showHelpAfterError(false).error(`error: Package ${rootPackageName} not found`)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyTree.add(rootPackage.name)
|
const match = line.match(/^-\s*(?<packageName>\S+)\s+(?<releaseType>patch|minor|major)$/)
|
||||||
|
|
||||||
/**
|
if (!match) {
|
||||||
* Recursively add dependencies to the dependency tree
|
throw new Error(`Invalid line: "${rawLine}"`)
|
||||||
*
|
}
|
||||||
* @param {string} handledPackageName The name of the package to handle
|
|
||||||
* @param {string} handledPackageNextVersion The next version of the package to handle
|
const {
|
||||||
*/
|
groups: { packageName, releaseType },
|
||||||
function handlePackage(handledPackageName, handledPackageNextVersion) {
|
} = match
|
||||||
packages.forEach(({ package: { name, version, dependencies, optionalDependencies, peerDependencies } }) => {
|
|
||||||
|
const rootPackage = allPackages[packageName]
|
||||||
|
|
||||||
|
if (!rootPackage) {
|
||||||
|
throw new Error(`Package "${packageName}" does not exist`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootReleaseWeight = releaseTypeToWeight(releaseType)
|
||||||
|
registerPackageToRelease(packageName, rootReleaseWeight)
|
||||||
|
dependencyTree.add(rootPackage.name)
|
||||||
|
|
||||||
|
handlePackageDependencies(rootPackage.name, getNextVersion(rootPackage.version, rootReleaseWeight))
|
||||||
|
})
|
||||||
|
|
||||||
|
const commandsToExecute = ['', 'Commands to execute:', '']
|
||||||
|
const releasedPackages = ['', '### Released packages', '']
|
||||||
|
|
||||||
|
dependencyTree.resolve().forEach(dependencyName => {
|
||||||
|
const releaseWeight = packagesToRelease.get(dependencyName)
|
||||||
|
const {
|
||||||
|
package: { version },
|
||||||
|
} = allPackages[dependencyName]
|
||||||
|
commandsToExecute.push(`./scripts/bump-pkg ${dependencyName} ${RELEASE_TYPE[releaseWeight].toLocaleLowerCase()}`)
|
||||||
|
releasedPackages.push(`- ${dependencyName} ${getNextVersion(version, releaseWeight)}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(commandsToExecute.join('\n'))
|
||||||
|
console.log(releasedPackages.join('\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively add dependencies to the dependency tree
|
||||||
|
*
|
||||||
|
* @param {string} packageName The name of the package to handle
|
||||||
|
* @param {string} packageNextVersion The next version of the package to handle
|
||||||
|
*/
|
||||||
|
function handlePackageDependencies(packageName, packageNextVersion) {
|
||||||
|
Object.values(allPackages).forEach(
|
||||||
|
({ package: { name, version, dependencies, optionalDependencies, peerDependencies } }) => {
|
||||||
let releaseWeight
|
let releaseWeight
|
||||||
|
|
||||||
if (
|
if (shouldPackageBeReleased(packageName, { ...dependencies, ...optionalDependencies }, packageNextVersion)) {
|
||||||
shouldPackageBeReleased(
|
|
||||||
handledPackageName,
|
|
||||||
{ ...dependencies, ...optionalDependencies },
|
|
||||||
handledPackageNextVersion
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
releaseWeight = RELEASE_WEIGHT.PATCH
|
releaseWeight = RELEASE_WEIGHT.PATCH
|
||||||
} else if (shouldPackageBeReleased(handledPackageName, peerDependencies || {}, handledPackageNextVersion)) {
|
} else if (shouldPackageBeReleased(packageName, peerDependencies || {}, packageNextVersion)) {
|
||||||
releaseWeight = versionToReleaseWeight(version)
|
releaseWeight = versionToReleaseWeight(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (releaseWeight !== undefined) {
|
if (releaseWeight !== undefined) {
|
||||||
registerPackageToRelease(name, releaseWeight)
|
registerPackageToRelease(name, releaseWeight)
|
||||||
dependencyTree.add(name, handledPackageName)
|
dependencyTree.add(name, packageName)
|
||||||
handlePackage(name, getNextVersion(version, releaseWeight))
|
handlePackageDependencies(name, getNextVersion(version, releaseWeight))
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
handlePackage(rootPackage.name, getNextVersion(rootPackage.version, rootReleaseWeight))
|
|
||||||
|
|
||||||
const outputLines = dependencyTree.resolve().map(dependencyName => {
|
|
||||||
const releaseTypeName = RELEASE_TYPE[packagesToRelease.get(dependencyName)].toLocaleLowerCase()
|
|
||||||
return `- ${dependencyName} ${releaseTypeName}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const outputLog = ['', 'New packages list:', '', ...outputLines]
|
|
||||||
|
|
||||||
if (writeChangelog) {
|
|
||||||
await updateChangelog(outputLines)
|
|
||||||
outputLog.unshift('', `File updated: ${changelogConfig.path}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(outputLog.join('\n'))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function releaseTypeToWeight(type) {
|
function releaseTypeToWeight(type) {
|
||||||
@ -155,38 +158,7 @@ function versionToReleaseWeight(version) {
|
|||||||
* @returns {string} The incremented version
|
* @returns {string} The incremented version
|
||||||
*/
|
*/
|
||||||
function getNextVersion(version, releaseWeight) {
|
function getNextVersion(version, releaseWeight) {
|
||||||
return semver.inc(version, RELEASE_TYPE[releaseWeight])
|
return semver.inc(version, RELEASE_TYPE[releaseWeight].toLocaleLowerCase())
|
||||||
}
|
|
||||||
|
|
||||||
const changelogRegex = new RegExp(
|
|
||||||
`${escapeRegExp(changelogConfig.startTag)}(.*)${escapeRegExp(changelogConfig.endTag)}`,
|
|
||||||
's'
|
|
||||||
)
|
|
||||||
|
|
||||||
async function importPackagesFromChangelog() {
|
|
||||||
const content = await fs.readFile(changelogConfig.path)
|
|
||||||
const block = changelogRegex.exec(content)?.[1].trim()
|
|
||||||
|
|
||||||
if (block === undefined) {
|
|
||||||
throw new Error(`Could not find changelog block in ${changelogConfig.path}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = block.matchAll(/^- (?<name>[^ ]+) (?<type>patch|minor|major)$/gm)
|
|
||||||
|
|
||||||
for (const { groups: { name, type } } of lines) {
|
|
||||||
registerPackageToRelease(name, releaseTypeToWeight(type))
|
|
||||||
dependencyTree.add(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateChangelog(lines) {
|
|
||||||
const content = await fs.readFile(changelogConfig.path)
|
|
||||||
await fs.writeFile(
|
|
||||||
changelogConfig.path,
|
|
||||||
content
|
|
||||||
.toString()
|
|
||||||
.replace(changelogRegex, [changelogConfig.startTag, '', ...lines, '', changelogConfig.endTag].join('\n'))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(error => {
|
main().catch(error => {
|
||||||
|
@ -5471,11 +5471,6 @@ commander@^8.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||||
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
|
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
|
||||||
|
|
||||||
commander@^9.2.0:
|
|
||||||
version "9.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-9.2.0.tgz#6e21014b2ed90d8b7c9647230d8b7a94a4a419a9"
|
|
||||||
integrity sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==
|
|
||||||
|
|
||||||
commander@~2.19.0:
|
commander@~2.19.0:
|
||||||
version "2.19.0"
|
version "2.19.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||||
|
Loading…
Reference in New Issue
Block a user