/** * `cleo release open` — Phase 3 verb of the new release pipeline. * * Consumes the plan file at `.cleo/release/.plan.json` (written by * {@link releasePlan} in T9525) and dispatches the `release-prepare.yml` * GitHub Actions workflow via `gh workflow run`. UPDATEs the `releases` * row's `status` to `pr-opened` and persists the resolved workflow run URL * into `releases.workflow_run_url`. * * This verb is **side-effectful** — it shells out to `gh` and writes one * row to the `releases` table. It does NOT push commits, mutate any * source files, or invoke npm/cargo publish. * * Implements SPEC-T9345 §4.3 (R-050 through R-071): * * - R-050 .. R-053 — pre-condition gates (plan exists, releases status, * gh auth, workflow file). * - R-060 .. R-062 — side effects (gh workflow run + DB update; optional * plan-commit when `--commit-plan` is supplied). * - R-070 .. R-071 — post-conditions (status='pr-opened', workflow_run_url * is a valid gh run URL). * * All `gh` and `git` subprocesses run with a 60s timeout per task rules. * * @task T9530 * @epic T9494 * @adr ADR-T9345 * @spec .cleo/rcasd/T9345/research/SPEC-T9345-release-pipeline-v2.md §4.3 */ import { type EngineResult } from '@cleocode/contracts'; /** Default workflow file name dispatched by `cleo release open`. */ export declare const DEFAULT_OPEN_WORKFLOW: "release-prepare.yml"; /** * Options accepted by {@link releaseOpen}. * * @task T9530 */ export interface ReleaseOpenOptions { /** The release version, e.g. `v2026.6.0`. Required positional input. */ version: string; /** Workflow file name to dispatch. Defaults to `release-prepare.yml`. */ workflow?: string; /** When true, poll `gh run watch` until the run reaches a terminal state. */ watch?: boolean; /** * When true, commit the plan file to the active branch before dispatching. * NOT the default — the workflow can re-derive the plan from `releases` + tasks.db. */ commitPlan?: boolean; /** * Project root override. Defaults to the canonical project root resolved * via {@link getProjectRoot} (walks up from `process.cwd()` for monorepo * subdir invocations; honours `CLEO_ROOT` / `CLEO_PROJECT_ROOT`). * * @task T9583 */ projectRoot?: string; } /** * Data payload returned by {@link releaseOpen} on success. * * @task T9530 */ export interface ReleaseOpenResult { /** The version dispatched (matches input). */ version: string; /** The GitHub Actions run URL recorded into `releases.workflow_run_url`. */ workflowRunUrl: string; /** True iff `--watch` was supplied (caller waited for the run). */ watching: boolean; /** True iff this invocation was a no-op because status was already `pr-opened`. */ idempotent?: boolean; /** * Plan file sha256, computed for downstream provenance tracking and * returned in the result envelope. Per T10105 this is NO LONGER passed * as a `--field` to `gh workflow run`, because the * `release-prepare.yml workflow_dispatch.inputs` block does not declare * the field and the GitHub Actions API rejected the dispatch with * HTTP 422 "Unexpected inputs provided" during the v2026.5.100 ship. */ planBlobSha256: string; } /** * Internal handle to subprocess runners. Public via {@link __test__} so unit * tests can swap out the real `gh` / `git` callers without `vi.mock` plumbing. * * @internal */ export interface ReleaseOpenRunner { /** Run `gh ` and return trimmed stdout. Throws on non-zero exit. */ runGh: (args: readonly string[], cwd: string) => string; /** Test whether `gh auth status` exits 0. Returns the boolean directly. */ checkGhAuth: (cwd: string) => boolean; } /** * Default runner — invokes the real `gh` binary on PATH with a 60s timeout. * * @internal */ declare function makeDefaultRunner(): ReleaseOpenRunner; /** * R-050: load and validate the plan file at * `.cleo/release/.plan.json`. * * Returns the raw JSON body (for sha256 hashing) AND the parsed plan, or an * `E_PLAN_NOT_FOUND` / `E_RELEASE_PLAN_INVALID` envelope on failure. * * @internal */ declare function loadPlanForOpen(version: string, projectRoot: string): EngineResult<{ rawBody: string; planPath: string; }>; /** * R-053: assert the workflow file exists under `.github/workflows/`. * * @internal */ declare function assertWorkflowFile(workflow: string, projectRoot: string): EngineResult<{ workflowPath: string; }>; /** * Resolve the most recent workflow run URL for the given workflow file after * dispatch. Returns the URL string or `null` if `gh run list` yields nothing. * * Polls up to {@link maxAttempts} times because `gh workflow run` returns * BEFORE the run is registered in `gh run list` (the API has a small lag). * * @internal */ declare function resolveLatestRunUrl(workflow: string, cwd: string, runner: ReleaseOpenRunner, maxAttempts?: number): string | null; /** * UPDATE the `releases` row to `status='pr-opened'` and persist the * resolved workflow run URL (R-061 + R-070 + R-071). * * @internal */ declare function updateReleaseRow(version: string, workflowRunUrl: string, projectRoot: string): Promise; /** * Read the current status + workflow_run_url of the releases row for a version. * * @internal */ declare function readReleaseStatus(version: string, projectRoot: string): Promise<{ status: string; workflowRunUrl: string | null; } | null>; /** * R-062: commit the plan file to the active branch when `--commit-plan` is * supplied. The default flow leaves the plan uncommitted; the workflow can * re-derive the plan envelope from `releases` + tasks.db without it. * * @internal */ declare function commitPlanFile(planPath: string, version: string, projectRoot: string): void; /** * Dispatch the `release-prepare.yml` workflow for the given version and * UPDATE the `releases` row to `status='pr-opened'`. * * Idempotency: if the row is ALREADY at `status='pr-opened'` AND has a * non-null `workflow_run_url`, the verb is a no-op modulo `meta.idempotent=true` * (returns the existing URL without re-dispatching). * * @example * ```ts * const result = await releaseOpen({ version: 'v2026.6.0' }); * if (result.success) { * console.log(`Dispatched: ${result.data.workflowRunUrl}`); * } * ``` * * @task T9530 */ export declare function releaseOpen(opts: ReleaseOpenOptions, runnerOverride?: ReleaseOpenRunner): Promise>; /** * Internal helpers exposed for unit testing. NOT part of the public API. * * @internal */ export declare const __test__: { assertWorkflowFile: typeof assertWorkflowFile; commitPlanFile: typeof commitPlanFile; loadPlanForOpen: typeof loadPlanForOpen; makeDefaultRunner: typeof makeDefaultRunner; readReleaseStatus: typeof readReleaseStatus; resolveLatestRunUrl: typeof resolveLatestRunUrl; updateReleaseRow: typeof updateReleaseRow; }; export {}; //# sourceMappingURL=open.d.ts.map