You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
7.0 KiB
239 lines
7.0 KiB
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( |
|
"Would you like 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( |
|
"Would you like 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); |
|
} |
|
})();
|
|
|