diff --git a/.eslintrc.js b/.eslintrc.js index 8cd6fd26fa..672e740d6e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,6 +22,7 @@ module.exports = { { files: [ "**/*.js", + "**/*.mjs", ], extends: [ "eslint:recommended", diff --git a/scripts/clear-release-pr.mjs b/scripts/clear-release-pr.mjs new file mode 100755 index 0000000000..954501c788 --- /dev/null +++ b/scripts/clear-release-pr.mjs @@ -0,0 +1,210 @@ +#!/usr/bin/env node +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +// This script creates a release PR +import { execSync, exec, spawn } from "child_process"; +import commandLineArgs from "command-line-args"; +import fse from "fs-extra"; +import { basename } from "path"; +import semver from "semver"; +import { promisify } from "util"; + +const { + SemVer, + valid: semverValid, + rcompare: semverRcompare, + lte: semverLte, +} = semver; +const { readJsonSync } = fse; +const execP = promisify(exec); + +const options = commandLineArgs([ + { + name: "type", + defaultOption: true, + }, + { + name: "preid", + }, +]); + +const validReleaseValues = [ + "major", + "minor", + "patch", +]; +const validPrereleaseValues = [ + "premajor", + "preminor", + "prepatch", + "prerelease", +]; +const validPreidValues = [ + "alpha", + "beta", +]; + +const errorMessages = { + noReleaseType: `No release type provided. Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`, + invalidRelease: (invalid) => `Invalid release type was provided (value was "${invalid}"). Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`, + noPreid: `No preid was provided. Use '--preid' to specify. Valid options are: ${validPreidValues.join(", ")}`, + invalidPreid: (invalid) => `Invalid preid was provided (value was "${invalid}"). Valid options are: ${validPreidValues.join(", ")}`, + wrongCwd: "It looks like you are running this script from the 'scripts' directory. This script assumes it is run from the root of the git repo", +}; + +if (!options.type) { + console.error(errorMessages.noReleaseType); + process.exit(1); +} + +if (validReleaseValues.includes(options.type)) { + // do nothing, is valid +} else if (validPrereleaseValues.includes(options.type)) { + if (!options.preid) { + console.error(errorMessages.noPreid); + process.exit(1); + } + + if (!validPreidValues.includes(options.preid)) { + console.error(errorMessages.invalidPreid(options.preid)); + process.exit(1); + } +} else { + console.error(errorMessages.invalidRelease(options.type)); + process.exit(1); +} + +if (basename(process.cwd()) === "scripts") { + console.error(errorMessages.wrongCwd); +} + + +const currentVersion = new SemVer(readJsonSync("./package.json").version); +const currentVersionMilestone = `${currentVersion.major}.${currentVersion.minor}.${currentVersion.patch}`; + +console.log(`current version: ${currentVersion.format()}`); +console.log("fetching tags..."); +execSync("git fetch --tags --force"); + +const actualTags = execSync("git tag --list", { encoding: "utf-8" }).split(/\r?\n/).map(line => line.trim()); +const [previousReleasedVersion] = actualTags + .map(semverValid) + .filter(Boolean) + .sort(semverRcompare) + .filter(version => semverLte(version, currentVersion)); + +const npmVersionArgs = [ + "npm", + "version", + options.type, +]; + +if (options.preid) { + npmVersionArgs.push(`--preid=${options.preid}`); +} + +npmVersionArgs.push("--git-tag-version false"); + +execSync(npmVersionArgs.join(" "), { stdio: "ignore" }); + +const newVersion = new SemVer(readJsonSync("./package.json").version); + +const getMergedPrsArgs = [ + "gh", + "pr", + "list", + "--limit=500", // Should be big enough, if not we need to release more often ;) + "--state=merged", + "--base=master", + "--json mergeCommit,title,author,labels,number,milestone", +]; + +console.log("retreiving last 500 PRs to create release PR body..."); +const mergedPrs = JSON.parse(execSync(getMergedPrsArgs.join(" "), { encoding: "utf-8" })); +const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone && pr.milestone.title === currentVersionMilestone); +const relaventPrsQuery = await Promise.all( + milestoneRelevantPrs.map(async pr => ({ + pr, + stdout: (await execP(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout, + })) +) +const relaventPrs = relaventPrsQuery + .filter(query => query.stdout) + .map(query => query.pr); + +const enhancementPrLabelName = "enhancement"; +const bugfixPrLabelName = "bug"; + +const enhancementPrs = relaventPrs.filter(pr => pr.labels.some(label => label.name === enhancementPrLabelName)); +const bugfixPrs = relaventPrs.filter(pr => pr.labels.some(label => label.name === bugfixPrLabelName)); +const maintenencePrs = relaventPrs.filter(pr => pr.labels.every(label => label.name !== bugfixPrLabelName && label.name !== enhancementPrLabelName)); + +console.log("Found:"); +console.log(`${enhancementPrs.length} enhancement PRs`); +console.log(`${bugfixPrs.length} bug fix PRs`); +console.log(`${maintenencePrs.length} maintenence PRs`); + +const prBodyLines = [ + `## Changes since ${previousReleasedVersion}`, + "", +]; + +if (enhancementPrs.length > 0) { + prBodyLines.push( + "## 🚀 Features", + "", + ...enhancementPrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`), + "", + ) +} + +if (bugfixPrs.length > 0) { + prBodyLines.push( + "## 🐛 Bug Fixes", + "", + ...bugfixPrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`), + "", + ) +} + +if (maintenencePrs.length > 0) { + prBodyLines.push( + "## 🧰 Maintenance", + "", + ...maintenencePrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`), + "", + ) +} + +const prBody = prBodyLines.join("\n"); +const prBase = newVersion.patch === 0 + ? "master" + : `release/v${newVersion.major}.${newVersion.minor}`; +const createPrArgs = [ + "pr", + "create", + "--base", prBase, + "--title", `release ${newVersion.format()}`, + "--label", "skip-changelog", + "--body-file", "-" +]; + +const createPrProcess = spawn("gh", createPrArgs, { stdio: "pipe" }) +let result = ""; + +createPrProcess.stdout.on("data", (chunk) => result += chunk); + +createPrProcess.stdin.write(prBody) +createPrProcess.stdin.end(); + +await new Promise((resolve) => { + createPrProcess.on('close', () => { + createPrProcess.stdout.removeAllListeners(); + resolve(); + }) +}) + +console.log(result); diff --git a/yarn.lock b/yarn.lock index 52e0391c41..b9ab87db8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1426,6 +1426,11 @@ dependencies: "@types/color-convert" "*" +"@types/command-line-args@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.2.0.tgz#adbb77980a1cc376bb208e3f4142e907410430f6" + integrity sha512-UuKzKpJJ/Ief6ufIaIzr3A/0XnluX7RvFgwkV89Yzvm77wCh1kFaFmqN8XEnGcN62EuHdedQjEMb8mYxFLGPyA== + "@types/connect-history-api-fallback@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" @@ -2818,6 +2823,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3909,6 +3919,16 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +command-line-args@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + commander@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" @@ -4424,7 +4444,7 @@ debug@^3.1.0, debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -5913,6 +5933,13 @@ find-npm-prefix@^1.0.2: resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf" integrity sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA== +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -6992,7 +7019,7 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -8694,11 +8721,6 @@ lodash-es@^4.17.21: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -8707,33 +8729,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - lodash._root@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" @@ -8764,11 +8764,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - lodash.union@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" @@ -9752,7 +9747,6 @@ npm@^6.14.17: cmd-shim "^3.0.3" columnify "~1.5.4" config-chain "^1.1.12" - debuglog "*" detect-indent "~5.0.0" detect-newline "^2.1.0" dezalgo "~1.0.3" @@ -9767,7 +9761,6 @@ npm@^6.14.17: has-unicode "~2.0.1" hosted-git-info "^2.8.9" iferr "^1.0.2" - imurmurhash "*" infer-owner "^1.0.4" inflight "~1.0.6" inherits "^2.0.4" @@ -9786,14 +9779,8 @@ npm@^6.14.17: libnpx "^10.2.4" lock-verify "^2.1.0" lockfile "^1.0.4" - lodash._baseindexof "*" lodash._baseuniq "~4.6.0" - lodash._bindcallback "*" - lodash._cacheindexof "*" - lodash._createcache "*" - lodash._getnative "*" lodash.clonedeep "~4.5.0" - lodash.restparam "*" lodash.union "~4.6.0" lodash.uniq "~4.5.0" lodash.without "~4.4.0" @@ -13197,6 +13184,11 @@ typescript@^4.5.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + uglify-js@^3.1.4: version "3.9.4" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.4.tgz#867402377e043c1fc7b102253a22b64e5862401b"