Refactor/Fix release script (#13619)
This commit is contained in:
1 parent
8df3b5ee60
commit
76d0890142
23 files changed
+275
-188
No files matched your search
@@ -13,3 +13,5 @@
|
|||||||
/website/static/playground.js
|
/website/static/playground.js
|
||||||
/website/static/lib/
|
/website/static/lib/
|
||||||
/scripts/benchmark/*/
|
/scripts/benchmark/*/
|
||||||
|
**/.yarn/**
|
||||||
|
**/.pnp.*
|
||||||
+1
-1
@@ -25,7 +25,7 @@ package-lock.json
|
|||||||
!.yarn/plugins
|
!.yarn/plugins
|
||||||
!.yarn/sdks
|
!.yarn/sdks
|
||||||
!.yarn/versions
|
!.yarn/versions
|
||||||
.pnp.*
|
**/.pnp.*
|
||||||
.nyc_output
|
.nyc_output
|
||||||
.devcontainer
|
.devcontainer
|
||||||
# When installing software on gitpod.io, `core.*` files are generated
|
# When installing software on gitpod.io, `core.*` files are generated
|
||||||
|
|||||||
+2
-1
@@ -2,7 +2,8 @@
|
|||||||
.cache/
|
.cache/
|
||||||
coverage/
|
coverage/
|
||||||
.nyc_output/
|
.nyc_output/
|
||||||
.yarn/
|
**/.yarn/**
|
||||||
|
**/.pnp.*
|
||||||
/dist*/
|
/dist*/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
"author": "James Long",
|
"author": "James Long",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./index.cjs",
|
"main": "./src/index.cjs",
|
||||||
"browser": "./standalone.js",
|
"browser": "./standalone.js",
|
||||||
"unpkg": "./standalone.js",
|
"unpkg": "./standalone.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ node ./scripts/release/release.js --version NEW_VERSION
|
|||||||
|
|
||||||
The script has its own `package.json` so we can reinstall the root's `node_modules/` while making the release.
|
The script has its own `package.json` so we can reinstall the root's `node_modules/` while making the release.
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
|
||||||
|
- `--manual` Manual run release process instead of publish from GitHub actions
|
||||||
|
- `--dry` Dry run
|
||||||
|
- `--skip-dependencies-install` Skip dependencies installation
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
This script was inspired by [React's release script](https://github.com/facebook/react/tree/001f9ef/scripts/release).
|
This script was inspired by [React's release script](https://github.com/facebook/react/tree/001f9ef/scripts/release).
|
||||||
@@ -26,5 +26,5 @@ await runCommand("git fetch --tags");
|
|||||||
// Install script's dependencies before any require
|
// Install script's dependencies before any require
|
||||||
await runCommand("yarn");
|
await runCommand("yarn");
|
||||||
|
|
||||||
const { default: run } = await import("./run.js");
|
// Ready to run
|
||||||
await run();
|
await import("./run.js");
|
||||||
+62
-47
@@ -1,61 +1,76 @@
|
|||||||
import chalk from "chalk";
|
|
||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
import { outdent } from "outdent";
|
|
||||||
import { runGit, readJson } from "./utils.js";
|
import { runGit, readJson } from "./utils.js";
|
||||||
import parseArguments from "./parse-arguments.js";
|
import parseArguments from "./parse-arguments.js";
|
||||||
|
import { logPromise } from "./utils.js";
|
||||||
|
import * as steps from "./steps/index.js";
|
||||||
|
|
||||||
const importDefault = async (module) => (await import(module)).default;
|
const params = parseArguments();
|
||||||
|
const { stdout: previousVersion } = await runGit([
|
||||||
async function run() {
|
|
||||||
const params = parseArguments();
|
|
||||||
|
|
||||||
const { stdout: previousVersion } = await runGit([
|
|
||||||
"describe",
|
"describe",
|
||||||
"--tags",
|
"--tags",
|
||||||
"--abbrev=0",
|
"--abbrev=0",
|
||||||
]);
|
]);
|
||||||
|
if (semver.parse(previousVersion) === null) {
|
||||||
if (semver.parse(previousVersion) === null) {
|
|
||||||
throw new Error(`Unexpected previousVersion: ${previousVersion}`);
|
throw new Error(`Unexpected previousVersion: ${previousVersion}`);
|
||||||
} else {
|
} else {
|
||||||
params.previousVersion = previousVersion;
|
params.previousVersion = previousVersion;
|
||||||
params.previousVersionOnDefaultBranch = (
|
params.previousVersionOnDefaultBranch = (
|
||||||
await readJson("package.json")
|
await readJson("package.json")
|
||||||
).version;
|
).version;
|
||||||
}
|
|
||||||
|
|
||||||
const steps = await Promise.all(
|
|
||||||
[
|
|
||||||
"./steps/validate-new-version.js",
|
|
||||||
"./steps/check-git-status.js",
|
|
||||||
!params.skipDependenciesInstall && "./steps/install-dependencies.js",
|
|
||||||
params.manual && "./steps/run-tests.js",
|
|
||||||
"./steps/update-version.js",
|
|
||||||
params.manual && "./steps/generate-bundles.js",
|
|
||||||
"./steps/update-changelog.js",
|
|
||||||
"./steps/push-to-git.js",
|
|
||||||
params.manual
|
|
||||||
? "./steps/publish-to-npm.js"
|
|
||||||
: "./steps/wait-for-bot-release.js",
|
|
||||||
"./steps/show-instructions-after-npm-publish.js",
|
|
||||||
"./steps/update-dependents-count.js",
|
|
||||||
"./steps/bump-prettier.js",
|
|
||||||
"./steps/post-publish-steps.js",
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.map((step) => importDefault(step))
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (const step of steps) {
|
|
||||||
await step(params);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
const message = outdent.string(error.message.trim());
|
|
||||||
const stack = error.stack.replace(message, "");
|
|
||||||
console.error(`${chalk.red("error")} ${message}\n${stack}`);
|
|
||||||
process.exitCode = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default run;
|
for (let step of [
|
||||||
|
{
|
||||||
|
name: "Validating new version",
|
||||||
|
process: steps.validateNewVersion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Checking git status",
|
||||||
|
process: steps.checkGitStatus,
|
||||||
|
skip: params.dry,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Installing NPM dependencies",
|
||||||
|
process: steps.installDependencies,
|
||||||
|
skip: params.dry || params.skipDependenciesInstall || !params.manual,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Linting files",
|
||||||
|
process: steps.lintFiles,
|
||||||
|
skip: params.dry,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Running tests",
|
||||||
|
process: steps.runTests,
|
||||||
|
skip: params.dry || !params.manual,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Bumping version",
|
||||||
|
process: steps.updateVersion,
|
||||||
|
skip: params.dry,
|
||||||
|
},
|
||||||
|
steps.generateBundles,
|
||||||
|
steps.updateChangelog,
|
||||||
|
{
|
||||||
|
name: "Committing and pushing to remote",
|
||||||
|
process: steps.pushToGit,
|
||||||
|
skip: params.dry,
|
||||||
|
},
|
||||||
|
params.manual ? steps.publishToNpm : steps.waitForBotRelease,
|
||||||
|
steps.showInstructionsAfterNpmPublish,
|
||||||
|
steps.updateDependentsCount,
|
||||||
|
steps.bumpPrettier,
|
||||||
|
steps.postPublishSteps,
|
||||||
|
]) {
|
||||||
|
if (typeof step === "function") {
|
||||||
|
step = { process: step };
|
||||||
|
}
|
||||||
|
|
||||||
|
const runStep = () => step.process(params);
|
||||||
|
|
||||||
|
if (step.name) {
|
||||||
|
await logPromise(step.name, runStep, step.skip);
|
||||||
|
} else if (!step.skip) {
|
||||||
|
await runStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,28 @@
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { runYarn, logPromise, readJson } from "../utils.js";
|
import { runYarn, logPromise, readJson } from "../utils.js";
|
||||||
|
|
||||||
export default async function generateBundles({ version }) {
|
export default async function generateBundles({ dry, version, manual }) {
|
||||||
|
if (!manual) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await logPromise(
|
await logPromise(
|
||||||
"Generating bundles",
|
"Generating bundles",
|
||||||
runYarn(["build", "--clean", "--print-size", "--compare-size"])
|
runYarn(["build", "--clean", "--print-size", "--compare-size"])
|
||||||
);
|
);
|
||||||
|
|
||||||
const builtPkg = await readJson("dist/package.json");
|
const builtPkg = await readJson("dist/package.json");
|
||||||
if (builtPkg.version !== version) {
|
if (!dry && builtPkg.version !== version) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Expected ${version} in dist/package.json but found ${builtPkg.version}`
|
`Expected ${version} in dist/package.json but found ${builtPkg.version}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await logPromise("Running tests on generated bundles", runYarn("test:dist"));
|
await logPromise(
|
||||||
|
"Running tests on generated bundles",
|
||||||
|
() => runYarn("test:dist"),
|
||||||
|
/* shouldSkip */ dry
|
||||||
|
);
|
||||||
|
|
||||||
console.log(chalk.green.bold("Build successful!\n"));
|
console.log(chalk.green.bold("Build successful!\n"));
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
export { default as validateNewVersion } from "./validate-new-version.js";
|
||||||
|
export { default as checkGitStatus } from "./check-git-status.js";
|
||||||
|
export { default as installDependencies } from "./install-dependencies.js";
|
||||||
|
export { default as lintFiles } from "./lint-files.js";
|
||||||
|
export { default as runTests } from "./run-tests.js";
|
||||||
|
export { default as updateVersion } from "./update-version.js";
|
||||||
|
export { default as generateBundles } from "./generate-bundles.js";
|
||||||
|
export { default as updateChangelog } from "./update-changelog.js";
|
||||||
|
export { default as pushToGit } from "./push-to-git.js";
|
||||||
|
export { default as publishToNpm } from "./publish-to-npm.js";
|
||||||
|
export { default as waitForBotRelease } from "./wait-for-bot-release.js";
|
||||||
|
export { default as showInstructionsAfterNpmPublish } from "./show-instructions-after-npm-publish.js";
|
||||||
|
export { default as updateDependentsCount } from "./update-dependents-count.js";
|
||||||
|
export { default as bumpPrettier } from "./bump-prettier.js";
|
||||||
|
export { default as postPublishSteps } from "./post-publish-steps.js";
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { execa } from "execa";
|
import { execa } from "execa";
|
||||||
import { runYarn, runGit, logPromise } from "../utils.js";
|
import { runYarn, runGit } from "../utils.js";
|
||||||
|
|
||||||
async function install() {
|
export default async function installDependencies() {
|
||||||
await execa("rm", ["-rf", "node_modules"]);
|
await execa("rm", ["-rf", "node_modules"]);
|
||||||
await runYarn(["install"]);
|
await runYarn(["install"]);
|
||||||
|
|
||||||
@@ -15,7 +15,3 @@ async function install() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function installDependencies() {
|
|
||||||
return logPromise("Installing NPM dependencies", install());
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { runYarn } from "../utils.js";
|
||||||
|
|
||||||
|
export default () => runYarn("lint");
|
||||||
@@ -3,8 +3,6 @@ import outdent from "outdent";
|
|||||||
import { execa } from "execa";
|
import { execa } from "execa";
|
||||||
import { fetchText, logPromise } from "../utils.js";
|
import { fetchText, logPromise } from "../utils.js";
|
||||||
|
|
||||||
const outdentString = outdent.string;
|
|
||||||
|
|
||||||
const SCHEMA_REPO = "SchemaStore/schemastore";
|
const SCHEMA_REPO = "SchemaStore/schemastore";
|
||||||
const SCHEMA_PATH = "src/schemas/json/prettierrc.json";
|
const SCHEMA_PATH = "src/schemas/json/prettierrc.json";
|
||||||
const RAW_URL = `https://raw.githubusercontent.com/${SCHEMA_REPO}/master/${SCHEMA_PATH}`;
|
const RAW_URL = `https://raw.githubusercontent.com/${SCHEMA_REPO}/master/${SCHEMA_PATH}`;
|
||||||
@@ -25,40 +23,46 @@ async function checkSchema() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return outdentString(chalk`
|
return outdent`
|
||||||
{bold.underline The schema in {yellow SchemaStore} needs an update.}
|
${chalk.bold.underline(
|
||||||
- Open {cyan.underline ${EDIT_URL}}
|
"The schema in {yellow SchemaStore"
|
||||||
- Run {yellow node scripts/generate-schema.mjs} and copy the new schema
|
)} needs an update.}
|
||||||
|
- Open ${chalk.cyan.underline(EDIT_URL)}
|
||||||
|
- Run ${chalk.yellow(
|
||||||
|
"node scripts/generate-schema.mjs"
|
||||||
|
)} and copy the new schema
|
||||||
- Paste it on GitHub interface
|
- Paste it on GitHub interface
|
||||||
- Open a PR
|
- Open a PR
|
||||||
`);
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function twitterAnnouncement() {
|
function twitterAnnouncement() {
|
||||||
return outdentString(chalk`
|
return outdent`
|
||||||
{bold.underline Announce on Twitter}
|
${chalk.bold.underline("Announce on Twitter")}
|
||||||
- Open {cyan.underline https://tweetdeck.twitter.com}
|
- Open ${chalk.cyan.underline("https://tweetdeck.twitter.com")}
|
||||||
- Make sure you are tweeting from the {yellow @PrettierCode} account.
|
- Make sure you are tweeting from the {yellow @PrettierCode} account.
|
||||||
- Tweet about the release, including the blog post URL.
|
- Tweet about the release, including the blog post URL.
|
||||||
`);
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function postPublishSteps() {
|
export default async function postPublishSteps({ dry }) {
|
||||||
const steps = [await checkSchema(), twitterAnnouncement()].filter(Boolean);
|
|
||||||
|
|
||||||
console.log(chalk.bold.green("The script has finished!\n"));
|
console.log(chalk.bold.green("The script has finished!\n"));
|
||||||
|
|
||||||
if (steps.length === 0) {
|
if (dry) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const steps = [await checkSchema(), twitterAnnouncement()].filter(Boolean);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
outdentString(chalk`
|
outdent`
|
||||||
{yellow.bold The following ${
|
${chalk.yellow.bold(
|
||||||
|
`The following ${
|
||||||
steps.length === 1 ? "step is" : "steps are"
|
steps.length === 1 ? "step is" : "steps are"
|
||||||
} optional.}
|
} optional.`
|
||||||
|
)}
|
||||||
|
|
||||||
${steps.join("\n\n")}
|
${steps.join("\n\n")}
|
||||||
`)
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,35 @@
|
|||||||
import { execa } from "execa";
|
import { execa } from "execa";
|
||||||
import enquirer from "enquirer";
|
import enquirer from "enquirer";
|
||||||
import { logPromise } from "../utils.js";
|
import { waitForEnter } from "../utils.js";
|
||||||
|
|
||||||
|
export default async function publishToNpm({ dry }) {
|
||||||
|
console.log(`Ready to publish to NPM${dry ? "(--dry-run)" : ""}`);
|
||||||
|
|
||||||
|
await waitForEnter();
|
||||||
|
|
||||||
|
const commonArgs = ["publish"];
|
||||||
|
if (dry) {
|
||||||
|
commonArgs.push("--dry-run");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retry "npm publish" when to enter OTP is failed.
|
|
||||||
*/
|
|
||||||
async function retryNpmPublish() {
|
|
||||||
const runNpmPublish = async () => {
|
const runNpmPublish = async () => {
|
||||||
|
const args = [...commonArgs];
|
||||||
|
|
||||||
|
if (!dry) {
|
||||||
const { otp } = await enquirer.prompt({
|
const { otp } = await enquirer.prompt({
|
||||||
type: "input",
|
type: "input",
|
||||||
name: "otp",
|
name: "otp",
|
||||||
message: "Please enter your npm OTP",
|
message: "Please enter your npm OTP",
|
||||||
});
|
});
|
||||||
await execa("npm", ["publish", "--otp", otp], { cwd: "./dist" });
|
args.push("--otp", otp);
|
||||||
|
}
|
||||||
|
|
||||||
|
await execa("npm", args, { cwd: "./dist" });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retry "npm publish" when to enter OTP is failed.
|
||||||
|
*/
|
||||||
for (let i = 5; i > 0; i--) {
|
for (let i = 5; i > 0; i--) {
|
||||||
try {
|
try {
|
||||||
return await runNpmPublish();
|
return await runNpmPublish();
|
||||||
@@ -26,11 +42,3 @@ async function retryNpmPublish() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function publishToNpm({ dry }) {
|
|
||||||
if (dry) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await logPromise("Publishing to npm", retryNpmPublish());
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,8 @@
|
|||||||
import { runGit, logPromise } from "../utils.js";
|
import { runGit } from "../utils.js";
|
||||||
|
|
||||||
async function pushGit({ version, repo }) {
|
export default async function pushToGit({ version, repo }) {
|
||||||
await runGit(["commit", "-am", `Release ${version}`]);
|
await runGit(["commit", "-am", `Release ${version}`]);
|
||||||
await runGit(["tag", "-a", version, "-m", `Release ${version}`]);
|
await runGit(["tag", "-a", version, "-m", `Release ${version}`]);
|
||||||
await runGit(["push", "--repo", repo]);
|
await runGit(["push", "--repo", repo]);
|
||||||
await runGit(["push", "--tags", "--repo", repo]);
|
await runGit(["push", "--tags", "--repo", repo]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function pushToGit(params) {
|
|
||||||
if (params.dry) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return logPromise("Committing and pushing to remote", pushGit(params));
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
import { runYarn, logPromise } from "../utils.js";
|
import { runYarn } from "../utils.js";
|
||||||
|
|
||||||
export default async function runTests() {
|
export default () => runYarn("test");
|
||||||
await logPromise("Running linter", runYarn("lint:eslint"));
|
|
||||||
await logPromise("Running Prettier on docs", runYarn("lint:prettier"));
|
|
||||||
await logPromise("Running tests", runYarn("test"));
|
|
||||||
}
|
|
||||||
@@ -7,8 +7,6 @@ import {
|
|||||||
waitForEnter,
|
waitForEnter,
|
||||||
} from "../utils.js";
|
} from "../utils.js";
|
||||||
|
|
||||||
const outdentString = outdent.string;
|
|
||||||
|
|
||||||
const RELEASE_URL_BASE = "https://github.com/prettier/prettier/releases/new?";
|
const RELEASE_URL_BASE = "https://github.com/prettier/prettier/releases/new?";
|
||||||
export function getReleaseUrl(version, previousVersion) {
|
export function getReleaseUrl(version, previousVersion) {
|
||||||
const semverDiff = semver.diff(version, previousVersion);
|
const semverDiff = semver.diff(version, previousVersion);
|
||||||
@@ -42,22 +40,24 @@ export default async function showInstructionsAfterNpmPublish({
|
|||||||
const releaseUrl = getReleaseUrl(version, previousVersion);
|
const releaseUrl = getReleaseUrl(version, previousVersion);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
outdentString(chalk`
|
outdent`
|
||||||
{green.bold Prettier ${version} published!}
|
${chalk.green.bold(`Prettier ${version} published!`)}
|
||||||
|
|
||||||
{yellow.bold Some manual steps are necessary.}
|
${chalk.yellow.bold("Some manual steps are necessary.")}
|
||||||
|
|
||||||
{bold.underline Create a GitHub Release}
|
${chalk.bold.underline("Create a GitHub Release")}
|
||||||
- Go to {cyan.underline ${releaseUrl}}
|
- Go to ${chalk.cyan.underline(releaseUrl)}
|
||||||
- Press {bgGreen.black Publish release }
|
- Press ${chalk.bgGreen.black("Publish release ")}
|
||||||
|
|
||||||
{bold.underline Test the new release}
|
${chalk.bold.underline("Test the new release")}
|
||||||
- In a new session, run {yellow npm i prettier@latest} in another directory
|
- In a new session, run ${chalk.yellow(
|
||||||
|
"npm i prettier@latest"
|
||||||
|
)} in another directory
|
||||||
- Test the API and CLI
|
- Test the API and CLI
|
||||||
|
|
||||||
After that, we can proceed to bump this repo's Prettier dependency.
|
After that, we can proceed to bump this repo's Prettier dependency.
|
||||||
Press ENTER to continue.
|
`
|
||||||
`)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitForEnter();
|
await waitForEnter();
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import { execa } from "execa";
|
import { execa } from "execa";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import outdent from "outdent";
|
|
||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
import {
|
import {
|
||||||
waitForEnter,
|
waitForEnter,
|
||||||
@@ -11,8 +10,6 @@ import {
|
|||||||
getChangelogContent,
|
getChangelogContent,
|
||||||
} from "../utils.js";
|
} from "../utils.js";
|
||||||
|
|
||||||
const outdentString = outdent.string;
|
|
||||||
|
|
||||||
function writeChangelog(params) {
|
function writeChangelog(params) {
|
||||||
const changelog = fs.readFileSync("CHANGELOG.md", "utf8");
|
const changelog = fs.readFileSync("CHANGELOG.md", "utf8");
|
||||||
const newEntry = `# ${params.version}\n\n` + getChangelogContent(params);
|
const newEntry = `# ${params.version}\n\n` + getChangelogContent(params);
|
||||||
@@ -30,7 +27,15 @@ async function getChangelogForPatch({ version, previousVersion }) {
|
|||||||
return changelog;
|
return changelog;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function updateChangelog({ version, previousVersion }) {
|
export default async function updateChangelog({
|
||||||
|
dry,
|
||||||
|
version,
|
||||||
|
previousVersion,
|
||||||
|
}) {
|
||||||
|
if (dry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const semverDiff = semver.diff(version, previousVersion);
|
const semverDiff = semver.diff(version, previousVersion);
|
||||||
|
|
||||||
if (semverDiff !== "patch") {
|
if (semverDiff !== "patch") {
|
||||||
@@ -45,11 +50,11 @@ export default async function updateChangelog({ version, previousVersion }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.warn(
|
console.warn(
|
||||||
outdentString(chalk`
|
`${chalk.yellow("warning")} The file ${chalk.bold(
|
||||||
{yellow warning} The file {bold ${blogPost.file}} doesn't exist, but it will be referenced in {bold CHANGELOG.md}. Make sure to create it later.
|
blogPost.file
|
||||||
|
)} doesn't exist, but it will be referenced in ${chalk.bold(
|
||||||
Press ENTER to continue.
|
"CHANGELOG.md"
|
||||||
`)
|
)}. Make sure to create it later.`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const body = await getChangelogForPatch({
|
const body = await getChangelogForPatch({
|
||||||
@@ -61,7 +66,6 @@ export default async function updateChangelog({ version, previousVersion }) {
|
|||||||
previousVersion,
|
previousVersion,
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
console.log("Press ENTER to continue.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitForEnter();
|
await waitForEnter();
|
||||||
|
|||||||
@@ -71,7 +71,11 @@ function formatNumber(value) {
|
|||||||
return Math.floor(value / 1e5) / 10 + " million";
|
return Math.floor(value / 1e5) / 10 + " million";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function updateDependentsCount() {
|
export default async function updateDependentsCount({ dry }) {
|
||||||
|
if (dry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await update();
|
await update();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
import {
|
import { runYarn, readJson, writeJson, processFile } from "../utils.js";
|
||||||
runYarn,
|
|
||||||
logPromise,
|
|
||||||
readJson,
|
|
||||||
writeJson,
|
|
||||||
processFile,
|
|
||||||
} from "../utils.js";
|
|
||||||
|
|
||||||
async function bump({ version }) {
|
export default async function updateVersion({ version }) {
|
||||||
const pkg = await readJson("package.json");
|
const pkg = await readJson("package.json");
|
||||||
pkg.version = version;
|
pkg.version = version;
|
||||||
await writeJson("package.json", pkg);
|
await writeJson("package.json", pkg);
|
||||||
@@ -34,7 +28,3 @@ async function bump({ version }) {
|
|||||||
cwd: "./website",
|
cwd: "./website",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function updateVersion(params) {
|
|
||||||
await logPromise("Bumping version", bump(params));
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,19 @@ import chalk from "chalk";
|
|||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
|
|
||||||
export default function validateNewVersion({ version, previousVersion }) {
|
export default function validateNewVersion({ version, previousVersion }) {
|
||||||
|
if (!version) {
|
||||||
|
throw new Error("'--version' is required");
|
||||||
|
}
|
||||||
|
|
||||||
if (!semver.valid(version)) {
|
if (!semver.valid(version)) {
|
||||||
throw new Error("Invalid version specified");
|
throw new Error(
|
||||||
|
`Invalid version '${chalk.red.underline(version)}' specified`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!semver.gt(version, previousVersion)) {
|
if (!semver.gt(version, previousVersion)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Version ${chalk.yellow(version)} has already been published`
|
`Version '${chalk.yellow.underline(version)}' has already been published`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,6 @@ import chalk from "chalk";
|
|||||||
import outdent from "outdent";
|
import outdent from "outdent";
|
||||||
import { waitForEnter, logPromise } from "../utils.js";
|
import { waitForEnter, logPromise } from "../utils.js";
|
||||||
|
|
||||||
const outdentString = outdent.string;
|
|
||||||
|
|
||||||
export async function isVersionReleased(version) {
|
export async function isVersionReleased(version) {
|
||||||
const response = await fetch("https://registry.npmjs.org/prettier/");
|
const response = await fetch("https://registry.npmjs.org/prettier/");
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
@@ -23,8 +21,6 @@ async function checkBotPermission() {
|
|||||||
return maintainers.some(({ name }) => name === "prettier-bot");
|
return maintainers.some(({ name }) => name === "prettier-bot");
|
||||||
}
|
}
|
||||||
|
|
||||||
checkBotPermission();
|
|
||||||
|
|
||||||
const sleep = () =>
|
const sleep = () =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
setTimeout(resolve, 30_000);
|
setTimeout(resolve, 30_000);
|
||||||
@@ -37,35 +33,45 @@ export default async function waitForBotRelease({ dry, version }) {
|
|||||||
|
|
||||||
if (!(await checkBotPermission())) {
|
if (!(await checkBotPermission())) {
|
||||||
console.log(
|
console.log(
|
||||||
outdentString(chalk/* indent */ `
|
outdent`
|
||||||
1. Go to {green.underline https://www.npmjs.com/package/prettier/access}
|
1. Go to ${chalk.green.underline(
|
||||||
2. Add "{yellow prettier-bot}" as prettier package maintainer.
|
"https://www.npmjs.com/package/prettier/access"
|
||||||
|
)}
|
||||||
Press ENTER to continue.
|
2. Add "${chalk.yellow("prettier-bot")}" as prettier package maintainer.
|
||||||
`)
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitForEnter();
|
await waitForEnter();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
outdentString(chalk/* indent */ `
|
outdent`
|
||||||
1. Go to {green.underline https://www.npmjs.com/package/prettier/access}
|
1. Go to ${chalk.green.underline(
|
||||||
2. Make sure "{yellow Publishing access}" section is set to "{yellow Require two-factor authentication or automation tokens}".
|
"https://www.npmjs.com/package/prettier/access"
|
||||||
|
)}
|
||||||
Press ENTER to continue.
|
2. Make sure "${chalk.yellow(
|
||||||
`)
|
"Publishing access"
|
||||||
|
)}" section is set to "${chalk.yellow(
|
||||||
|
"Require two-factor authentication or automation tokens"
|
||||||
|
)}".
|
||||||
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitForEnter();
|
await waitForEnter();
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
outdentString(chalk/* indent */ `
|
outdent`
|
||||||
1. Go to {green.underline https://github.com/prettier/release-workflow/actions/workflows/release.yml}
|
1. Go to ${chalk.green.underline(
|
||||||
2. Click "{green Run workflow}" button, type "{yellow.underline ${version}}" in "Version to release", uncheck all checkboxes, hit the "{bgGreen Run workflow}" button.
|
"https://github.com/prettier/release-workflow/actions/workflows/release.yml"
|
||||||
|
)}
|
||||||
Press ENTER to continue.
|
2. Click "${chalk.green(
|
||||||
`)
|
"Run workflow"
|
||||||
|
)}" button, type "${chalk.yellow.underline(
|
||||||
|
version
|
||||||
|
)}" in "Version to release", uncheck all checkboxes, hit the "${chalk.bgGreen(
|
||||||
|
"Run workflow"
|
||||||
|
)}" button.
|
||||||
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitForEnter();
|
await waitForEnter();
|
||||||
|
|||||||
@@ -4,12 +4,20 @@ import chalk from "chalk";
|
|||||||
import validateNewVersion from "../steps/validate-new-version.js";
|
import validateNewVersion from "../steps/validate-new-version.js";
|
||||||
|
|
||||||
describe("validate-new-version", () => {
|
describe("validate-new-version", () => {
|
||||||
|
it("throws error for missing version", () => {
|
||||||
|
assert.throws(
|
||||||
|
() => {
|
||||||
|
validateNewVersion({});
|
||||||
|
},
|
||||||
|
{ message: "'--version' is required" }
|
||||||
|
);
|
||||||
|
});
|
||||||
it("throws error for invalid semver", () => {
|
it("throws error for invalid semver", () => {
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => {
|
() => {
|
||||||
validateNewVersion({ version: "foo" });
|
validateNewVersion({ version: "foo" });
|
||||||
},
|
},
|
||||||
{ message: "Invalid version specified" }
|
{ message: `Invalid version '${chalk.red.underline("foo")}' specified` }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it("throws error when version isn't greater than prev version", () => {
|
it("throws error when version isn't greater than prev version", () => {
|
||||||
@@ -17,7 +25,11 @@ describe("validate-new-version", () => {
|
|||||||
() => {
|
() => {
|
||||||
validateNewVersion({ version: "0.0.1", previousVersion: "0.0.2" });
|
validateNewVersion({ version: "0.0.1", previousVersion: "0.0.2" });
|
||||||
},
|
},
|
||||||
{ message: `Version ${chalk.yellow("0.0.1")} has already been published` }
|
{
|
||||||
|
message: `Version '${chalk.yellow(
|
||||||
|
"0.0.1"
|
||||||
|
)}' has already been published`,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
+36
-17
@@ -2,38 +2,54 @@ import fs from "node:fs";
|
|||||||
import readline from "node:readline";
|
import readline from "node:readline";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { execa } from "execa";
|
import { execa } from "execa";
|
||||||
import stringWidth from "string-width";
|
|
||||||
import outdent from "outdent";
|
import outdent from "outdent";
|
||||||
import getFormattedDate from "./get-formatted-date.js";
|
import getFormattedDate from "./get-formatted-date.js";
|
||||||
|
|
||||||
readline.emitKeypressEvents(process.stdin);
|
readline.emitKeypressEvents(process.stdin);
|
||||||
|
|
||||||
const OK = chalk.bgGreen.black(" DONE ");
|
const statusConfig = [
|
||||||
const FAIL = chalk.bgRed.black(" FAIL ");
|
{ color: "bgGreen", text: "DONE" },
|
||||||
|
{ color: "bgRed", text: "FAIL" },
|
||||||
|
{ color: "bgGray", text: "SKIPPED" },
|
||||||
|
];
|
||||||
|
const maxLength = Math.max(...statusConfig.map(({ text }) => text.length)) + 2;
|
||||||
|
const padStatusText = (text) => {
|
||||||
|
while (text.length < maxLength) {
|
||||||
|
text = text.length % 2 ? `${text} ` : ` ${text}`;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
const status = {};
|
||||||
|
for (const { color, text } of statusConfig) {
|
||||||
|
status[text] = chalk[color].black(padStatusText(text));
|
||||||
|
}
|
||||||
|
|
||||||
function fitTerminal(input) {
|
function fitTerminal(input, suffix = "") {
|
||||||
const columns = Math.min(process.stdout.columns, 80);
|
const columns = Math.min(process.stdout.columns || 40, 80);
|
||||||
const WIDTH = columns - stringWidth(OK) + 1;
|
const WIDTH = columns - maxLength + 1;
|
||||||
if (input.length < WIDTH) {
|
if (input.length < WIDTH) {
|
||||||
input += chalk.dim(".").repeat(WIDTH - input.length - 1);
|
const repeatCount = Math.max(WIDTH - input.length - 1 - suffix.length, 0);
|
||||||
|
input += chalk.dim(".").repeat(repeatCount) + suffix;
|
||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logPromise(name, promiseOrAsyncFunction) {
|
async function logPromise(name, promiseOrAsyncFunction, shouldSkip = false) {
|
||||||
const promise =
|
|
||||||
typeof promiseOrAsyncFunction === "function"
|
|
||||||
? promiseOrAsyncFunction()
|
|
||||||
: promiseOrAsyncFunction;
|
|
||||||
|
|
||||||
process.stdout.write(fitTerminal(name));
|
process.stdout.write(fitTerminal(name));
|
||||||
|
|
||||||
|
if (shouldSkip) {
|
||||||
|
process.stdout.write(`${status.SKIPPED}\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await promise;
|
const result = await (typeof promiseOrAsyncFunction === "function"
|
||||||
process.stdout.write(`${OK}\n`);
|
? promiseOrAsyncFunction()
|
||||||
|
: promiseOrAsyncFunction);
|
||||||
|
process.stdout.write(`${status.DONE}\n`);
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
process.stdout.write(`${FAIL}\n`);
|
process.stdout.write(`${status.FAIL}\n`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +58,7 @@ async function runYarn(args, options) {
|
|||||||
args = Array.isArray(args) ? args : [args];
|
args = Array.isArray(args) ? args : [args];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await execa("yarn", ["--silent", ...args], options);
|
return await execa("yarn", ["--silent", "run", ...args], options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`\`yarn ${args.join(" ")}\` failed\n${error.stdout}`);
|
throw new Error(`\`yarn ${args.join(" ")}\` failed\n${error.stdout}`);
|
||||||
}
|
}
|
||||||
@@ -54,6 +70,9 @@ function runGit(args, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function waitForEnter() {
|
function waitForEnter() {
|
||||||
|
console.log();
|
||||||
|
console.log(chalk.gray("Press ENTER to continue."));
|
||||||
|
|
||||||
process.stdin.setRawMode(true);
|
process.stdin.setRawMode(true);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
Reference in new issue
Block a user