feat(scripts/gen-deps-list.js): new util to generate deps list (#6181)
This commit is contained in:
parent
2d01056ea9
commit
50f3ab7798
@ -27,3 +27,11 @@
|
||||
> - 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-end-->
|
||||
|
@ -5,6 +5,8 @@
|
||||
"@babel/register": "^7.0.0",
|
||||
"babel-jest": "^27.3.1",
|
||||
"benchmark": "^2.1.4",
|
||||
"commander": "^9.2.0",
|
||||
"deptree": "^1.0.0",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
@ -23,6 +25,7 @@
|
||||
"lodash": "^4.17.4",
|
||||
"prettier": "^2.0.5",
|
||||
"promise-toolbox": "^0.21.0",
|
||||
"semver": "^7.3.6",
|
||||
"sorted-object": "^2.0.1",
|
||||
"vuepress": "^1.4.1"
|
||||
},
|
||||
|
195
scripts/gen-deps-list.js
Executable file
195
scripts/gen-deps-list.js
Executable file
@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
|
||||
const { program, Argument } = require('commander')
|
||||
const DepTree = require('deptree')
|
||||
const fs = require('fs').promises
|
||||
const joinPath = require('path').join
|
||||
const semver = require('semver')
|
||||
const { getPackages } = require('./utils')
|
||||
const escapeRegExp = require('lodash/escapeRegExp')
|
||||
const invert = require('lodash/invert')
|
||||
|
||||
const changelogConfig = {
|
||||
path: joinPath(__dirname, '../CHANGELOG.unreleased.md'),
|
||||
startTag: '<!--packages-start-->',
|
||||
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_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()
|
||||
|
||||
async function main() {
|
||||
if (readChangelog) {
|
||||
await importPackagesFromChangelog()
|
||||
}
|
||||
|
||||
const packages = await getPackages(true)
|
||||
const rootPackage = packages.find(pkg => pkg.name === rootPackageName)
|
||||
|
||||
if (!rootPackage) {
|
||||
program.showHelpAfterError(false).error(`error: Package ${rootPackageName} not found`)
|
||||
}
|
||||
|
||||
dependencyTree.add(rootPackage.name)
|
||||
|
||||
/**
|
||||
* Recursively add dependencies to the dependency tree
|
||||
*
|
||||
* @param {string} handledPackageName The name of the package to handle
|
||||
* @param {string} handledPackageNextVersion The next version of the package to handle
|
||||
*/
|
||||
function handlePackage(handledPackageName, handledPackageNextVersion) {
|
||||
packages.forEach(({ package: { name, version, dependencies, optionalDependencies, peerDependencies } }) => {
|
||||
let releaseWeight
|
||||
|
||||
if (
|
||||
shouldPackageBeReleased(
|
||||
handledPackageName,
|
||||
{ ...dependencies, ...optionalDependencies },
|
||||
handledPackageNextVersion
|
||||
)
|
||||
) {
|
||||
releaseWeight = RELEASE_WEIGHT.PATCH
|
||||
} else if (shouldPackageBeReleased(handledPackageName, peerDependencies || {}, handledPackageNextVersion)) {
|
||||
releaseWeight = versionToReleaseWeight(version)
|
||||
}
|
||||
|
||||
if (releaseWeight !== undefined) {
|
||||
registerPackageToRelease(name, releaseWeight)
|
||||
dependencyTree.add(name, handledPackageName)
|
||||
handlePackage(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) {
|
||||
return RELEASE_WEIGHT[type.toLocaleUpperCase()]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name The package name to check
|
||||
* @param {object} dependencies The package dependencies name/constraint map
|
||||
* @param {string} version The version to check the dependency constraint against
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function shouldPackageBeReleased(name, dependencies, version) {
|
||||
if (!Object.prototype.hasOwnProperty.call(dependencies, name)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
['xo-web', 'xo-server', '@xen-orchestra/proxy'].includes(name) || !semver.satisfies(version, dependencies[name])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} packageName
|
||||
* @param {int} releaseWeight
|
||||
*/
|
||||
function registerPackageToRelease(packageName, releaseWeight) {
|
||||
const currentWeight = packagesToRelease.get(packageName) || 0
|
||||
|
||||
packagesToRelease.set(packageName, Math.max(currentWeight, releaseWeight))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} version
|
||||
* @returns {int}
|
||||
*/
|
||||
function versionToReleaseWeight(version) {
|
||||
return semver.major(version) > 0
|
||||
? RELEASE_WEIGHT.MAJOR
|
||||
: semver.minor(version) > 0
|
||||
? RELEASE_WEIGHT.MINOR
|
||||
: RELEASE_WEIGHT.PATCH
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} version The version to increment
|
||||
* @param {int} releaseWeight The release weight to apply
|
||||
* @returns {string} The incremented version
|
||||
*/
|
||||
function getNextVersion(version, releaseWeight) {
|
||||
return semver.inc(version, RELEASE_TYPE[releaseWeight])
|
||||
}
|
||||
|
||||
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 => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
33
yarn.lock
33
yarn.lock
@ -5448,6 +5448,11 @@ commander@^8.3.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||
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:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||
@ -12263,7 +12268,7 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lru-cache@^7.0.4:
|
||||
lru-cache@^7.4.0:
|
||||
version "7.8.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.1.tgz#68ee3f4807a57d2ba185b7fd90827d5c21ce82bb"
|
||||
integrity sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==
|
||||
@ -16341,6 +16346,32 @@ semver@^7.0.0, semver@^7.3.2, semver@^7.3.5:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.3.6:
|
||||
version "7.3.6"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b"
|
||||
integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==
|
||||
dependencies:
|
||||
lru-cache "^7.4.0"
|
||||
|
||||
send@0.17.2:
|
||||
version "0.17.2"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820"
|
||||
integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
destroy "~1.0.4"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
fresh "0.5.2"
|
||||
http-errors "1.8.1"
|
||||
mime "1.6.0"
|
||||
ms "2.1.3"
|
||||
on-finished "~2.3.0"
|
||||
range-parser "~1.2.1"
|
||||
statuses "~1.5.0"
|
||||
|
||||
send@0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
|
||||
|
Loading…
Reference in New Issue
Block a user