@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
116 lines (115 loc) • 5.72 kB
JavaScript
import { execFileSync, execSync } from "child_process";
import * as fs from "fs";
import * as path from "path";
import { logError, logInfo, logSuccess } from "../utils/log.js";
import { resolveCredentials, codeartifactLogin } from "./registry.js";
function repoRoot() {
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
}
function computeSnapshotVersion(root) {
const sha = execSync("git rev-parse --short HEAD", { cwd: root, encoding: "utf-8" }).trim();
const stamp = new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14);
return `0.0.0-dev.${stamp}.${sha}`;
}
function setPackageVersion(file, version) {
const content = fs.readFileSync(file, "utf-8");
const next = content.replace(/"version"(\s*:\s*)"[^"]*"/, `"version"$1"${version}"`);
fs.writeFileSync(file, next);
}
function dirtyPackageJsons(root) {
const out = execSync("git diff --name-only", { cwd: root, encoding: "utf-8" });
return new Set(out.split("\n").map((s) => s.trim()).filter((s) => s.endsWith("package.json")));
}
export function registerRegistryPublish(registry) {
registry
.command("publish")
.description("Publish an in-development snapshot of @mesh-tech packages to CodeArtifact (dev dist-tag)")
.argument("[context]", 'Platform context for Zitadel auth (e.g. "mesh.dev")')
.option("--snapshot", "Publish a dev snapshot build (required — real releases go through changesets/CI)", false)
.option("--tag <tag>", "dist-tag to publish the snapshot under", "dev")
.option("--only <pkgs>", "Comma-separated substring filter, e.g. --only agent-sdk,app-kit (default: all publishable)")
.option("--dry-run", "Build + resolve versions but do not upload", false)
.option("--role <arn>", "IAM role ARN for registry access")
.action(async (context, opts) => {
if (!opts.snapshot) {
logError("mesh registry publish currently supports only snapshot (dev) builds.\n\n" +
"Re-run with --snapshot. Real releases go through changesets + the\n" +
"changeset-version CI workflow, not this command.");
process.exit(1);
}
const root = repoRoot();
const awsCreds = resolveCredentials(context, opts.role);
const env = Object.keys(awsCreds).length > 0
? { ...process.env, ...awsCreds }
: undefined;
logInfo("Authenticating with CodeArtifact...");
if (!codeartifactLogin(env)) {
logError("CodeArtifact login failed. Run: mesh registry login <context>");
process.exit(1);
}
const listPath = path.join(root, "scripts/lib/publishable-packages.json");
const all = JSON.parse(fs.readFileSync(listPath, "utf-8"));
const patterns = (opts.only ?? "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
const selected = patterns.length
? all.filter((p) => patterns.some((pat) => p.dir.includes(pat) || p.name.includes(pat)))
: all;
if (selected.length === 0) {
logError(`No publishable package matched --only '${opts.only}'`);
process.exit(1);
}
const version = computeSnapshotVersion(root);
logInfo(`Snapshot version: ${version} (dist-tag '${opts.tag}')`);
const preDirty = dirtyPackageJsons(root);
const backups = [];
const stamp = (dir) => {
const file = path.join(root, dir, "package.json");
if (!fs.existsSync(file))
return;
backups.push({ file, content: fs.readFileSync(file, "utf-8") });
setPackageVersion(file, version);
};
try {
for (const p of selected) {
stamp(p.dir);
for (const sub of p.subPackages ?? []) {
stamp(path.join(p.dir, typeof sub === "string" ? sub : sub.dir));
}
}
const args = [
"scripts/publish-packages.sh",
"--tag",
opts.tag,
"--skip-latest",
...(patterns.length ? ["--only", selected.map((p) => p.dir).join(",")] : []),
...(opts.dryRun ? ["--dry-run"] : []),
];
logInfo(`Publishing ${selected.length} package(s)...`);
execFileSync("bash", args, { cwd: root, stdio: "inherit", env: env ?? process.env });
}
finally {
for (const b of backups)
fs.writeFileSync(b.file, b.content);
logInfo("Restored source package.json versions.");
const collateral = [...dirtyPackageJsons(root)].filter((f) => !preDirty.has(f));
if (collateral.length) {
try {
execFileSync("git", ["checkout", "--", ...collateral], { cwd: root, stdio: "inherit" });
logInfo(`Reverted ${collateral.length} package.json file(s) re-vendored by build:publish.`);
}
catch {
logError(`Could not auto-revert build:publish side-effects — run \`git checkout --\` on:\n ${collateral.join("\n ")}`);
}
}
}
logSuccess(`Published snapshot ${version} under dist-tag '${opts.tag}'.`);
console.log("\nPin these EXACTLY in the consumer repo (prereleases are never auto-resolved by ^/~):");
for (const p of selected)
console.log(` "${p.name}": "${version}",`);
console.log(`\nOr always grab the newest dev build:\n pnpm add ${selected
.map((p) => `${p.name}@${opts.tag}`)
.join(" ")}`);
});
}