From d0fbe7a3ddc91e785f81436c81b667d61183f68e Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Tue, 22 Nov 2022 09:28:52 -0500 Subject: [PATCH 1/8] ci: add prerelease process https://coveord.atlassian.net/browse/KIT-2114 --- .deployment.config.json | 3 +- .github/workflows/masterbot.yml | 1 + Jenkinsfile | 32 +++-- README.md | 26 ++++ package-lock.json | 117 ++++++++++++++++-- package.json | 6 +- .../atomic-angular/modify-package-json.js | 2 + packages/atomic-angular/package.json | 1 + packages/atomic-react/package.json | 1 + packages/auth/package.json | 1 + packages/quantic/package.json | 1 + scripts/deploy/bump-version.mjs | 42 ++----- scripts/deploy/manual-major-prerelease.mjs | 73 +++++++++++ scripts/deploy/publish.mjs | 61 +++++---- scripts/exec.mjs | 20 ++- scripts/git.mjs | 65 ++++++++++ scripts/packages.mjs | 74 ++++++++++- scripts/prerelease.mjs | 75 +++++++++++ scripts/release.mjs | 15 +++ 19 files changed, 533 insertions(+), 83 deletions(-) create mode 100644 scripts/deploy/manual-major-prerelease.mjs create mode 100644 scripts/git.mjs create mode 100644 scripts/prerelease.mjs create mode 100644 scripts/release.mjs diff --git a/.deployment.config.json b/.deployment.config.json index ff87d26f6d5..eacb1fca148 100644 --- a/.deployment.config.json +++ b/.deployment.config.json @@ -234,7 +234,8 @@ "no_endpoint": true }, "package_rollout": { - "only_consider_changesets_after": "b244fe702d8e96d016a52715e92c8131acfde3ba" + "only_consider_changesets_after": "b244fe702d8e96d016a52715e92c8131acfde3ba", + "disabled": $[STOP_AT_DEV] }, "deployment_config_version": 7 } diff --git a/.github/workflows/masterbot.yml b/.github/workflows/masterbot.yml index 63ea20a1555..e41bb8fd1da 100644 --- a/.github/workflows/masterbot.yml +++ b/.github/workflows/masterbot.yml @@ -3,6 +3,7 @@ on: push: branches: - master + - 'prerelease/**' jobs: build: name: 'Build' diff --git a/Jenkinsfile b/Jenkinsfile index 47c15776e84..83f713a7fbb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,10 +1,17 @@ +def parseSemanticVersion(String version) { + def semanticVersionRegex = /^(((([0-9]+)\.[0-9]+)\.[0-9]+)(?:\-.+)?)$/ + def (_, prerelease, patch, minor, major) = (version =~ semanticVersionRegex)[0] + return [major, minor, patch, prerelease] +} + node('heavy && linux && docker') { checkout scm def tag = sh(script: "git tag --contains", returnStdout: true).trim() def isBump = !!tag - def isMaster = env.BRANCH_NAME == 'master' + def isOnReleaseBranch = env.BRANCH_NAME == 'master' + def isOnPrereleaseBranch = env.BRANCH_NAME.startsWith('prerelease/') - if (!isMaster) { + if (!isOnReleaseBranch && !isOnPrereleaseBranch) { return } @@ -37,7 +44,11 @@ node('heavy && linux && docker') { withCredentials([ string(credentialsId: 'NPM_TOKEN', variable: 'NPM_TOKEN')]) { sh "echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > ~/.npmrc" - sh 'npm run npm:publish:alpha || true' + if (isOnReleaseBranch) { + sh 'npm run npm:publish:alpha || true' + } else { + sh 'npm run npm:publish || true' + } } } } @@ -48,23 +59,22 @@ node('heavy && linux && docker') { headless = readJSON file: 'packages/headless/package.json' atomic = readJSON file: 'packages/atomic/package.json' atomicReact = readJSON file: 'packages/atomic-react/package.json' - semanticVersionRegex = /^([^\.]*)\.[^\.]*/ - - (headlessMinor, headlessMajor) = (headless.version =~ semanticVersionRegex)[0] - (atomicMinor, atomicMajor) = (atomic.version =~ semanticVersionRegex)[0] - (atomicReactMinor, atomicReactMajor) = (atomicReact.version =~ semanticVersionRegex)[0] + (headlessMajor, headlessMinor, headlessPatch) = parseSemanticVersion(headless.version) + (atomicMajor, atomicMinor, atomicPatch) = parseSemanticVersion(atomic.version) + (atomicReactMajor, atomicReactMinor, atomicReactPatch) = parseSemanticVersion(atomicReact) sh "deployment-package package create --with-deploy \ --resolve HEADLESS_MAJOR_VERSION=${headlessMajor} \ --resolve HEADLESS_MINOR_VERSION=${headlessMinor} \ - --resolve HEADLESS_PATCH_VERSION=${headless.version} \ + --resolve HEADLESS_PATCH_VERSION=${headlessPatch} \ --resolve ATOMIC_MAJOR_VERSION=${atomicMajor} \ --resolve ATOMIC_MINOR_VERSION=${atomicMinor} \ - --resolve ATOMIC_PATCH_VERSION=${atomic.version} \ + --resolve ATOMIC_PATCH_VERSION=${atomicPatch} \ --resolve ATOMIC_REACT_MAJOR_VERSION=${atomicReactMajor} \ --resolve ATOMIC_REACT_MINOR_VERSION=${atomicReactMinor} \ - --resolve ATOMIC_REACT_PATCH_VERSION=${atomicReact.version} \ + --resolve ATOMIC_REACT_PATCH_VERSION=${atomicReactPatch} \ + --resolve STOP_AT_DEV=${!isOnReleaseBranch} \ || true" } } diff --git a/README.md b/README.md index 18dce9e5aac..e86a97f1446 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,29 @@ The following Visual Studio Code extensions are recommended: - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + +## Prerelease +### To start a major prerelease (e.g., `1.2.3` → `2.0.0-pre.0`) +1. Make sure that `HEAD` refers to a version bump on `master`. +2. Create a new branch prefixed with `prerelease/` (e.g., `prerelease/headless_atomic_v2`). +3. Run `npm run bump:version:major-prerelease -- @coveo/package1 @coveo/package2 @coveo/package3`. +4. Push the new commit and its tags to the branch. + +### Adding a new package to an already started prerelease +1. Checkout the prerelease branch. +2. Run `npm run bump:version:major-prerelease -- @coveo/package1` where `@coveo/package1` is the new package. +3. Push the new commit and its tags to the branch. + +### Updating a prerelease branch +1. Wait for `master` to reference a version bump. +2. Pull changes from `master` into the prerelease branch. + +### Officially releasing (e.g., `2.0.0-pre.15` → `2.0.0`) +1. Create a pull request from the prerelease branch to “master”. + * Associate with a Jira issue which QA will use to validate. + * Update the changelogs manually, adding all the changes from the last release to the new release. + * Update all dependents to use the prerelease version (include the `-pre.` suffix). +2. Wait for `master` to reference a version bump. +3. Squash as a version bump. + * You can copy the title & description of the last version bump commit on the prerelease branch. + * You must add the link to the Jira issue at the beginning of the description. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 39089a67c82..a3e2f956939 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@commitlint/config-conventional": "17.0.3", "@commitlint/config-lerna-scopes": "17.0.2", "@commitlint/lint": "9.1.2", + "@coveo/semantic-monorepo-tools": "1.5.5", "@octokit/rest": "18.12.0", "@rollup/plugin-typescript": "8.3.4", "@trivago/prettier-plugin-sort-imports": "3.4.0", @@ -37,6 +38,7 @@ "concurrently": "6.5.1", "cross-fetch": "3.1.5", "cz-conventional-changelog": "3.3.0", + "detect-indent": "7.0.1", "esbuild": "0.14.2", "esbuild-plugin-alias": "0.2.1", "eslint": "8.22.0", @@ -3963,6 +3965,20 @@ "resolved": "packages/quantic", "link": true }, + "node_modules/@coveo/semantic-monorepo-tools": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@coveo/semantic-monorepo-tools/-/semantic-monorepo-tools-1.5.5.tgz", + "integrity": "sha512-zKd7ENzBoW9wPpKDleSa+w463HR0FfnqZO9vt27pbUjFuNAeQqwydI3nb9YGOJjen7USpfOA9D5zuJrjWTyVJg==", + "dev": true, + "dependencies": { + "conventional-changelog-writer": "^5.0.1", + "conventional-commits-parser": "^3.2.4", + "debug": "^4.3.3", + "git-raw-commits": "^2.0.11", + "semver": "^7.3.7", + "tempfile": "^4.0.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -24522,6 +24538,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/commitizen/node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/commitizen/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -27356,12 +27381,12 @@ } }, "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12.20" } }, "node_modules/detect-newline": { @@ -62198,6 +62223,31 @@ "node": ">=4" } }, + "node_modules/tempfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-4.0.0.tgz", + "integrity": "sha512-dNH6UWyI8kijDmLVb0IJvCG4JZ5uOmy40CLoi/dVySL49v0f0Y+jIN2rE6Hj85y8mIIya1vwpKZlL9jASs5ktg==", + "dev": true, + "dependencies": { + "temp-dir": "^2.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempfile/node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/tempy": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", @@ -66319,6 +66369,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/write-json-file/node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/write-json-file/node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -84899,6 +84958,20 @@ } } }, + "@coveo/semantic-monorepo-tools": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@coveo/semantic-monorepo-tools/-/semantic-monorepo-tools-1.5.5.tgz", + "integrity": "sha512-zKd7ENzBoW9wPpKDleSa+w463HR0FfnqZO9vt27pbUjFuNAeQqwydI3nb9YGOJjen7USpfOA9D5zuJrjWTyVJg==", + "dev": true, + "requires": { + "conventional-changelog-writer": "^5.0.1", + "conventional-commits-parser": "^3.2.4", + "debug": "^4.3.3", + "git-raw-commits": "^2.0.11", + "semver": "^7.3.7", + "tempfile": "^4.0.0" + } + }, "@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -102896,6 +102969,12 @@ "supports-color": "^7.1.0" } }, + "detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true + }, "fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -105083,9 +105162,9 @@ "dev": true }, "detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", "dev": true }, "detect-newline": { @@ -132692,6 +132771,24 @@ "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", "dev": true }, + "tempfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-4.0.0.tgz", + "integrity": "sha512-dNH6UWyI8kijDmLVb0IJvCG4JZ5uOmy40CLoi/dVySL49v0f0Y+jIN2rE6Hj85y8mIIya1vwpKZlL9jASs5ktg==", + "dev": true, + "requires": { + "temp-dir": "^2.0.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + } + } + }, "tempy": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", @@ -135763,6 +135860,12 @@ "write-file-atomic": "^3.0.0" }, "dependencies": { + "detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true + }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", diff --git a/package.json b/package.json index fcd7cdabe2d..785ecc9f8c9 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "commit": "git-cz", "setup:snyk": "node ./scripts/snyk/remove-samples-workspace.mjs", "bump:version": "node ./scripts/deploy/bump-version.mjs", + "bump:version:major-prerelease": "node ./scripts/deploy/manual-major-prerelease.mjs", + "npm:publish": "lerna run --stream npm:publish", "npm:publish:alpha": "lerna run --stream npm:publish:alpha", "npm:tag": "node ./scripts/deploy/update-npm-tag.mjs", "npm:tag:beta": "npm run npm:tag -- beta", @@ -58,7 +60,9 @@ "rimraf": "3.0.2", "semver": "7.3.7", "ts-jest": "27.1.5", - "typescript": "4.6.4" + "typescript": "4.6.4", + "@coveo/semantic-monorepo-tools": "1.5.5", + "detect-indent": "7.0.1" }, "workspaces": [ "packages/bueno", diff --git a/packages/atomic-angular/modify-package-json.js b/packages/atomic-angular/modify-package-json.js index 4f037218362..3cd6465a2f1 100644 --- a/packages/atomic-angular/modify-package-json.js +++ b/packages/atomic-angular/modify-package-json.js @@ -7,6 +7,8 @@ const packageJSON = JSON.parse(readFileSync(packageJSONPath)); if (!packageJSON.scripts) { packageJSON.scripts = {}; } +packageJSON.scripts['npm:publish'] = + 'node ../../../../../scripts/deploy/publish.mjs'; packageJSON.scripts['npm:publish:alpha'] = 'node ../../../../../scripts/deploy/publish.mjs alpha'; diff --git a/packages/atomic-angular/package.json b/packages/atomic-angular/package.json index eaa6bf54307..3da766b830f 100644 --- a/packages/atomic-angular/package.json +++ b/packages/atomic-angular/package.json @@ -6,6 +6,7 @@ "start": "ng serve", "build": "ng build && npm run copy:assets && npm run npm:modify:dist", "copy:assets": "ncp ../atomic/dist/atomic/assets projects/atomic-angular/dist/assets && ncp ../atomic/dist/atomic/lang projects/atomic-angular/dist/lang", + "npm:publish": "npm --prefix projects/atomic-angular/dist run npm:publish", "npm:publish:alpha": "npm --prefix projects/atomic-angular/dist run npm:publish:alpha", "npm:modify:dist": "node modify-package-json.js" }, diff --git a/packages/atomic-react/package.json b/packages/atomic-react/package.json index bc7bd4c7e2b..ec9e483a008 100644 --- a/packages/atomic-react/package.json +++ b/packages/atomic-react/package.json @@ -15,6 +15,7 @@ "compile:cjs": "tsc -p tsconfig.cjs.json", "compile:iife": "rollup --config rollup.config.js", "compile": "concurrently \"npm run compile:esm\" \"npm run compile:cjs\" \"npm run compile:iife\"", + "npm:publish": "node ../../scripts/deploy/publish.mjs", "npm:publish:alpha": "node ../../scripts/deploy/publish.mjs alpha", "copy:assets": "ncp ../atomic/dist/atomic/assets dist/assets && ncp ../atomic/dist/atomic/lang dist/lang " }, diff --git a/packages/auth/package.json b/packages/auth/package.json index 232a56c303a..3163d65aa11 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -17,6 +17,7 @@ "test:watch": "jest --watch --colors --no-cache --silent=false", "typedefinitions": "tsc -p src/tsconfig.build.json -d --emitDeclarationOnly --declarationDir dist/definitions", "clean": "rimraf -rf dist/*", + "npm:publish": "node ../../scripts/deploy/publish.mjs", "npm:publish:alpha": "node ../../scripts/deploy/publish.mjs alpha", "e2e:saml": "vite manual-e2e/saml/" }, diff --git a/packages/quantic/package.json b/packages/quantic/package.json index a6020b57af4..39334a86277 100644 --- a/packages/quantic/package.json +++ b/packages/quantic/package.json @@ -32,6 +32,7 @@ "doc:generate": "mkdir -p docs/out; jsdoc -c jsdoc-config.json > docs/out/quantic-docs.json", "package:create": "../../node_modules/.bin/ts-node scripts/build/create-package.ts --remove-translations", "package:create:publish": "../../node_modules/.bin/ts-node scripts/build/create-package.ts --remove-translations --promote", + "npm:publish": "node ../../scripts/deploy/publish.mjs", "npm:publish:alpha": "node ../../scripts/deploy/publish.mjs alpha", "preinstall": "node scripts/npm/check-sfdx-project.js", "postinstall": "node scripts/npm/setup-quantic.js" diff --git a/scripts/deploy/bump-version.mjs b/scripts/deploy/bump-version.mjs index 34be4d8d61d..f45156afb94 100644 --- a/scripts/deploy/bump-version.mjs +++ b/scripts/deploy/bump-version.mjs @@ -1,30 +1,16 @@ -import {execute} from '../exec.mjs'; - -async function getHeadCommitHash() { - return execute('git', ['rev-parse', 'HEAD']); -} - -async function getHeadCommitTag() { - return execute('git', ['tag', '--points-at', 'HEAD']); -} - -async function checkoutLatestMaster() { - await execute('git', ['checkout', 'master']); - await execute('git', ['pull', 'origin', 'master']); -} +import { + isOnReleaseBranch, + getHowManyCommitsBehindUpstream, + getHeadCommitTag, +} from '../git.mjs'; +import {bumpPrereleaseVersionAndPush} from '../prerelease.mjs'; +import {bumpReleaseVersionAndPush} from '../release.mjs'; async function bumpVersionAndPush() { try { - await execute('npx', [ - '--no-install', - 'lerna', - 'version', - '--conventional-commits', - '--conventional-graduate', - '--no-private', - '--yes', - '--exact', - ]); + (await isOnReleaseBranch()) + ? await bumpReleaseVersionAndPush() + : await bumpPrereleaseVersionAndPush(); } catch (e) { console.error( 'Failed to bump version. Exiting to not publish local changes.', @@ -36,13 +22,9 @@ async function bumpVersionAndPush() { async function main() { try { - const buildCommitHash = await getHeadCommitHash(); - await checkoutLatestMaster(); - const masterCommitHash = await getHeadCommitHash(); - - if (buildCommitHash !== masterCommitHash) { + if ((await getHowManyCommitsBehindUpstream()) !== 0) { console.log( - 'Build commit does not match latest master commit. Skipping version bump.' + 'Build commit does not match latest commit. Skipping version bump.' ); return; } diff --git a/scripts/deploy/manual-major-prerelease.mjs b/scripts/deploy/manual-major-prerelease.mjs new file mode 100644 index 00000000000..e267bf1c8d3 --- /dev/null +++ b/scripts/deploy/manual-major-prerelease.mjs @@ -0,0 +1,73 @@ +import {getCurrentVersion} from '@coveo/semantic-monorepo-tools'; +import {resolve} from 'path'; +import {execute} from '../exec.mjs'; +import {commitVersionBump, stageAll, tagExists, tagPackages} from '../git.mjs'; +import { + getPackageDefinitionFromPackageName, + updatePackageVersion, +} from '../packages.mjs'; +import {prereleaseSuffix} from '../prerelease.mjs'; + +/** + * @typedef {import('../packages.mjs').PackageDefinition} PackageDefinition + */ + +/** + * @param {number} packageName + * @param {number} major + * @param {number} prerelease + * @returns {Promise} + */ +async function getNewMajorPrerelease(packageName, major, prerelease = 0) { + const version = `${major}.0.0-${prereleaseSuffix}.${prerelease}`; + if (await tagExists(`${packageName}@${version}`)) { + return getNewMajorPrerelease(packageName, major, prerelease + 1); + } + return version; +} + +/** + * @param {PackageDefinition} packageDef + */ +async function getNewVersion(packageDef) { + const version = getCurrentVersion( + resolve('.', 'packages', packageDef.packageDir) + ); + return getNewMajorPrerelease(packageDef.name, version.major + 1); +} + +/** + * @param {PackageDefinition[]} packages + */ +async function locallyBumpVersions(packages) { + for (const packageDef of packages) { + const newVersion = await getNewVersion(packageDef); + updatePackageVersion( + packageDef.name, + newVersion, + packages.map(({packageDir}) => packageDir) + ); + } + await execute('npm', ['install', '--package-lock-only']); +} + +/** + * @param {string[]} args + */ +export async function main(args) { + const packageNamesToBump = args.slice(2); + if (!packageNamesToBump.length) { + console.error('You must specify which packages to bump.'); + return; + } + const packages = packageNamesToBump.map(getPackageDefinitionFromPackageName); + await locallyBumpVersions(packages); + const updatedPackages = packageNamesToBump.map( + getPackageDefinitionFromPackageName + ); + await stageAll(); + await commitVersionBump(updatedPackages); + await tagPackages(updatedPackages); +} + +main(process.argv); diff --git a/scripts/deploy/publish.mjs b/scripts/deploy/publish.mjs index 8a6a974cdf5..a79cd9a08fb 100644 --- a/scripts/deploy/publish.mjs +++ b/scripts/deploy/publish.mjs @@ -1,42 +1,55 @@ -import {execSync, spawnSync} from 'child_process'; -import {readFileSync} from 'fs'; import {resolve} from 'path'; -import {getPackageFromPath} from '../packages.mjs'; +import {execute} from '../exec.mjs'; +import {isOnReleaseBranch} from '../git.mjs'; +import {getPackageDefinitionFromPath} from '../packages.mjs'; +import {isPrereleaseVersion} from '../prerelease.mjs'; const [tag] = process.argv.slice(2); -const pathToPackageJSON = resolve(process.cwd(), 'package.json'); -const pkg = getPackageFromPath(pathToPackageJSON); +const pathToPackageJSON = resolve(process.cwd(), './package.json'); +const pkg = getPackageDefinitionFromPath(pathToPackageJSON); const packageRef = `${pkg.name}@${pkg.version}`; -function shouldPublish() { +async function isAlreadyPublished() { try { - const packageVersionNotPublished = !execSync( - `npm view ${packageRef}` - ).toString().length; - return packageVersionNotPublished; + const isPublished = !!execute('npm', ['view', packageRef]); + return isPublished; } catch (e) { - const isFirstPublish = e.toString().includes('code E404'); - return isFirstPublish; + const isFirstPublish = e.includes('code E404'); + return !isFirstPublish; } } -function publish() { +async function shouldPublish() { + if (isAlreadyPublished()) { + return false; + } + if (await isOnReleaseBranch()) { + return true; + } + // On a prerelease branch, we may not want to prerelease some packages. + // We only want to prerelease packages that were already bumped to a prerelease version. + return isPrereleaseVersion(pkg.version); +} + +async function publish() { const params = ['publish', '--verbose', '--access', 'public']; if (tag) { params.push('--tag', tag); } - spawnSync('npm', params, { - stdio: 'inherit', - }); + await execute('npm', params); } -if (shouldPublish()) { - publish(); -} else { - console.info( - `Skipped publishing ${packageRef} (${ - tag || 'latest' - }) since it's already published.` - ); +async function main() { + if (await shouldPublish()) { + await publish(); + } else { + console.info( + `Skipped publishing ${packageRef} (${ + tag || 'latest' + }) since it's already published.` + ); + } } + +main(); diff --git a/scripts/exec.mjs b/scripts/exec.mjs index b046a1a55bb..ff0147d22a1 100644 --- a/scripts/exec.mjs +++ b/scripts/exec.mjs @@ -1,3 +1,4 @@ +import {Buffer} from 'buffer'; import {spawn} from 'child_process'; /** @@ -15,8 +16,10 @@ function trimNewline(str) { export function execute(command, args = []) { return new Promise((resolve, reject) => { const proc = spawn(command, args); - let data = ''; - let error = ''; + /** @type {Uint8Array} */ + let dataChunks = []; + /** @type {Uint8Array} */ + let errorChunks = []; console.log( '\x1b[35m>\x1b[0m\xa0', @@ -28,7 +31,7 @@ export function execute(command, args = []) { proc.stdout.on('data', (chunk) => { console.log(trimNewline(chunk.toString())); - data += chunk.toString(); + dataChunks.push(Buffer.from(chunk)); }); proc.stderr.on('data', (chunk) => { const exclamation = '\x1b[31m!\x1b[0m\xa0'; @@ -36,12 +39,17 @@ export function execute(command, args = []) { exclamation, trimNewline(chunk.toString()).replace('\n', '\n' + exclamation) ); - error += chunk.toString(); + errorChunks.push(Buffer.from(chunk)); }); proc.on('exit', (code) => code === 0 - ? resolve(trimNewline(data)) - : reject(JSON.stringify({code, error: trimNewline(error)})) + ? resolve(trimNewline(Buffer.concat(dataChunks).toString('utf8'))) + : reject( + JSON.stringify({ + code, + error: trimNewline(Buffer.concat(errorChunks).toString('utf8')), + }) + ) ); }); } diff --git a/scripts/git.mjs b/scripts/git.mjs new file mode 100644 index 00000000000..bf875fb881c --- /dev/null +++ b/scripts/git.mjs @@ -0,0 +1,65 @@ +import {gitTag} from '@coveo/semantic-monorepo-tools'; +import {readFileSync} from 'fs'; +import {resolve} from 'path'; +import {execute} from './exec.mjs'; + +/** + * @typedef {import('./packages.mjs').PackageDefinition} PackageDefinition + */ + +const releaseBranch = 'master'; + +async function getBranchName() { + return await execute('git', ['branch', '--show-current']); +} + +export async function isOnReleaseBranch() { + return (await getBranchName()) === releaseBranch; +} + +export async function getHowManyCommitsBehindUpstream() { + return parseInt(await execute('git', ['rev-list', '--count', 'HEAD..@{u}'])); +} + +export async function getHeadCommitTag() { + return await execute('git', ['tag', '--points-at', 'HEAD']); +} + +/** + * @param {string} tag + */ +export async function tagExists(tag) { + return !!(await execute('git', ['tag', '-l', tag])); +} + +export async function stageAll() { + await execute('git', ['add', '.']); +} + +/** + * @param {PackageDefinition[]} updatedPackages + */ +export async function commitVersionBump(updatedPackages) { + /** @type {{command: {version: {message: string}}}} */ + const lernaConfig = JSON.parse( + readFileSync(resolve('.', 'lerna.json')).toString() + ); + const lernaMessageSection = lernaConfig.command.version.message; + const updatedPackagesMessageSection = updatedPackages + .map( + (packageDef) => `\u0020-\u0020${packageDef.name}@${packageDef.version}` + ) + .join('\n'); + const message = `${lernaMessageSection}\n\n${updatedPackagesMessageSection}`; + await execute('git', ['commit', '--no-verify', '-m', message]); +} + +/** + * @param {PackageDefinition[]} packages + */ +export async function tagPackages(packages) { + for (const {name, version} of packages) { + const tag = `${name}@${version}`; + await gitTag(tag); + } +} diff --git a/scripts/packages.mjs b/scripts/packages.mjs index ef0f0525cad..7edc67399db 100644 --- a/scripts/packages.mjs +++ b/scripts/packages.mjs @@ -1,4 +1,7 @@ +import detectIndent from 'detect-indent'; +import {existsSync, writeFileSync} from 'fs'; import {readFileSync} from 'fs'; +import {resolve} from 'path'; export const packageDirsNpmTag = [ 'atomic', @@ -12,11 +15,76 @@ export const packageDirsNpmTag = [ export const packageDirsSnyk = ['headless', 'atomic']; +/** + * @typedef PackageDefinition + * @property {string} name + * @property {string} version + * @property {string} packageDir Relative to `/packages` (e.g., `atomic-angular/projects/atomic-angular`). + */ + /** * @param {string} fullPath - * @returns {{name: string, version: string}} + * @returns {import('@lerna/package').RawManifest} */ export function getPackageFromPath(fullPath) { - const {name, version} = JSON.parse(readFileSync(fullPath).toString()); - return {name, version}; + return JSON.parse(readFileSync(fullPath).toString()); +} + +/** + * @param {string} packageDir E.g.: `samples/some-package` + * @returns {PackageDefinition} + */ +export function getPackageDefinitionFromPackageDir(packageDir) { + const fullPath = resolve('.', 'packages', packageDir, 'package.json'); + if (!existsSync(fullPath)) { + throw `Could not find package at ${fullPath}.`; + } + const {name, version} = getPackageFromPath(fullPath); + return {name, version, packageDir}; +} + +/** + * @param {string} name + */ +export function getPackageDefinitionFromPackageName(name) { + return packageDirsNpmTag + .map((packageDir) => getPackageDefinitionFromPackageDir(packageDir)) + .find((packageDef) => packageDef.name === name); +} + +/** + * @param {string} packageName + * @param {string} newVersion + * @param {string[]} depdendenciesPackageDirs E.g.: [`samples/some-package`] + */ +export function updatePackageVersion( + packageName, + newVersion, + depdendenciesPackageDirs +) { + depdendenciesPackageDirs.forEach((packageDir) => { + const fullPath = resolve('.', 'packages', packageDir, 'package.json'); + const originalContentAsText = readFileSync(fullPath).toString(); + const {indent} = detectIndent(originalContentAsText); + /** @type {import('@lerna/package').RawManifest} */ + const manifest = JSON.parse(originalContentAsText); + + if (manifest.name === packageName) { + manifest.version = newVersion; + } else { + if (packageName in manifest.dependencies) { + manifest.dependencies[packageName] = newVersion; + } + if (packageName in manifest.devDependencies) { + manifest.devDependencies[packageName] = newVersion; + } + if (packageName in manifest.peerDependencies) { + manifest.peerDependencies[packageName] = newVersion; + } + } + writeFileSync( + fullPath, + JSON.stringify(manifest, undefined, indent || ' ') + ); + }); } diff --git a/scripts/prerelease.mjs b/scripts/prerelease.mjs new file mode 100644 index 00000000000..b6fb26d02fb --- /dev/null +++ b/scripts/prerelease.mjs @@ -0,0 +1,75 @@ +import { + getNextVersion, + getCurrentVersion, + gitPush, + gitPushTags, +} from '@coveo/semantic-monorepo-tools'; +import {resolve} from 'path'; +import {execute} from './exec.mjs'; +import {commitVersionBump, stageAll, tagPackages} from './git.mjs'; +import { + packageDirsNpmTag, + getPackageDefinitionFromPackageDir, + updatePackageVersion, +} from './packages.mjs'; + +/** + * @typedef {import('./packages.mjs').PackageDefinition} PackageDefinition + */ + +export const prereleaseSuffix = 'pre'; + +/** + * @param {string} version + */ +export function isPrereleaseVersion(version) { + return !!version.match(new RegExp(`-${prereleaseSuffix}\\.[0-9]+\$`)); +} + +function getPackagesToPrereleaseBump() { + return packageDirsNpmTag + .map(getPackageDefinitionFromPackageDir) + .filter(({version}) => isPrereleaseVersion(version)); +} + +/** + * @param {PackageDefinition} packageDef + */ +async function getNewVersion(packageDef) { + const version = getCurrentVersion( + resolve('.', 'packages', packageDef.packageDir) + ); + return getNextVersion(version, { + type: 'prerelease', + preid: prereleaseSuffix, + }); +} + +/** + * @param {PackageDefinition[]} packages + */ +async function locallyBumpVersions(packages) { + for (const packageDef of packages) { + const newVersion = await getNewVersion(packageDef); + updatePackageVersion( + packageDef.name, + newVersion, + packages.map(({packageDir}) => packageDir) + ); + } + await execute('npm', ['install', '--package-lock-only']); +} + +export async function bumpPrereleaseVersionAndPush() { + console.info('Doing a prerelease version bump.'); + const packages = getPackagesToPrereleaseBump(); + await locallyBumpVersions(packages); + const updatedPackages = packages.map(({packageDir}) => + getPackageDefinitionFromPackageDir(packageDir) + ); + await stageAll(); + await commitVersionBump(updatedPackages); + await tagPackages(updatedPackages); + await gitPush(); + await gitPushTags(); +} diff --git a/scripts/release.mjs b/scripts/release.mjs new file mode 100644 index 00000000000..e1cd5dc167a --- /dev/null +++ b/scripts/release.mjs @@ -0,0 +1,15 @@ +import {execute} from './exec.mjs'; + +export async function bumpReleaseVersionAndPush() { + console.info('Doing a release version bump.'); + await execute('npx', [ + '--no-install', + 'lerna', + 'version', + '--conventional-commits', + '--conventional-graduate', + '--no-private', + '--yes', + '--exact', + ]); +} From c460721003c3fdf3c7ee7bb4dd5d8ecc48195745 Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Tue, 22 Nov 2022 09:30:35 -0500 Subject: [PATCH 2/8] fixed updatePackageVersion https://coveord.atlassian.net/browse/KIT-2114 --- scripts/packages.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/packages.mjs b/scripts/packages.mjs index 7edc67399db..6570c6d335b 100644 --- a/scripts/packages.mjs +++ b/scripts/packages.mjs @@ -72,13 +72,13 @@ export function updatePackageVersion( if (manifest.name === packageName) { manifest.version = newVersion; } else { - if (packageName in manifest.dependencies) { + if (packageName in (manifest.dependencies || {})) { manifest.dependencies[packageName] = newVersion; } - if (packageName in manifest.devDependencies) { + if (packageName in (manifest.devDependencies || {})) { manifest.devDependencies[packageName] = newVersion; } - if (packageName in manifest.peerDependencies) { + if (packageName in (manifest.peerDependencies || {})) { manifest.peerDependencies[packageName] = newVersion; } } From 74dc65809efb11e21aad074153dfa402a13fe48a Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Tue, 22 Nov 2022 09:51:53 -0500 Subject: [PATCH 3/8] fixed error in jenkinsfile https://coveord.atlassian.net/browse/KIT-2114 --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 83f713a7fbb..01d575545ae 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -62,7 +62,7 @@ node('heavy && linux && docker') { (headlessMajor, headlessMinor, headlessPatch) = parseSemanticVersion(headless.version) (atomicMajor, atomicMinor, atomicPatch) = parseSemanticVersion(atomic.version) - (atomicReactMajor, atomicReactMinor, atomicReactPatch) = parseSemanticVersion(atomicReact) + (atomicReactMajor, atomicReactMinor, atomicReactPatch) = parseSemanticVersion(atomicReact.version) sh "deployment-package package create --with-deploy \ --resolve HEADLESS_MAJOR_VERSION=${headlessMajor} \ From 2df2a35534ab937e0341de165248cc089f4a290b Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Thu, 24 Nov 2022 15:51:17 -0500 Subject: [PATCH 4/8] Made `execute` use buffers directly instead of an array of buffers https://coveord.atlassian.net/browse/KIT-2114 --- scripts/exec.mjs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/exec.mjs b/scripts/exec.mjs index ff0147d22a1..b553ca7de29 100644 --- a/scripts/exec.mjs +++ b/scripts/exec.mjs @@ -16,10 +16,10 @@ function trimNewline(str) { export function execute(command, args = []) { return new Promise((resolve, reject) => { const proc = spawn(command, args); - /** @type {Uint8Array} */ - let dataChunks = []; - /** @type {Uint8Array} */ - let errorChunks = []; + /** @type {Buffer} */ + let dataBuffer = Buffer.alloc(0); + /** @type {Buffer} */ + let errorBuffer = Buffer.alloc(0); console.log( '\x1b[35m>\x1b[0m\xa0', @@ -31,7 +31,7 @@ export function execute(command, args = []) { proc.stdout.on('data', (chunk) => { console.log(trimNewline(chunk.toString())); - dataChunks.push(Buffer.from(chunk)); + dataBuffer = Buffer.concat([dataBuffer, chunk]); }); proc.stderr.on('data', (chunk) => { const exclamation = '\x1b[31m!\x1b[0m\xa0'; @@ -39,15 +39,15 @@ export function execute(command, args = []) { exclamation, trimNewline(chunk.toString()).replace('\n', '\n' + exclamation) ); - errorChunks.push(Buffer.from(chunk)); + errorBuffer = Buffer.concat([errorBuffer, chunk]); }); proc.on('exit', (code) => code === 0 - ? resolve(trimNewline(Buffer.concat(dataChunks).toString('utf8'))) + ? resolve(trimNewline(dataBuffer.toString('utf8'))) : reject( JSON.stringify({ code, - error: trimNewline(Buffer.concat(errorChunks).toString('utf8')), + error: trimNewline(errorBuffer.toString('utf8')), }) ) ); From 4ce9b17d3ecf5884146fb85eaadc8b2a501e9eae Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Thu, 24 Nov 2022 16:14:20 -0500 Subject: [PATCH 5/8] prefixed all NodeJS packages with `node:` https://coveord.atlassian.net/browse/KIT-2114 --- packages/atomic/scripts/copy-dayjs-locales.mjs | 2 +- packages/atomic/scripts/copy-headless.mjs | 2 +- packages/atomic/scripts/create-generated-folder.mjs | 2 +- packages/atomic/scripts/list-assets.mjs | 2 +- packages/atomic/scripts/split-locales.mjs | 2 +- packages/headless/esbuild.mjs | 4 ++-- packages/headless/scripts/extract-documentation.mjs | 2 +- .../samples/angular/scripts/link-hoisted-dependencies.mjs | 4 ++-- scripts/deploy/invalidate-cloudfront.mjs | 2 +- scripts/deploy/manual-major-prerelease.mjs | 2 +- scripts/deploy/publish.mjs | 2 +- scripts/deploy/update-npm-tag.mjs | 4 ++-- scripts/exec.mjs | 4 ++-- scripts/git.mjs | 4 ++-- scripts/notify-docs/published-ui-kit.mjs | 2 +- scripts/packages.mjs | 5 ++--- scripts/prerelease.mjs | 2 +- scripts/reports/bundle-size/command.mjs | 4 ++-- scripts/reports/bundle-size/time-series.mjs | 2 +- scripts/snyk/remove-samples-workspace.mjs | 6 +++--- 20 files changed, 29 insertions(+), 30 deletions(-) diff --git a/packages/atomic/scripts/copy-dayjs-locales.mjs b/packages/atomic/scripts/copy-dayjs-locales.mjs index a810c8a870a..35fb777bbb2 100644 --- a/packages/atomic/scripts/copy-dayjs-locales.mjs +++ b/packages/atomic/scripts/copy-dayjs-locales.mjs @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'node:fs'; import {promisify} from 'util'; const readFile = promisify(fs.readFile); diff --git a/packages/atomic/scripts/copy-headless.mjs b/packages/atomic/scripts/copy-headless.mjs index ffae12ed4c2..a7dafefa7f3 100644 --- a/packages/atomic/scripts/copy-headless.mjs +++ b/packages/atomic/scripts/copy-headless.mjs @@ -1,5 +1,5 @@ -import fs from 'fs'; import _ncp from 'ncp'; +import fs from 'node:fs'; import {promisify} from 'util'; const ncp = promisify(_ncp); diff --git a/packages/atomic/scripts/create-generated-folder.mjs b/packages/atomic/scripts/create-generated-folder.mjs index f1081becd2b..faf5a1920ae 100644 --- a/packages/atomic/scripts/create-generated-folder.mjs +++ b/packages/atomic/scripts/create-generated-folder.mjs @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'node:fs'; import {promisify} from 'util'; const rm = promisify(fs.rm); diff --git a/packages/atomic/scripts/list-assets.mjs b/packages/atomic/scripts/list-assets.mjs index 59447a7601c..f770c972227 100644 --- a/packages/atomic/scripts/list-assets.mjs +++ b/packages/atomic/scripts/list-assets.mjs @@ -1,4 +1,4 @@ -import {readdirSync, writeFileSync} from 'fs'; +import {readdirSync, writeFileSync} from 'node:fs'; const files = readdirSync('dist/atomic/assets'); writeFileSync('docs/assets.json', JSON.stringify({assets: files})); diff --git a/packages/atomic/scripts/split-locales.mjs b/packages/atomic/scripts/split-locales.mjs index b48d2112640..e305d22b71f 100644 --- a/packages/atomic/scripts/split-locales.mjs +++ b/packages/atomic/scripts/split-locales.mjs @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'node:fs'; import {promisify} from 'util'; const mkdir = promisify(fs.mkdir); diff --git a/packages/headless/esbuild.mjs b/packages/headless/esbuild.mjs index 2a84bfbc855..3458432c724 100644 --- a/packages/headless/esbuild.mjs +++ b/packages/headless/esbuild.mjs @@ -1,7 +1,7 @@ import {build} from 'esbuild'; import alias from 'esbuild-plugin-alias'; -import {readFileSync, promises} from 'fs'; -import {resolve} from 'path'; +import {readFileSync, promises} from 'node:fs'; +import {resolve} from 'node:path'; import {umdWrapper} from '../../scripts/bundle/umd.mjs'; import {apacheLicense} from '../../scripts/license/apache.mjs'; diff --git a/packages/headless/scripts/extract-documentation.mjs b/packages/headless/scripts/extract-documentation.mjs index a0e78cfaac8..032da5c70b9 100644 --- a/packages/headless/scripts/extract-documentation.mjs +++ b/packages/headless/scripts/extract-documentation.mjs @@ -1,4 +1,4 @@ -import {readdirSync} from 'fs'; +import {readdirSync} from 'node:fs'; import {promisify} from 'util'; import {execute} from '../../../scripts/exec.mjs'; diff --git a/packages/samples/angular/scripts/link-hoisted-dependencies.mjs b/packages/samples/angular/scripts/link-hoisted-dependencies.mjs index 652941eab63..f97b7fd8f65 100644 --- a/packages/samples/angular/scripts/link-hoisted-dependencies.mjs +++ b/packages/samples/angular/scripts/link-hoisted-dependencies.mjs @@ -1,7 +1,7 @@ // TODO: remove this script when nohoist (https://github.com/npm/rfcs/issues/287) is introduced. import {execSync} from 'child_process'; -import {existsSync, symlinkSync, mkdirSync} from 'fs'; -import {resolve, parse} from 'path'; +import {existsSync, symlinkSync, mkdirSync} from 'node:fs'; +import {resolve, parse} from 'node:path'; /** @param {string} dependency */ function getLocalPathToDependency(dependency) { diff --git a/scripts/deploy/invalidate-cloudfront.mjs b/scripts/deploy/invalidate-cloudfront.mjs index 37730edbc52..97a3fd6569b 100644 --- a/scripts/deploy/invalidate-cloudfront.mjs +++ b/scripts/deploy/invalidate-cloudfront.mjs @@ -1,5 +1,5 @@ import {CloudFront} from 'aws-sdk'; -import {resolve} from 'path'; +import {resolve} from 'node:path'; import {getPackageFromPath} from '../packages.mjs'; const cloudfront = new CloudFront(); diff --git a/scripts/deploy/manual-major-prerelease.mjs b/scripts/deploy/manual-major-prerelease.mjs index e267bf1c8d3..5d0f3b9be4e 100644 --- a/scripts/deploy/manual-major-prerelease.mjs +++ b/scripts/deploy/manual-major-prerelease.mjs @@ -1,5 +1,5 @@ import {getCurrentVersion} from '@coveo/semantic-monorepo-tools'; -import {resolve} from 'path'; +import {resolve} from 'node:path'; import {execute} from '../exec.mjs'; import {commitVersionBump, stageAll, tagExists, tagPackages} from '../git.mjs'; import { diff --git a/scripts/deploy/publish.mjs b/scripts/deploy/publish.mjs index a79cd9a08fb..1489449afa6 100644 --- a/scripts/deploy/publish.mjs +++ b/scripts/deploy/publish.mjs @@ -1,4 +1,4 @@ -import {resolve} from 'path'; +import {resolve} from 'node:path'; import {execute} from '../exec.mjs'; import {isOnReleaseBranch} from '../git.mjs'; import {getPackageDefinitionFromPath} from '../packages.mjs'; diff --git a/scripts/deploy/update-npm-tag.mjs b/scripts/deploy/update-npm-tag.mjs index 4ea4030544f..3685a876bcc 100644 --- a/scripts/deploy/update-npm-tag.mjs +++ b/scripts/deploy/update-npm-tag.mjs @@ -1,7 +1,7 @@ +import {resolve} from 'node:path'; +import {promisify} from 'node:util'; import {execute} from '../exec.mjs'; import {packageDirsNpmTag, getPackageFromPath} from '../packages.mjs'; -import {resolve} from 'path'; -import {promisify} from 'util'; async function main() { const requests = packageDirsNpmTag diff --git a/scripts/exec.mjs b/scripts/exec.mjs index b553ca7de29..cb8f56ce781 100644 --- a/scripts/exec.mjs +++ b/scripts/exec.mjs @@ -1,5 +1,5 @@ -import {Buffer} from 'buffer'; -import {spawn} from 'child_process'; +import {Buffer} from 'node:buffer'; +import {spawn} from 'node:child_process'; /** * @param {string} str diff --git a/scripts/git.mjs b/scripts/git.mjs index bf875fb881c..ec7a24902ce 100644 --- a/scripts/git.mjs +++ b/scripts/git.mjs @@ -1,6 +1,6 @@ import {gitTag} from '@coveo/semantic-monorepo-tools'; -import {readFileSync} from 'fs'; -import {resolve} from 'path'; +import {readFileSync} from 'node:fs'; +import {resolve} from 'node:path'; import {execute} from './exec.mjs'; /** diff --git a/scripts/notify-docs/published-ui-kit.mjs b/scripts/notify-docs/published-ui-kit.mjs index 00d88e980ba..f686a1ab76c 100644 --- a/scripts/notify-docs/published-ui-kit.mjs +++ b/scripts/notify-docs/published-ui-kit.mjs @@ -1,5 +1,5 @@ import {Octokit} from '@octokit/rest'; -import {resolve} from 'path'; +import {resolve} from 'node:path'; import {getPackageFromPath} from '../packages.mjs'; const headlessPackageJson = getPackageFromPath( diff --git a/scripts/packages.mjs b/scripts/packages.mjs index 6570c6d335b..21a80c934d3 100644 --- a/scripts/packages.mjs +++ b/scripts/packages.mjs @@ -1,7 +1,6 @@ import detectIndent from 'detect-indent'; -import {existsSync, writeFileSync} from 'fs'; -import {readFileSync} from 'fs'; -import {resolve} from 'path'; +import {existsSync, writeFileSync, readFileSync} from 'node:fs'; +import {resolve} from 'node:path'; export const packageDirsNpmTag = [ 'atomic', diff --git a/scripts/prerelease.mjs b/scripts/prerelease.mjs index b6fb26d02fb..f1d2fda4083 100644 --- a/scripts/prerelease.mjs +++ b/scripts/prerelease.mjs @@ -4,7 +4,7 @@ import { gitPush, gitPushTags, } from '@coveo/semantic-monorepo-tools'; -import {resolve} from 'path'; +import {resolve} from 'node:path'; import {execute} from './exec.mjs'; import {commitVersionBump, stageAll, tagPackages} from './git.mjs'; import { diff --git a/scripts/reports/bundle-size/command.mjs b/scripts/reports/bundle-size/command.mjs index a7ad90683f3..c9c092e3443 100644 --- a/scripts/reports/bundle-size/command.mjs +++ b/scripts/reports/bundle-size/command.mjs @@ -1,5 +1,5 @@ -import {statSync, readdirSync} from 'fs'; -import {resolve} from 'path'; +import {statSync, readdirSync} from 'node:fs'; +import {resolve} from 'node:path'; import {execute} from '../../exec.mjs'; async function setup() { diff --git a/scripts/reports/bundle-size/time-series.mjs b/scripts/reports/bundle-size/time-series.mjs index 32a6cff5e89..57493eecd22 100644 --- a/scripts/reports/bundle-size/time-series.mjs +++ b/scripts/reports/bundle-size/time-series.mjs @@ -1,4 +1,4 @@ -import {existsSync, writeFileSync, appendFileSync} from 'fs'; +import {existsSync, writeFileSync, appendFileSync} from 'node:fs'; import {computeFileSizes} from './command.mjs'; const branch = process.env.GIT_BRANCH; diff --git a/scripts/snyk/remove-samples-workspace.mjs b/scripts/snyk/remove-samples-workspace.mjs index b87b81e7ece..af945f20624 100644 --- a/scripts/snyk/remove-samples-workspace.mjs +++ b/scripts/snyk/remove-samples-workspace.mjs @@ -1,6 +1,6 @@ -import {readFileSync, writeFileSync} from 'fs'; -import {resolve} from 'path'; -import {cwd} from 'process'; +import {readFileSync, writeFileSync} from 'node:fs'; +import {resolve} from 'node:path'; +import {cwd} from 'node:process'; import {packageDirsSnyk} from '../packages.mjs'; const packageJsonPath = resolve(cwd(), 'package.json'); From 966f5472924d264a85c3e3b1423c792fa57c2e04 Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Fri, 25 Nov 2022 08:54:00 -0500 Subject: [PATCH 6/8] simplified logic to get package manifest and definition https://coveord.atlassian.net/browse/KIT-2114 --- scripts/deploy/invalidate-cloudfront.mjs | 15 +++---- scripts/deploy/manual-major-prerelease.mjs | 3 +- scripts/deploy/update-npm-tag.mjs | 9 +++-- scripts/notify-docs/published-ui-kit.mjs | 21 ++++------ scripts/packages.mjs | 46 ++++++++++++++++------ scripts/prerelease.mjs | 3 +- 6 files changed, 58 insertions(+), 39 deletions(-) diff --git a/scripts/deploy/invalidate-cloudfront.mjs b/scripts/deploy/invalidate-cloudfront.mjs index 97a3fd6569b..d110b2184a1 100644 --- a/scripts/deploy/invalidate-cloudfront.mjs +++ b/scripts/deploy/invalidate-cloudfront.mjs @@ -1,22 +1,23 @@ import {CloudFront} from 'aws-sdk'; import {resolve} from 'node:path'; -import {getPackageFromPath} from '../packages.mjs'; +import {getPackageDefinitionFromPackageDir} from '../packages.mjs'; const cloudfront = new CloudFront(); -async function getMajorVersion(dir) { - const {version} = getPackageFromPath( - resolve('..', '..', 'packages', dir, 'package.json') - ); +/** + * @param {import('../packages.mjs').PackageDir} dir + */ +function getMajorVersion(dir) { + const {version} = getPackageDefinitionFromPackageDir(resolve(dir)); return version.split('.')[0]; } async function main() { const pathsToInvalidate = [ '/atomic/latest/*', - `/atomic/v${await getMajorVersion('atomic')}/*`, + `/atomic/v${getMajorVersion('atomic')}/*`, '/headless/latest/*', - `/headless/v${await getMajorVersion('headless')}/*`, + `/headless/v${getMajorVersion('headless')}/*`, ]; const invalidationRequest = cloudfront.createInvalidation({ diff --git a/scripts/deploy/manual-major-prerelease.mjs b/scripts/deploy/manual-major-prerelease.mjs index 5d0f3b9be4e..2d7e2b0f3e5 100644 --- a/scripts/deploy/manual-major-prerelease.mjs +++ b/scripts/deploy/manual-major-prerelease.mjs @@ -4,6 +4,7 @@ import {execute} from '../exec.mjs'; import {commitVersionBump, stageAll, tagExists, tagPackages} from '../git.mjs'; import { getPackageDefinitionFromPackageName, + getPackagePathFromPackageDir, updatePackageVersion, } from '../packages.mjs'; import {prereleaseSuffix} from '../prerelease.mjs'; @@ -31,7 +32,7 @@ async function getNewMajorPrerelease(packageName, major, prerelease = 0) { */ async function getNewVersion(packageDef) { const version = getCurrentVersion( - resolve('.', 'packages', packageDef.packageDir) + getPackagePathFromPackageDir(packageDef.packageDir) ); return getNewMajorPrerelease(packageDef.name, version.major + 1); } diff --git a/scripts/deploy/update-npm-tag.mjs b/scripts/deploy/update-npm-tag.mjs index 3685a876bcc..abada3a22b2 100644 --- a/scripts/deploy/update-npm-tag.mjs +++ b/scripts/deploy/update-npm-tag.mjs @@ -1,13 +1,14 @@ import {resolve} from 'node:path'; import {promisify} from 'node:util'; import {execute} from '../exec.mjs'; -import {packageDirsNpmTag, getPackageFromPath} from '../packages.mjs'; +import { + packageDirsNpmTag, + getPackageDefinitionFromPackageDir, +} from '../packages.mjs'; async function main() { const requests = packageDirsNpmTag - .map((dir) => - getPackageFromPath(resolve('..', '..', 'packages', dir, 'package.json')) - ) + .map((dir) => getPackageDefinitionFromPackageDir(dir)) .map(({name, version}) => updateNpmTag(name, version)); await Promise.all(requests); diff --git a/scripts/notify-docs/published-ui-kit.mjs b/scripts/notify-docs/published-ui-kit.mjs index f686a1ab76c..fa3cefdb84e 100644 --- a/scripts/notify-docs/published-ui-kit.mjs +++ b/scripts/notify-docs/published-ui-kit.mjs @@ -1,16 +1,6 @@ import {Octokit} from '@octokit/rest'; import {resolve} from 'node:path'; -import {getPackageFromPath} from '../packages.mjs'; - -const headlessPackageJson = getPackageFromPath( - resolve('..', '..', 'packages', 'headless', 'package.json') -); -const atomicPackageJson = getPackageFromPath( - resolve('..', '..', 'packages', 'atomic', 'package.json') -); -const quanticPackageJson = getPackageFromPath( - resolve('..', '..', 'packages', 'quantic', 'package.json') -); +import {getPackageDefinitionFromPackageDir} from '../packages.mjs'; const token = process.env.GITHUB_TOKEN || ''; const github = new Octokit({auth: token}); @@ -20,9 +10,12 @@ const repo = 'doc_jekyll-public-site'; const event_type = 'published_ui-kit_to_npm'; async function notify() { - const headless_version = headlessPackageJson.version; - const atomic_version = atomicPackageJson.version; - const quantic_version = quanticPackageJson.version; + const {version: headless_version} = + getPackageDefinitionFromPackageDir('headless'); + const {version: atomic_version} = + getPackageDefinitionFromPackageDir('atomic'); + const {version: quantic_version} = + getPackageDefinitionFromPackageDir('quantic'); const client_payload = {headless_version, atomic_version, quantic_version}; return github.repos.createDispatchEvent({ diff --git a/scripts/packages.mjs b/scripts/packages.mjs index 21a80c934d3..7c2d9d1d4e5 100644 --- a/scripts/packages.mjs +++ b/scripts/packages.mjs @@ -1,8 +1,9 @@ import detectIndent from 'detect-indent'; import {existsSync, writeFileSync, readFileSync} from 'node:fs'; import {resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; -export const packageDirsNpmTag = [ +export const packageDirsNpmTag = /** @type {const} */ ([ 'atomic', 'auth', 'bueno', @@ -10,35 +11,53 @@ export const packageDirsNpmTag = [ 'atomic-react', 'atomic-angular/projects/atomic-angular', 'quantic', -]; +]); +/** + * @typedef {(typeof packageDirsNpmTag)[number]} PackageDir + */ + +/** @type {PackageDir[]} */ export const packageDirsSnyk = ['headless', 'atomic']; +export const workspacesRoot = resolve( + fileURLToPath(import.meta.url), + '..', + '..' +); + /** * @typedef PackageDefinition * @property {string} name * @property {string} version - * @property {string} packageDir Relative to `/packages` (e.g., `atomic-angular/projects/atomic-angular`). + * @property {PackageDir} packageDir */ +/** + * @param {PackageDir} packageDir + */ +export function getPackagePathFromPackageDir(packageDir) { + return resolve(workspacesRoot, 'packages', packageDir); +} + /** * @param {string} fullPath * @returns {import('@lerna/package').RawManifest} */ -export function getPackageFromPath(fullPath) { - return JSON.parse(readFileSync(fullPath).toString()); +function getPackageManifestFromPackagePath(fullPath) { + return resolve(JSON.parse(readFileSync(fullPath).toString()), 'package.json'); } /** - * @param {string} packageDir E.g.: `samples/some-package` + * @param {PackageDir} packageDir E.g.: `samples/some-package` * @returns {PackageDefinition} */ export function getPackageDefinitionFromPackageDir(packageDir) { - const fullPath = resolve('.', 'packages', packageDir, 'package.json'); + const fullPath = getPackagePathFromPackageDir(packageDir); if (!existsSync(fullPath)) { throw `Could not find package at ${fullPath}.`; } - const {name, version} = getPackageFromPath(fullPath); + const {name, version} = getPackageManifestFromPackagePath(fullPath); return {name, version, packageDir}; } @@ -54,7 +73,7 @@ export function getPackageDefinitionFromPackageName(name) { /** * @param {string} packageName * @param {string} newVersion - * @param {string[]} depdendenciesPackageDirs E.g.: [`samples/some-package`] + * @param {PackageDir[]} depdendenciesPackageDirs E.g.: [`samples/some-package`] */ export function updatePackageVersion( packageName, @@ -62,8 +81,11 @@ export function updatePackageVersion( depdendenciesPackageDirs ) { depdendenciesPackageDirs.forEach((packageDir) => { - const fullPath = resolve('.', 'packages', packageDir, 'package.json'); - const originalContentAsText = readFileSync(fullPath).toString(); + const manifestPath = resolve( + getPackagePathFromPackageDir(packageDir), + 'package.json' + ); + const originalContentAsText = readFileSync(manifestPath).toString(); const {indent} = detectIndent(originalContentAsText); /** @type {import('@lerna/package').RawManifest} */ const manifest = JSON.parse(originalContentAsText); @@ -82,7 +104,7 @@ export function updatePackageVersion( } } writeFileSync( - fullPath, + manifestPath, JSON.stringify(manifest, undefined, indent || ' ') ); }); diff --git a/scripts/prerelease.mjs b/scripts/prerelease.mjs index f1d2fda4083..35adb3dcbc2 100644 --- a/scripts/prerelease.mjs +++ b/scripts/prerelease.mjs @@ -11,6 +11,7 @@ import { packageDirsNpmTag, getPackageDefinitionFromPackageDir, updatePackageVersion, + getPackagePathFromPackageDir, } from './packages.mjs'; /** @@ -37,7 +38,7 @@ function getPackagesToPrereleaseBump() { */ async function getNewVersion(packageDef) { const version = getCurrentVersion( - resolve('.', 'packages', packageDef.packageDir) + getPackagePathFromPackageDir(packageDef.packageDir) ); return getNextVersion(version, { type: 'prerelease', From f339d7232abe7bda0022fe936a495c5f4e1aea46 Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Fri, 25 Nov 2022 09:17:37 -0500 Subject: [PATCH 7/8] use semver instead of regexp https://coveord.atlassian.net/browse/KIT-2114 --- scripts/prerelease.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/prerelease.mjs b/scripts/prerelease.mjs index 35adb3dcbc2..85bc4887855 100644 --- a/scripts/prerelease.mjs +++ b/scripts/prerelease.mjs @@ -5,6 +5,7 @@ import { gitPushTags, } from '@coveo/semantic-monorepo-tools'; import {resolve} from 'node:path'; +import semver from 'semver'; import {execute} from './exec.mjs'; import {commitVersionBump, stageAll, tagPackages} from './git.mjs'; import { @@ -24,7 +25,7 @@ export const prereleaseSuffix = 'pre'; * @param {string} version */ export function isPrereleaseVersion(version) { - return !!version.match(new RegExp(`-${prereleaseSuffix}\\.[0-9]+\$`)); + return !!semver.prerelease(version); } function getPackagesToPrereleaseBump() { From 9938c7e423281bde16e679bf919568226715df69 Mon Sep 17 00:00:00 2001 From: Benjamin Taillon Date: Fri, 25 Nov 2022 09:30:29 -0500 Subject: [PATCH 8/8] replaced stageAll with gitAdd https://coveord.atlassian.net/browse/KIT-2114 --- scripts/deploy/manual-major-prerelease.mjs | 6 +++--- scripts/git.mjs | 4 ---- scripts/prerelease.mjs | 5 +++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/scripts/deploy/manual-major-prerelease.mjs b/scripts/deploy/manual-major-prerelease.mjs index 2d7e2b0f3e5..77ebf91443e 100644 --- a/scripts/deploy/manual-major-prerelease.mjs +++ b/scripts/deploy/manual-major-prerelease.mjs @@ -1,7 +1,7 @@ -import {getCurrentVersion} from '@coveo/semantic-monorepo-tools'; +import {getCurrentVersion, gitAdd} from '@coveo/semantic-monorepo-tools'; import {resolve} from 'node:path'; import {execute} from '../exec.mjs'; -import {commitVersionBump, stageAll, tagExists, tagPackages} from '../git.mjs'; +import {commitVersionBump, tagExists, tagPackages} from '../git.mjs'; import { getPackageDefinitionFromPackageName, getPackagePathFromPackageDir, @@ -66,7 +66,7 @@ export async function main(args) { const updatedPackages = packageNamesToBump.map( getPackageDefinitionFromPackageName ); - await stageAll(); + await gitAdd('.'); await commitVersionBump(updatedPackages); await tagPackages(updatedPackages); } diff --git a/scripts/git.mjs b/scripts/git.mjs index ec7a24902ce..c1f2c2c6294 100644 --- a/scripts/git.mjs +++ b/scripts/git.mjs @@ -32,10 +32,6 @@ export async function tagExists(tag) { return !!(await execute('git', ['tag', '-l', tag])); } -export async function stageAll() { - await execute('git', ['add', '.']); -} - /** * @param {PackageDefinition[]} updatedPackages */ diff --git a/scripts/prerelease.mjs b/scripts/prerelease.mjs index 85bc4887855..c8fd553b7f1 100644 --- a/scripts/prerelease.mjs +++ b/scripts/prerelease.mjs @@ -3,11 +3,12 @@ import { getCurrentVersion, gitPush, gitPushTags, + gitAdd, } from '@coveo/semantic-monorepo-tools'; import {resolve} from 'node:path'; import semver from 'semver'; import {execute} from './exec.mjs'; -import {commitVersionBump, stageAll, tagPackages} from './git.mjs'; +import {commitVersionBump, tagPackages} from './git.mjs'; import { packageDirsNpmTag, getPackageDefinitionFromPackageDir, @@ -69,7 +70,7 @@ export async function bumpPrereleaseVersionAndPush() { const updatedPackages = packages.map(({packageDir}) => getPackageDefinitionFromPackageDir(packageDir) ); - await stageAll(); + await gitAdd('.'); await commitVersionBump(updatedPackages); await tagPackages(updatedPackages); await gitPush();