123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- const fs = require("fs");
- const path = require("path");
- const { execSync } = require("child_process");
- const updateChangelog = require("./updateChangelog");
- // skipping utils for now, as it has independent release process
- const PACKAGES = ["common", "math", "element", "excalidraw"];
- const PACKAGES_DIR = path.resolve(__dirname, "../packages");
- /**
- * Returns the arguments for the release script.
- *
- * Usage examples:
- * - yarn release --help -> prints this help message
- * - yarn release -> publishes `@excalidraw` packages with "test" tag and "-[hash]" version suffix
- * - yarn release --tag=test -> same as above
- * - yarn release --tag=next -> publishes `@excalidraw` packages with "next" tag and version "-[hash]" suffix
- * - yarn release --tag=next --non-interactive -> skips interactive prompts (runs on CI/CD), otherwise same as above
- * - yarn release --tag=latest --version=0.19.0 -> publishes `@excalidraw` packages with "latest" tag and version "0.19.0" & prepares changelog for the release
- *
- * @returns [tag, version, nonInteractive]
- */
- const getArguments = () => {
- let tag = "test";
- let version = "";
- let nonInteractive = false;
- for (const argument of process.argv.slice(2)) {
- if (/--help/.test(argument)) {
- console.info(`Available arguments:
- --tag=<tag> -> (optional) "test" (default), "next" for auto release, "latest" for stable release
- --version=<version> -> (optional) for "next" and "test", (required) for "latest" i.e. "0.19.0"
- --non-interactive -> (optional) disables interactive prompts`);
- console.info(`\nUsage examples:
- - yarn release -> publishes \`@excalidraw\` packages with "test" tag and "-[hash]" version suffix
- - yarn release --tag=test -> same as above
- - yarn release --tag=next -> publishes \`@excalidraw\` packages with "next" tag and version "-[hash]" suffix
- - yarn release --tag=next --non-interactive -> skips interactive prompts (runs on CI/CD), otherwise same as above
- - yarn release --tag=latest --version=0.19.0 -> publishes \`@excalidraw\` packages with "latest" tag and version "0.19.0" & prepares changelog for the release`);
- process.exit(0);
- }
- if (/--tag=/.test(argument)) {
- tag = argument.split("=")[1];
- }
- if (/--version=/.test(argument)) {
- version = argument.split("=")[1];
- }
- if (/--non-interactive/.test(argument)) {
- nonInteractive = true;
- }
- }
- if (tag !== "latest" && tag !== "next" && tag !== "test") {
- console.error(`Unsupported tag "${tag}", use "latest", "next" or "test".`);
- process.exit(1);
- }
- if (tag === "latest" && !version) {
- console.error("Pass the version to make the latest stable release!");
- process.exit(1);
- }
- if (!version) {
- // set the next version based on the excalidraw package version + commit hash
- const excalidrawPackageVersion = require(getPackageJsonPath(
- "excalidraw",
- )).version;
- const hash = getShortCommitHash();
- if (!excalidrawPackageVersion.includes(hash)) {
- version = `${excalidrawPackageVersion}-${hash}`;
- } else {
- // ensuring idempotency
- version = excalidrawPackageVersion;
- }
- }
- console.info(`Running with tag "${tag}" and version "${version}"...`);
- return [tag, version, nonInteractive];
- };
- const validatePackageName = (packageName) => {
- if (!PACKAGES.includes(packageName)) {
- console.error(`Package "${packageName}" not found!`);
- process.exit(1);
- }
- };
- const getPackageJsonPath = (packageName) => {
- validatePackageName(packageName);
- return path.resolve(PACKAGES_DIR, packageName, "package.json");
- };
- const updatePackageJsons = (nextVersion) => {
- const packageJsons = new Map();
- for (const packageName of PACKAGES) {
- const pkg = require(getPackageJsonPath(packageName));
- pkg.version = nextVersion;
- if (pkg.dependencies) {
- for (const dependencyName of PACKAGES) {
- if (!pkg.dependencies[`@excalidraw/${dependencyName}`]) {
- continue;
- }
- pkg.dependencies[`@excalidraw/${dependencyName}`] = nextVersion;
- }
- }
- packageJsons.set(packageName, `${JSON.stringify(pkg, null, 2)}\n`);
- }
- // modify once, to avoid inconsistent state
- for (const packageName of PACKAGES) {
- const content = packageJsons.get(packageName);
- fs.writeFileSync(getPackageJsonPath(packageName), content, "utf-8");
- }
- };
- const getShortCommitHash = () => {
- return execSync("git rev-parse --short HEAD").toString().trim();
- };
- const askToCommit = (tag, nextVersion) => {
- if (tag !== "latest") {
- return Promise.resolve();
- }
- return new Promise((resolve) => {
- const rl = require("readline").createInterface({
- input: process.stdin,
- output: process.stdout,
- });
- rl.question(
- "Do you want to commit these changes to git? (Y/n): ",
- (answer) => {
- rl.close();
- if (answer.toLowerCase() === "y") {
- execSync(`git add -u`);
- execSync(
- `git commit -m "chore: release @excalidraw/excalidraw@${nextVersion} 🎉"`,
- );
- } else {
- console.warn(
- "Skipping commit. Don't forget to commit manually later!",
- );
- }
- resolve();
- },
- );
- });
- };
- const buildPackages = () => {
- console.info("Running yarn install...");
- execSync(`yarn --frozen-lockfile`, { stdio: "inherit" });
- console.info("Removing existing build artifacts...");
- execSync(`yarn rm:build`, { stdio: "inherit" });
- for (const packageName of PACKAGES) {
- console.info(`Building "@excalidraw/${packageName}"...`);
- execSync(`yarn run build:esm`, {
- cwd: path.resolve(PACKAGES_DIR, packageName),
- stdio: "inherit",
- });
- }
- };
- const askToPublish = (tag, version) => {
- return new Promise((resolve) => {
- const rl = require("readline").createInterface({
- input: process.stdin,
- output: process.stdout,
- });
- rl.question(
- "Do you want to publish these changes to npm? (Y/n): ",
- (answer) => {
- rl.close();
- if (answer.toLowerCase() === "y") {
- publishPackages(tag, version);
- } else {
- console.info("Skipping publish.");
- }
- resolve();
- },
- );
- });
- };
- const publishPackages = (tag, version) => {
- for (const packageName of PACKAGES) {
- execSync(`yarn publish --tag ${tag}`, {
- cwd: path.resolve(PACKAGES_DIR, packageName),
- stdio: "inherit",
- });
- console.info(
- `Published "@excalidraw/${packageName}@${tag}" with version "${version}"! 🎉`,
- );
- }
- };
- /** main */
- (async () => {
- const [tag, version, nonInteractive] = getArguments();
- buildPackages();
- if (tag === "latest") {
- await updateChangelog(version);
- }
- updatePackageJsons(version);
- if (nonInteractive) {
- publishPackages(tag, version);
- } else {
- await askToCommit(tag, version);
- await askToPublish(tag, version);
- }
- })();
|