planRelease
uses tcmts.cli.manifests.packagesIn, which uses tcmts.cli.config.configValue, which uses node:path
The next release of a package on this branch: its version from the API's changes since its previous release (or as given, if that's at least as far).
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { exportsOf, packagesIn } from "#codebase/tcmts/cli/manifests"; import { apiReport, blockers, nextVersion, packageState, previousRelease, ReleaseError, rowsAt, } from "#codebase/tcmts/cli/releases"; import type { PackageState, Plan } from "#codebase/tcmts/cli/releases"; import { highest, levelOf } from "#codebase/tcmts/cli/semver_checks"; import { shortHash } from "#codebase/tcmts/core/hash"; import { compareVersions, isExactVersion } from "#codebase/tcmts/core/semver"; export function planRelease( cb: Codebase, name: string | undefined, version: string | undefined, force: boolean, ): Plan { const head = cb.store.head(); const rows = cb.store.names(); const all = packagesIn(cb, rows); if (!all.length) throw new ReleaseError( "no packages here: a definition named `manifest` makes its namespace one (see docs/publishing.md)", ); if (name === undefined && all.length > 1) throw new ReleaseError(`which package? ${all.map((p) => p.name).join(", ")}`); const pkg = name === undefined ? all[0]! : all.find((p) => p.name === name); if (!pkg) throw new ReleaseError(`no package ${name} (there's ${all.map((p) => p.name).join(", ")})`); const now: PackageState = { pkg, rows, exported: exportsOf(pkg, all, rows) }; const prev = previousRelease(cb, pkg.name, head); // (its code then, as `rows` is its code now: the files kept with it, a README, aren't its API) const then = prev && packageState( cb, rowsAt(cb, prev.causal).filter((r) => r.kind !== "file"), pkg.name, ); const report = then ? apiReport(cb, then, now) : undefined; const level = report && highest(report.findings); const computed = nextVersion(prev?.version, level); const v = version ?? computed; const at = `${pkg.name}@${v}`; if (!isExactVersion(v)) throw new ReleaseError(`'${v}' isn't a version (major.minor.patch, like ${computed})`); const existing = cb.store.release(pkg.name, v); if (existing) throw new ReleaseError( `${at} is already released (as ${shortHash(existing.causal)}); releases don't change`, ); if (prev && compareVersions(v, prev.version) <= 0) throw new ReleaseError( `${v} isn't after ${prev.version}, the release of ${pkg.name} this branch's history has`, ); if (!force) { const blocked = blockers(cb, pkg, all, rows); if (blocked) throw new ReleaseError(blocked); if (report && !report.findings.length && version === undefined) { throw new ReleaseError( `${pkg.name} has nothing new since ${prev!.version}${report.following.length ? " (only what it uses changed, in other packages)" : ""}: no release to make (give a version, or --force, to make one anyway)`, ); } if (compareVersions(v, computed) < 0) { const why = [ ...new Set(report!.findings.filter((f) => levelOf(f) === level).map((f) => f.path)), ]; throw new ReleaseError( `${v} is too small a step from ${prev!.version}: ${level === "major" ? "breaking changes" : "additions"} (${why.slice(0, 5).join(", ")}${why.length > 5 ? ", …" : ""}) call for ${computed} (\`tcm release check ${pkg.name}\` shows why; --force releases anyway)`, ); } } return { pkg, version: v, computed, head, now, prev, report, level }; }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.manifests.exportsOf
- tcmts.cli.manifests.packagesIn
- tcmts.cli.releases.PackageState
- tcmts.cli.releases.Plan
- tcmts.cli.releases.ReleaseError
- tcmts.cli.releases.apiReport
- tcmts.cli.releases.blockers
- tcmts.cli.releases.nextVersion
- tcmts.cli.releases.packageState
- tcmts.cli.releases.previousRelease
- tcmts.cli.releases.rowsAt
- tcmts.cli.semver_checks.highest
- tcmts.cli.semver_checks.levelOf
- tcmts.core.hash.shortHash
- tcmts.core.semver.compareVersions
- tcmts.core.semver.isExactVersion