eng/scripts/prepare-publish.js (155 lines of code) (raw):
// @ts-check
import pc from "picocolors";
import { parseArgs } from "util";
import { runOrExit } from "../../core/packages/internal-build-utils/dist/src/common.js";
import {
CommandFailedError,
checkForChangedFiles,
coreRepoRoot,
listPackages,
repoRoot,
} from "./helpers.js";
const columns = process.stdout.columns;
function log(...args) {
console.log(...args);
}
function logSuccess(message) {
log(pc.green(`✓ ${message}`));
}
function logRegionStart(text) {
const split = Math.floor((columns - text.length - 4) / 2);
log();
log(pc.cyan("-".repeat(split) + " " + text + " " + "-".repeat(split)));
}
function logRegionEnd() {
log(pc.cyan("-".repeat(columns)));
}
const args = parseArgs({
options: {
noCommit: { type: "boolean" },
onlyBumpVersions: { type: "boolean" }, // Only bump version, skip any extra steps like updating the docs and regenerating samples
},
args: process.argv.slice(2),
});
const NoChange = 0;
const Patch = 1;
const Minor = 2;
const Major = 3;
/**
* Set this to false to test this script without creating a new branch, checking for changes etc.
* DO NOT LEAVE TO FALSE
*/
const production = !args.values.noCommit;
let branch;
if (production) {
// Create and checkout branches
branch = `publish/${Date.now().toString(36)}`;
log("Creating branch in both repos", branch);
await doubleRun("git", "checkout", "-b", branch);
}
// Check that we have a clean slate before starting
if (production) {
await checkPrePublishState();
}
// Update the typespec core submodule
log("Updating typespec core submodule");
await typespecRun("git", "fetch", "https://github.com/microsoft/typespec", "main");
if (production) {
await typespecRun("git", "merge", "--ff-only", "FETCH_HEAD");
}
// Stage the typespec core publish
await typespecRun("pnpm", "change", "version", "--exclude", "standalone");
if (!args.values.onlyBumpVersions) {
await typespecRun("pnpm", "update-playground-versions");
}
await typespecRunWithRetries(3, "pnpm", "install");
if (await checkForChangedFiles(coreRepoRoot, undefined, { silent: true })) {
if (production) {
await typespecRun("git", "add", "-A");
await typespecRun("git", "commit", "-m", "Prepare typespec publish");
}
} else {
console.log("INFO: No changes to typespec.");
}
if (production && (await checkForChangedFiles(repoRoot, undefined, { silent: true }))) {
await typespecAzureRun("git", "commit", "-a", "-m", "Update core submodule");
}
log("Bumping cross-submodule dependencies");
// Determine project versions including any bumps from typespec publish above
const versions = await getProjectVersions();
// Stage typespec-azure publish
await typespecAzureRun("pnpm", "change", "version");
if (!args.values.onlyBumpVersions) {
await typespecAzureRun("pnpm", "update-playground-versions");
}
if (await checkForChangedFiles(repoRoot, undefined, { silent: true })) {
if (production) {
await typespecAzureRun("git", "add", "-A");
await typespecAzureRun("git", "commit", "-m", "Prepare typespec-azure publish");
}
} else {
console.log("INFO: No changes to typespec-azure.");
}
rebuildAndRegenSamplesToBumpTemplateVersions();
// And we're done
console.log();
if (production) {
console.log(`Success! Push ${branch} branches and send PRs.`);
} else {
console.log(`Success! All the files have been updated.`);
console.log("**DEVELOPMENT** The production flag is set to false");
}
async function checkPrePublishState() {
log("Checking repo state is clean");
if (await checkForChangedFiles()) {
console.error("ERROR: Cannot prepare publish because files above were modified.");
process.exit(1);
}
try {
if (production) {
await doubleRun("pnpm", "change", "status");
}
} catch (e) {
if (e instanceof CommandFailedError) {
console.error("ERROR: Cannot prepare publish because changelogs are missing.");
process.exit(1);
}
throw e;
}
logSuccess("Repo state is clean");
}
async function doubleRun(command, ...args) {
await typespecRun(command, ...args);
await typespecAzureRun(command, ...args);
}
async function typespecRun(command, ...args) {
console.log();
logRegionStart("typespec");
await runOrExit(command, args, { cwd: coreRepoRoot });
logRegionEnd();
}
async function typespecAzureRun(command, ...args) {
console.log();
logRegionStart("typespec-azure");
await runOrExit(command, args, { cwd: repoRoot });
logRegionEnd();
}
async function typespecAzureRunWithOptions(options, command, ...args) {
console.log();
logRegionStart("typespec-azure");
await runOrExit(command, args, { cwd: repoRoot, ...options });
}
async function typespecRunWithRetries(tries, command, ...args) {
try {
logRegionStart(`typespec (remaining tries: ${tries})`);
await runOrExit(command, args, { cwd: coreRepoRoot });
logRegionEnd();
} catch (err) {
logRegionEnd();
if (tries-- > 0) {
await typespecRunWithRetries(tries, command, ...args);
} else throw err;
}
}
async function typespecAzureRunWithRetries(tries, command, ...args) {
try {
logRegionStart(`typespec-azure (remaining tries: ${tries})`);
await runOrExit(command, args, { cwd: repoRoot });
logRegionEnd();
} catch (err) {
logRegionEnd();
if (tries-- > 0) {
await typespecAzureRunWithRetries(tries, command, ...args);
} else throw err;
}
}
async function getProjectVersions() {
const map = new Map();
for (const project of await listPackages()) {
map.set(project.manifest.name, project.manifest.version);
}
return map;
}
async function rebuildAndRegenSamplesToBumpTemplateVersions() {
await typespecAzureRunWithRetries(3, "pnpm", "install");
await typespecAzureRunWithOptions(
{ env: { ...process.env, TYPESPEC_SKIP_DOCUSAURUS_BUILD: true } },
"pnpm",
"build",
);
if (!args.values.onlyBumpVersions) {
await typespecAzureRun("pnpm", "build");
await typespecAzureRun("pnpm", "regen-samples");
}
if ((await checkForChangedFiles(coreRepoRoot, undefined, { silent: true })) && production) {
await typespecRun("git", "add", "-A");
await typespecRun("git", "commit", "-m", "Rebuild and regen samples to bump template versions");
}
if ((await checkForChangedFiles(repoRoot, undefined, { silent: true })) && production) {
await typespecAzureRun("git", "add", "-A");
await typespecAzureRun(
"git",
"commit",
"-m",
"Rebuild and regen samples to bump template versions",
);
}
}