UNPKG

@changesets/cli

Version:

A tool to manage versioning and changelogs with a focus on monorepos

219 lines (218 loc) 8.93 kB
import { a as isPublishSuccessful, i as isPublishFailure, n as readPlanFile, o as npmPublishQueue, r as getPublishTool, t as getPublishPlan } from "./getPublishPlan.mjs"; import { t as src_default } from "./src.mjs"; import { a as importantWarning } from "./cli-utilities.mjs"; import { t as readConfig } from "./read-config.mjs"; import { t as ensureChangesetFolder } from "./shared.mjs"; import { i as createOutputReport, n as createGitTags, r as formatGitTagResults, t as _usingCtx } from "./usingCtx.mjs"; import { ExitError } from "@changesets/errors"; import { log, progress, spinner } from "@clack/prompts"; import path, { resolve } from "node:path"; import { getPackages } from "@manypkg/get-packages"; import { readPreState } from "@changesets/pre"; //#region src/commands/publish/index.ts function formatPackageList(entry, versionColor = src_default.green) { return entry.toSorted((a, b) => a.name.localeCompare(b.name)).map((entry) => { const error = "result" in entry && isPublishFailure(entry) ? `\n${src_default.dim(`└`)} ${entry.code || "(no code)"}: ${entry.message || "Unknown error"}` : ""; return `${src_default.blueBright(entry.name)}@${versionColor(entry.version)}${error}`; }).join("\n"); } function showNonLatestTagWarning(tag, preState) { if (preState) importantWarning(` You are in prerelease mode, so packages will be published to the ${src_default.cyan(preState.tag)} npm tag, ${src_default.red("except")} for packages that have not had normal releases, which will be published to ${src_default.cyan("latest")}. `); else if (tag !== "latest") log.warn(`Packages will be released under the ${tag} tag.`); } async function bulkPublishPackages({ publishTool, publishQueue, packagesByName, artifactDir, otpCode, onResult }) { if (publishQueue.length === 0) return []; const publishPromises = publishQueue.map(async (item) => { const pkg = packagesByName.get(item.release.name); const result = await npmPublishQueue.add(() => publishTool.publish({ pkg, release: item.release, tarballPath: artifactDir ? resolve(artifactDir, item.release.tarball.path) : null, interactive: false, otpCode })); onResult?.(result); return { release: item.release, result }; }); return Promise.all(publishPromises); } async function publish(options) { try { var _usingCtx$1 = _usingCtx(); const reporter = _usingCtx$1.a(await createOutputReport(options?.output)); const cwd = options?.cwd ?? process.cwd(); const artifactDir = options?.fromPackDir ? path.resolve(cwd, options.fromPackDir) : void 0; const packages = await getPackages(cwd); const packagesByName = new Map(packages.packages.map((pkg) => [pkg.packageJson.name, pkg])); const publishTool = await getPublishTool(packages); await ensureChangesetFolder(packages.rootDir); const releaseTag = options?.tag && options.tag.length > 0 ? options.tag : void 0; const preState = !artifactDir ? await readPreState(packages.rootDir) : void 0; if (artifactDir && releaseTag) { log.error("Releasing under custom tag is not allowed in artifact mode."); throw new ExitError(1); } if (releaseTag && preState && preState.mode === "pre") { log.error(` Releasing under custom tag is not allowed in pre mode! To resolve this exit the pre mode by running ${src_default.cyan("changeset pre exit")}. `.trim()); throw new ExitError(1); } if (releaseTag || preState) showNonLatestTagWarning(options?.tag, preState); const config = await readConfig(packages); const plan = artifactDir ? await readPlanFile(path.join(artifactDir, "publish-plan.json")) : await getPublishPlan(packages.rootDir, config, { tag: releaseTag }); if (plan.length === 0) { log.warn("No unpublished projects to publish."); return; } let finishedCount = 0; const successfulNpmPublishes = []; const unsuccessfulNpmPublishes = []; const totalPublishCount = plan.reduce((count, chunk) => count + chunk.filter((release) => release.kind === "publish").length, 0); const gitTagReleases = []; const tagOnlyReleases = /* @__PURE__ */ new Set(); let otpCode = publishTool.getOtpCode(options?.otp); let sequential = process.stdin.isTTY && otpCode == null; const p = progress({ max: totalPublishCount }); const renderProgressMessage = () => finishedCount === 0 ? "Publishing packages" : `Publishing packages (${finishedCount}/${totalPublishCount})`; const advanceProgress = () => { finishedCount++; p.advance(1, renderProgressMessage()); }; if (totalPublishCount > 0) p.start(renderProgressMessage()); publishChunks: for (const chunk of plan) { let publishQueue = []; for (const release of chunk) { if (release.kind === "tag-only") { if (options?.gitTag ?? true) { gitTagReleases.push(release); tagOnlyReleases.add(release); } continue; } publishQueue.push({ release, result: void 0 }); } while (publishQueue.length > 0) { if (sequential) { const item = publishQueue.shift(); let interactive = false; let result = item.result ?? await npmPublishQueue.add(() => publishTool.publish({ pkg: packagesByName.get(item.release.name), release: item.release, tarballPath: artifactDir ? path.resolve(artifactDir, item.release.tarball.path) : null, interactive, otpCode })); while (result.result === "failed:needs-2fa") { otpCode = null; p.stop(`${src_default.blue(item.release.name)} requires 2FA verification to publish...`); if (totalPublishCount >= 2) importantWarning(src_default.italic(` Make sure to check the "skip 2fa for 5 minutes" option to not have to do this for every package being published after this! `.trim())); interactive = true; result = await npmPublishQueue.add(() => publishTool.publish({ pkg: packagesByName.get(item.release.name), release: item.release, tarballPath: artifactDir ? path.resolve(artifactDir, item.release.tarball.path) : null, interactive, otpCode: null })); } advanceProgress(); if (result.result === "failed:already-published") { if (finishedCount === totalPublishCount) p.clear(); else if (interactive) p.start(renderProgressMessage()); continue; } if (isPublishSuccessful(result)) { successfulNpmPublishes.push(result); if (!interactive) sequential = false; else if (finishedCount < totalPublishCount) p.start(renderProgressMessage()); } if (isPublishFailure(result)) { p.clear(); unsuccessfulNpmPublishes.push(result); break publishChunks; } continue; } const publishedItems = await bulkPublishPackages({ publishTool, publishQueue, packagesByName, otpCode, artifactDir, onResult: (result) => { if (process.stdin.isTTY && result.result === "failed:needs-2fa") return; advanceProgress(); } }); const results = publishedItems.map((item) => item.result); const successes = results.filter(isPublishSuccessful); successfulNpmPublishes.push(...successes); const failures = results.filter((result) => result.result === "failed"); unsuccessfulNpmPublishes.push(...failures); const recoverableItems = publishedItems.filter((item) => item.result.result === "failed:needs-2fa"); if (failures.length > 0 || !process.stdin.isTTY) { unsuccessfulNpmPublishes.push(...recoverableItems.map((item) => item.result)); publishQueue = []; if (failures.length > 0 || recoverableItems.length > 0) break publishChunks; continue; } publishQueue = recoverableItems.map((item, index) => ({ release: item.release, result: index === 0 ? item.result : void 0 })); if (publishQueue.length > 0) { sequential = true; otpCode = null; } } } if (successfulNpmPublishes.length !== 0) { const message = `Successfully published: ${formatPackageList(successfulNpmPublishes)}`; if (sequential) log.success(message); else p.stop(message); if (options?.gitTag ?? true) gitTagReleases.push(...successfulNpmPublishes.map((result) => ({ kind: "tag-only", ...result }))); } else p.clear(); if (unsuccessfulNpmPublishes.length !== 0) log.error(` Some packages failed to publish: ${formatPackageList(unsuccessfulNpmPublishes, src_default.red)} `.trim()); if (gitTagReleases.length > 0) { const p = spinner(); p.start("Creating git tags..."); const results = await createGitTags({ packages, releases: gitTagReleases, reporter }); p.stop(formatGitTagResults(packages.tool, { tagged: results.tagged.filter((release) => tagOnlyReleases.has(release)), existing: results.existing })); } if (unsuccessfulNpmPublishes.length !== 0) throw new ExitError(1); } catch (_) { _usingCtx$1.e = _; } finally { await _usingCtx$1.d(); } } //#endregion export { publish };