/** * Type definitions for `celilo publish` — the planner/executor split per * openspec/changes/publilo-cli/proposal.md Phase 2. * * The umbrella `PublishPlan` enumerates everything a publish run intends * to do. It's produced by the pure planner (`plan.ts`) and consumed by * the executor (`execute.ts`). The planner takes its inputs from * discovery + flag parsing; the executor only mutates state described in * the plan. Dry-run is "build plan, print it, exit"; a real publish is * "build plan, confirm, execute." */ /** * Subset of the npm package.json shape we touch. Kept narrow so the * planner can't accidentally rely on fields not part of the contract. */ export interface PackageJson { name?: string; version?: string; dependencies?: Record; devDependencies?: Record; peerDependencies?: Record; optionalDependencies?: Record; } /** * Single workspace package → npm-version mapping. Built once at the top * of a publish run from each package's package.json#version. In alpha * mode it gets tightened to the alpha versions before being threaded * through to the workspace-dep rewriter, so cross-package alpha pins * resolve to the alpha line. */ export type WorkspaceVersionMap = Map; /** * What kind of publish run is this? Most per-package decisions * (preflight gating, version rewrite, bun publish flags, post-publish * verification expectations) flow from this single discriminator. * * - `normal`: bump real semver, publish to `@latest`, run Phase 2/3/4. * - `alpha`: publish `X.Y.Z-alpha.N` to the `@alpha` dist-tag. Skip * Phase 2 (consumer pins opt in via `@alpha` manually). * Phase 3 only runs when `trackAlpha`. Phase 4 only when * `alphaModules`. * - `promote`: graduate a specific alpha to its base real version (e.g. * `@celilo/e2e@0.7.14-alpha.3` → `@celilo/e2e@0.7.14`). * Single package; Phase 2/3/4 run normally — this IS a * real release. */ export type PublishMode = | { kind: 'normal' } | { kind: 'alpha'; trackAlpha: boolean; alphaModules: boolean } | { kind: 'promote'; target: { name: string; version: string } }; /** * Parsed top-level flag bundle. Produced by `parseOptions` and threaded * into the planner. Doesn't include flag-validation errors — those bail * out before reaching here. */ export interface PublishOptions { allowStale: boolean; autoYes: boolean; mode: PublishMode; /** * Module ids to leave out of the module-registry sweep (`--skip-module * `, repeatable). For a module that cannot publish yet. A failed * module no longer stops the sweep (celilo#1369), but it still fails * the run at the end, so a module that cannot publish yet is held * back with this instead. */ skippedModules: string[]; /** * Which part of the publish touches the module registry (`--modules-only` * / `--skip-modules`). `run` is the default whole publish. `skip` runs * the npm phases only; `only` runs the module sweep only. * * The release pipeline uses the split so CLI delivery (the .deb, the * apt upload, the Forgejo release) does not depend on the module sweep * succeeding: npm publishes first, the fleet delivery happens, and the * module sweep runs last, where its failure reds the run without * withholding the CLI (celilo#1369). */ modulePhase: 'run' | 'skip' | 'only'; } /** * One workspace package source committing past its package.json version. * Surfaced by `checkStaleVersion` during preflight; blocks a normal * publish unless --allow-stale. */ export interface StalenessIssue { name: string; pkg: string; version: string; lastSrcCommit: string; lastPkgJsonCommit: string; } /** * Module source committed past its manifest.yml. Surfaced by * `checkModuleStale` during preflight; blocks a normal publish unless * --allow-stale or `bun run publish --release-touch`. */ export interface ModuleStaleIssue { moduleDir: string; lastSrcCommit: string; lastManifestCommit: string; } /** * Snapshot of everything preflight learned about the working tree. * Built once at the top of the run and threaded into the planner. */ export interface PreflightReport { dirty: boolean; dirtyOutput: string; workspaceStale: StalenessIssue[]; moduleStale: ModuleStaleIssue[]; } /** * Single workspace:^ → explicit-pin rewrite recorded by * `rewriteWorkspaceDeps`. Used by the executor for both the actual * package.json mutation and the post-publish verification step. */ export interface WorkspaceRewrite { depName: string; bucket: string; oldSpec: string; newSpec: string; } /** * Optional knobs for the package.json rewriter. Lifecycle: the planner * decides which to set per package (per-mode), the executor applies them * to the live file just before `bun publish` and restores from the * captured `original` after. */ export interface RewriteOptions { /** * Force the package's `version` field to this value before publish. * Used by --alpha (sets `X.Y.Z-alpha.N`) and --promote (forces the * base `X.Y.Z` even if source disagrees). */ targetVersion?: string; /** * Pin workspace deps to exact versions instead of `^`/`~`. Required for * --alpha mode: pre-release semver excludes pre-release versions from * caret ranges, so the only way to keep alpha consumers resolvable is * to pin to the exact alpha that was just published. */ exactPins?: boolean; /** * Stamp a `gitHead` field on the published package.json so future * `--alpha` runs can detect "no source change since prior alpha" and * skip. bun publish doesn't populate gitHead automatically. */ gitHead?: string; } /** * Per-package pre-publish work. Currently only `@celilo/e2e` triggers this * (it bundles the npm-compat registry server source inside its tarball). * Sim-content caches (website/npm) and standard-module netapps are NOT * bundled at publish — they're fetched from the public celilo sources at * `cele2e build-infra` time instead (ce-qwz Decisions 2B + 3, ce-i2i). */ export type PrePublishHookKind = 'registryServerBundle'; /** * Single workspace package planned to publish (or explicitly skip) in * this run. The plan is built before any side effects so the dry-run * can faithfully describe what `bun publish` would do. */ export interface WorkspaceItem { /** Workspace path relative to REPO_ROOT (e.g. 'packages/e2e'). */ pkg: string; /** npm package name (e.g. '@celilo/e2e'). */ name: string; /** Version currently in this package's package.json. */ baseVersion: string; /** * What this run will publish. Same as `baseVersion` for normal mode, * `X.Y.Z-alpha.N` for alpha mode, base-stripped for promote mode. */ versionToPublish: string; /** * npm dist-tag override. `'alpha'` for alpha mode; for normal mode a * prerelease version (e.g. `0.5.0-alpha.0`) derives its tag from the * prerelease identifier (ISS-0083) so it never lands on `@latest`. * undefined → `@latest` (only for stable versions). */ tag?: string; /** * Rewrite recipe applied to the package's package.json immediately * before `bun publish`. Restored from `originalPackageJson` after. */ rewriteOptions: RewriteOptions; /** Pre-publish work to do (netapp rebuild, cache staging, etc.). */ hooks: PrePublishHookKind[]; /** * If set, this package is explicitly NOT publishing this run (already * on npm, source unchanged since prior alpha, etc.). The reason * appears in the dry-run plan and the post-run summary. */ skipReason?: string; } /** * Pin update planned for a single consumer package.json. Generated by * walking the in-repo modules/, apps/, packages/, and any * EXTERNAL_PROJECT_PATHS, matching `@celilo/*` deps to current npm * latests. Produced in `--normal` and `--promote` modes; skipped in * `--alpha` (consumers opt into the @alpha tag manually). */ export interface ConsumerPinItem { /** Absolute path to the package.json. */ filePath: string; /** What this run will rewrite in that file. */ updates: Array<{ bucket: 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies'; depName: string; oldSpec: string; newSpec: string; }>; } /** * Single managed package planned to be force-pinned (or update-pulled) * in bun's global install. `target` is the version we expect after the * operation; `installed` is what's there now. */ export interface GlobalUpdateItem { name: string; installed: string | null; target: string; /** * If true, the executor will run `bun add -g @` — force- * pin to an exact version. Used for packages we just published this * run (so the global ends up on the precise version, not whatever * @latest resolves to a moment later). * * If false, the executor runs `bun update -g ` — chase whatever * `@latest` is now. Used for managed packages that we didn't publish * this run but might still be drifting in the global install. */ forcePin: boolean; } /** * Single module planned to publish to the celilo registry. Phase 4 is * the slimmest phase: revision selection and tarball construction live * inside `celilo module publish` itself, so the plan only needs to * enumerate the directories. */ export interface ModuleItem { moduleDir: string; } /** * Umbrella plan — the central data structure of Phase 2 of the publish * CLI refactor (openspec/changes/publilo-cli/proposal.md). One plan covers all four phases of * a publish run; the executor consumes it top-to-bottom. */ export interface PublishPlan { mode: PublishMode; options: PublishOptions; preflight: PreflightReport; /** Workspace npm publishes — Phase 1 of the publish flow. */ workspace: WorkspaceItem[]; /** Consumer package.json pin bumps — Phase 2. */ consumerPins: ConsumerPinItem[]; /** bun global install force-pins / refreshes — Phase 3. */ globalUpdate: GlobalUpdateItem[]; /** Modules to publish to the celilo registry — Phase 4. */ modulePublish: ModuleItem[]; } /** * Aggregated outcome of a publish run. Mirrors the prior * `runPublishPhase` return shape so the post-run summary can keep * reporting the same way (published list + skipped list). */ export interface PublishResult { published: Array<{ name: string; version: string }>; skipped: string[]; }