diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 879b4cd33..6cc0dbaf3 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -34,14 +34,14 @@ jobs: - name: Parse semver version from branch name id: semver_parser - uses: xile611/read-package-version-action@v2.1 + uses: xile611/read-package-version-action@main with: path: packages/vrender semver_string: ${{ github.ref_name }} semver_pattern: '^pre-release/(.*)$' # ^v?(.*)$ by default - name: Apply prereleaseName - run: node common/scripts/install-run-rush.js publish --apply --prerelease-name ${{ steps.semver_parser.outputs.pre_release_name }} --partial-prerelease + run: node common/scripts/apply-prerelease-version.js ${{ steps.semver_parser.outputs.pre_release_name }} ${{ steps.semver_parser.outputs.main }} - name: Build packages run: node common/scripts/install-run-rush.js build --only tag:package @@ -57,7 +57,7 @@ jobs: - name: Get npm version id: package-version - uses: xile611/read-package-version-action@v2.1 + uses: xile611/read-package-version-action@main with: path: packages/vrender @@ -67,4 +67,4 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} message: 'build: prelease version ${{ steps.package-version.outputs.current_version }}' branch: ${{ github.ref_name }} - author_name: ${{ github.actor }} \ No newline at end of file + author_name: ${{ github.actor }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e5ec9e177..061216594 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,20 @@ jobs: - name: Install rush run: node common/scripts/install-run-rush.js install --bypass-policy + - name: Parse semver version from branch name + id: semver_parser + uses: xile611/read-package-version-action@main + with: + path: packages/vgrammar + semver_string: ${{ github.ref_name }} + semver_pattern: '^release/(.*)$' # ^v?(.*)$ by default + + - name: update nextBump of version policies + uses: xile611/set-next-bump-of-rush@main + with: + release_version: ${{ steps.semver_parser.outputs.full }} + write_next_bump: true + - name: Update version run: node common/scripts/install-run-rush.js version --bump diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 048aef0fa..44cc6e879 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1,102 +1,8 @@ -/** - * This is configuration file is used for advanced publishing configurations with Rush. - * More documentation is available on the Rush website: https://rushjs.io - */ - -/** - * A list of version policy definitions. A "version policy" is a custom package versioning - * strategy that affects "rush change", "rush version", and "rush publish". The strategy applies - * to a set of projects that are specified using the "versionPolicyName" field in rush.json. - */ [ - // { - // /** - // * (Required) Indicates the kind of version policy being defined ("lockStepVersion" or "individualVersion"). - // * - // * The "lockStepVersion" mode specifies that the projects will use "lock-step versioning". This - // * strategy is appropriate for a set of packages that act as selectable components of a - // * unified product. The entire set of packages are always published together, and always share - // * the same NPM version number. When the packages depend on other packages in the set, the - // * SemVer range is usually restricted to a single version. - // */ - // "definitionName": "lockStepVersion", - // - // /** - // * (Required) The name that will be used for the "versionPolicyName" field in rush.json. - // * This name is also used command-line parameters such as "--version-policy" - // * and "--to-version-policy". - // */ - // "policyName": "MyBigFramework", - // - // /** - // * (Required) The current version. All packages belonging to the set should have this version - // * in the current branch. When bumping versions, Rush uses this to determine the next version. - // * (The "version" field in package.json is NOT considered.) - // */ - // "version": "1.0.0", - // - // /** - // * (Required) The type of bump that will be performed when publishing the next release. - // * When creating a release branch in Git, this field should be updated according to the - // * type of release. - // * - // * Valid values are: "prerelease", "release", "minor", "patch", "major" - // */ - // "nextBump": "prerelease", - // - // /** - // * (Optional) If specified, all packages in the set share a common CHANGELOG.md file. - // * This file is stored with the specified "main" project, which must be a member of the set. - // * - // * If this field is omitted, then a separate CHANGELOG.md file will be maintained for each - // * package in the set. - // */ - // "mainProject": "my-app", - // - // /** - // * (Optional) If enabled, the "rush change" command will prompt the user for their email address - // * and include it in the JSON change files. If an organization maintains multiple repos, tracking - // * this contact information may be useful for a service that automatically upgrades packages and - // * needs to notify engineers whose change may be responsible for a downstream build break. It might - // * also be useful for crediting contributors. Rush itself does not do anything with the collected - // * email addresses. The default value is "false". - // */ - // // "includeEmailInChangeFile": true - // }, - // - // { - // /** - // * (Required) Indicates the kind of version policy being defined ("lockStepVersion" or "individualVersion"). - // * - // * The "individualVersion" mode specifies that the projects will use "individual versioning". - // * This is the typical NPM model where each package has an independent version number - // * and CHANGELOG.md file. Although a single CI definition is responsible for publishing the - // * packages, they otherwise don't have any special relationship. The version bumping will - // * depend on how developers answer the "rush change" questions for each package that - // * is changed. - // */ - // "definitionName": "individualVersion", - // - // "policyName": "MyRandomLibraries", - // - // /** - // * (Optional) This can be used to enforce that all packages in the set must share a common - // * major version number, e.g. because they are from the same major release branch. - // * It can also be used to discourage people from accidentally making "MAJOR" SemVer changes - // * inappropriately. The minor/patch version parts will be bumped independently according - // * to the types of changes made to each project, according to the "rush change" command. - // */ - // "lockedMajor": 3, - // - // /** - // * (Optional) When publishing is managed by Rush, by default the "rush change" command will - // * request changes for any projects that are modified by a pull request. These change entries - // * will produce a CHANGELOG.md file. If you author your CHANGELOG.md manually or announce updates - // * in some other way, set "exemptFromRushChange" to true to tell "rush change" to ignore the projects - // * belonging to this version policy. - // */ - // "exemptFromRushChange": false, - // - // // "includeEmailInChangeFile": true - // } + { + "definitionName": "lockStepVersion", + "policyName": "vrenderMain", + "version": "0.14.7", + "nextBump": "patch" + } ] diff --git a/common/scripts/apply-prerelease-version.js b/common/scripts/apply-prerelease-version.js new file mode 100644 index 000000000..39806b11b --- /dev/null +++ b/common/scripts/apply-prerelease-version.js @@ -0,0 +1,14 @@ +const writePrereleaseVersion = require('./set-prerelease-version'); +const checkAndUpdateNextBump = require('./version-policies'); + + +function run() { + const preReleaseName = process.argv.slice(2)[0]; + const nextBump = checkAndUpdateNextBump(process.argv.slice(2)[1]); + + console.log('[apply prerelease version]: ', preReleaseName, nextBump); + + writePrereleaseVersion(nextBump, preReleaseName); +} + +run() \ No newline at end of file diff --git a/common/scripts/get-package-json.js b/common/scripts/get-package-json.js new file mode 100644 index 000000000..a3afad336 --- /dev/null +++ b/common/scripts/get-package-json.js @@ -0,0 +1,8 @@ +const fs = require('fs') + +function getPackageJson(pkgJsonPath) { + const pkgJson = fs.readFileSync(pkgJsonPath, { encoding: 'utf-8' }) + return JSON.parse(pkgJson); +} + +module.exports = getPackageJson; \ No newline at end of file diff --git a/common/scripts/parse-version.js b/common/scripts/parse-version.js index 3b6953c54..dba6f8b36 100644 --- a/common/scripts/parse-version.js +++ b/common/scripts/parse-version.js @@ -1,25 +1,20 @@ // see more about the regex here: https://semver.org/lang/zh-CN/ // reg test: https://regex101.com/r/vkijKf/1/ -const SEMVER_REG = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/gm; -function parseVersion() { - const branchName = process.argv.slice(2)[0]; +function parseVersion(version) { + const res = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/gm.exec(version); - - if (branchName) { - const versionName = branchName.includes('release/') ? branchName.replace('release/', '') : branchName; - const res = SEMVER_REG.exec(versionName); - - if (res) { - return { - major: res[1], - minor: res[2], - patch: res[3], - preReleaseName: res[4], - preReleaseType: res[4].includes('.') ? res[4].split('.')[0] : res[4] - }; - } + if (res) { + return { + major: +res[1], + minor: +res[2], + patch: +res[3], + preReleaseName: res[4], + preReleaseType: res[4] && res[4].includes('.') ? res[4].split('.')[0] : res[4] + }; } return null; -} \ No newline at end of file +} + +module.exports = parseVersion \ No newline at end of file diff --git a/common/scripts/pre-release.js b/common/scripts/pre-release.js index a919dc60f..2613278bb 100644 --- a/common/scripts/pre-release.js +++ b/common/scripts/pre-release.js @@ -1,33 +1,33 @@ /** * prelease + * node release.js [alpha.0] [patch | major | minor | 1.0.0] */ const { spawnSync } = require('child_process') -const fs = require('fs') const path = require('path') +const checkAndUpdateNextBump = require('./version-policies'); +const getPackageJson = require('./get-package-json'); +const writePrereleaseVersion = require('./set-prerelease-version'); -function getPackageJson(pkgJsonPath) { - const pkgJson = fs.readFileSync(pkgJsonPath, { encoding: 'utf-8' }) - return JSON.parse(pkgJson); -} const semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(alpha|beta|rc)(?:\.(?:(0|[1-9])))*)$/; const preReleaseNameReg = /^((alpha|beta|rc)(?:\.(?:0|[1-9]))*)$/; + function run() { let preReleaseName = process.argv.slice(2)[0]; let preReleaseType = ''; - const cwd = process.cwd(); - const rushJson = getPackageJson(`${cwd}/rush.json`) + const rushJson = getPackageJson(path.join(__dirname, '../../rush.json')) const package = rushJson.projects.find((project) => project.packageName === '@visactor/vrender'); let regRes = null; + if (typeof preReleaseName === 'string' && preReleaseName && (regRes = preReleaseNameReg.exec(preReleaseName))) { preReleaseType = regRes[2]; } else if (!preReleaseName) { if (package) { - const pkgJsonPath = path.resolve(package.projectFolder, 'package.json') + const pkgJsonPath = path.join(__dirname, '../../', package.projectFolder, 'package.json') const pkgJson = getPackageJson(pkgJsonPath) const currentVersion = pkgJson.version; @@ -54,20 +54,14 @@ function run() { if (preReleaseName && preReleaseType) { // 1. apply version and update version of package.json - spawnSync('sh', ['-c', `rush publish --apply --prerelease-name ${preReleaseName} --partial-prerelease`], { - stdio: 'inherit', - shell: false, - }); - - + writePrereleaseVersion(checkAndUpdateNextBump(process.argv.slice(2)[1]), preReleaseName) + // 2. build all the packages spawnSync('sh', ['-c', `rush build --only tag:package`], { stdio: 'inherit', shell: false, }); - - // 3. publish to npm spawnSync('sh', ['-c', `rush publish --publish --include-all --tag ${preReleaseType}`], { stdio: 'inherit', @@ -81,7 +75,7 @@ function run() { }); if (package) { - const pkgJsonPath = path.resolve(package.projectFolder, 'package.json') + const pkgJsonPath = path.join(__dirname, '../../', package.projectFolder, 'package.json'); const pkgJson = getPackageJson(pkgJsonPath) // 5. add the the changes @@ -100,3 +94,4 @@ function run() { } run() + diff --git a/common/scripts/release.js b/common/scripts/release.js index e7ea515db..49aae5801 100644 --- a/common/scripts/release.js +++ b/common/scripts/release.js @@ -1,25 +1,32 @@ /** - * release + * prelease + * node release.js [alpha.0] [patch | major | minor | 1.0.0] */ const { spawnSync } = require('child_process') const fs = require('fs') const path = require('path') +const checkAndUpdateNextBump = require('./version-policies'); function getPackageJson(pkgJsonPath) { const pkgJson = fs.readFileSync(pkgJsonPath, { encoding: 'utf-8' }) return JSON.parse(pkgJson) } + function run() { + let releaseVersion = process.argv.slice(2)[0]; const cwd = process.cwd(); - + // 0. update `nextBump` + checkAndUpdateNextBump(releaseVersion); + // 1. update version of package.json, this operation will remove the common/changes spawnSync('sh', ['-c', `rush version --bump`], { stdio: 'inherit', shell: false, }); + // 2. build all the packages spawnSync('sh', ['-c', `rush build --only tag:package`], { stdio: 'inherit', @@ -38,11 +45,11 @@ function run() { shell: false, }); - const rushJson = getPackageJson(`${cwd}/rush.json`); + const rushJson = getPackageJson(path.join(__dirname, '../../rush.json')); const package = rushJson.projects.find((project) => project.name === '@visactor/vrender'); if (package) { - const pkgJsonPath = path.resolve(project.projectFolder, 'package.json') + const pkgJsonPath = path.join(__dirname, '../../', project.projectFolder, 'package.json') const pkgJson = getPackageJson(pkgJsonPath) // 5. add tag @@ -72,3 +79,4 @@ function run() { } run() + diff --git a/common/scripts/set-json-file.js b/common/scripts/set-json-file.js new file mode 100644 index 000000000..40f0f0184 --- /dev/null +++ b/common/scripts/set-json-file.js @@ -0,0 +1,30 @@ +function setJsonFileByKey(file, json, keys, newValue) { + const prevValue = keys.reduce((res, k) => { + return res[k]; + }, json); + + if (prevValue !== newValue) { + let startIndex = 0; + + keys.forEach(k => { + const keyStr = `"${k}"`; + const index = file.indexOf(keyStr, startIndex); + + if (index >= 0) { + startIndex = index + keyStr.length + 1; + } + }) + + const leftIndex = file.indexOf('"', startIndex); + const rightIndex = leftIndex >= 0 ? file.indexOf('"', leftIndex +1) : -1; + + if (leftIndex >= 0 && rightIndex >= 0) { + return `${file.slice(0, leftIndex)}"${newValue}"${file.slice(rightIndex + 1)}` + } + } + + return file; +} + +module.exports = setJsonFileByKey; + diff --git a/common/scripts/set-prerelease-version.js b/common/scripts/set-prerelease-version.js new file mode 100644 index 000000000..698c957c3 --- /dev/null +++ b/common/scripts/set-prerelease-version.js @@ -0,0 +1,82 @@ +const fs = require('fs') +const path = require('path') +const getPackageJson = require('./get-package-json'); +const parseVersion = require('./parse-version'); +const setJsonFileByKey = require('./set-json-file'); + + +function writePrereleaseVersion(nextBump, preReleaseName) { + const rushJson = getPackageJson(path.join(__dirname, '../../rush.json')); + const projects = rushJson.projects; + const mainPackage = projects.find((project) => project.packageName === '@visactor/vrender'); + + if (!mainPackage) { + return; + } + + const mainPkgJsonPath = path.join(__dirname, '../../', mainPackage.projectFolder, 'package.json') + const mainPkgJson = getPackageJson(mainPkgJsonPath) + const mainVersion = mainPkgJson.version; + console.log(`The version of main project is ${mainVersion}`); + const version = parseVersion(mainVersion); + console.log('Parsed version:', version); + + if (!version) { + return; + } + + if (!version.preReleaseName) { + if (nextBump === 'major') { + version.major += 1; + version.minor = 0; + version.patch = 0; + } else if (nextBump === 'minor') { + version.minor += 1; + version.patch = 0; + } else { + version.patch += 1; + } + } + + const nextVersion = `${version.major}.${version.minor}.${version.patch}-${preReleaseName}`; + const published = projects.filter(project => project.shouldPublish).map(project => project.packageName); + console.log(`next version is: ${nextVersion}`); + + projects.forEach(project => { + const pkgJsonPath = path.join( __dirname, '../../', project.projectFolder, 'package.json') + let jsonFile = fs.readFileSync(pkgJsonPath, { encoding: 'utf-8' }) + const pkgJson = JSON.parse(jsonFile); + + if (project.shouldPublish) { + console.log(`handle project: ${project.packageName}, from ${pkgJson.version} to ${nextVersion}`); + } else { + console.log(`handle project: ${project.packageName}, update "dependencies" and "devDependencies" `); + } + + if (project.shouldPublish) { + jsonFile = setJsonFileByKey(jsonFile, pkgJson, ['version'], nextVersion); + } + + if (pkgJson.dependencies) { + Object.keys(pkgJson.dependencies).forEach(dep => { + if (published.includes(dep)) { + jsonFile = setJsonFileByKey(jsonFile, pkgJson, ['dependencies', dep], `workspace:${nextVersion}`); + } + }); + } + + if (pkgJson.devDependencies) { + Object.keys(pkgJson.devDependencies).forEach(dep => { + if (published.includes(dep)) { + jsonFile = setJsonFileByKey(jsonFile, pkgJson, ['devDependencies', dep], `workspace:${nextVersion}`); + } + }); + } + + fs.writeFileSync(pkgJsonPath, jsonFile) + }); + + +} + +module.exports = writePrereleaseVersion; \ No newline at end of file diff --git a/common/scripts/version-policies.js b/common/scripts/version-policies.js new file mode 100644 index 000000000..dec8fb218 --- /dev/null +++ b/common/scripts/version-policies.js @@ -0,0 +1,85 @@ + +const fs = require('fs') +const path = require('path') +const parseVersion = require('./parse-version'); +const PRERELEASE = 'prerelease'; +const MINOR = 'minor'; +const MAJOR = 'major'; +const PATCH = 'patch'; +const NEXT_BUMPMS = [PRERELEASE, PATCH, MINOR, MAJOR]; +const setJsonFileByKey = require('./set-json-file'); + + +const parseNextBumpFromVersion = ( + versionString +) => { + const res = parseVersion(versionString); + + if (res) { + if (res.patch === 0) { + return res.minor == 0 ? MAJOR : MINOR; + } + + return PATCH + } + + console.error(`can parse nextBump from version: ${versionString}`) + process.exit(1); +} + +const writeNextBump = ( + nextBump, +) => { + const filePath = path.join(__dirname, '../config/rush/version-policies.json'); + let fileContent = fs.readFileSync(filePath).toString() + const json = JSON.parse(fileContent); + const curNextBump = json[0].nextBump + + if (nextBump !== curNextBump) { + fileContent = setJsonFileByKey(fileContent, json, ['0', 'nextBump'], nextBump); + + fs.writeFileSync(path.join(__dirname, '../config/rush/version-policies.json'), fileContent) + } +} + +const readNextBumpFromChanges = () => { + const changeRoot = path.join(__dirname, '../changes/@visactor/vrender'); + const filenames = fs.readdirSync(changeRoot); + + if (filenames && filenames.length) { + const changeType = []; + + filenames.forEach(fileName => { + const json = JSON.parse(fs.readFileSync(path.join(changeRoot, fileName)).toString()); + + if (json.changes && json.changes.length) { + json.changes.forEach(change => { + if (change.type && !changeType.includes(change.type)) { + changeType.push(change.type); + } + }) + } + }); + + return changeType.includes(MAJOR) ? MAJOR : changeType.includes(MINOR) ? MINOR : PATCH; + } else { + process.exit(1); + } +} + +const checkAndUpdateNextBump = (version) => { + let nextBump = PATCH; + + if (version && NEXT_BUMPMS.includes(version)) { + nextBump = version; + } else if (version) { + nextBump = parseNextBumpFromVersion(version); + } else { + nextBump = readNextBumpFromChanges(); + } + writeNextBump(nextBump); + + return nextBump; +} + +module.exports = checkAndUpdateNextBump; diff --git a/rush.json b/rush.json index bd9826694..c928165ce 100644 --- a/rush.json +++ b/rush.json @@ -23,31 +23,36 @@ "packageName": "@visactor/vrender-components", "projectFolder": "packages/vrender-components", "tags": ["package"], - "shouldPublish": true + "shouldPublish": true, + "versionPolicyName": "vrenderMain" }, { "packageName": "@visactor/react-vrender-utils", "projectFolder": "packages/react-vrender-utils", "tags": ["package"], - "shouldPublish": true + "shouldPublish": true, + "versionPolicyName": "vrenderMain" }, { "packageName": "@visactor/react-vrender", "projectFolder": "packages/react-vrender", "tags": ["package"], - "shouldPublish": true + "shouldPublish": true, + "versionPolicyName": "vrenderMain" }, { "packageName": "@visactor/vrender-kits", "projectFolder": "packages/vrender-kits", "tags": ["package"], - "shouldPublish": true + "shouldPublish": true, + "versionPolicyName": "vrenderMain" }, { "packageName": "@visactor/vrender", "projectFolder": "packages/vrender", "tags": ["package"], - "shouldPublish": true + "shouldPublish": true, + "versionPolicyName": "vrenderMain" }, { "packageName": "@internal/bundler",