mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-10 03:30:59 +00:00
fix: close sandbox-native metadata gaps
Ensure local runs use the worktree checkpoint path by default, expose source and sandbox paths in API/web surfaces, and remove dead fork/rewind push controls. Update docs for clone-based sandboxes and durable checkpoint timelines.
This commit is contained in:
parent
80aad30f73
commit
ab9b28875b
30 changed files with 399 additions and 186 deletions
|
|
@ -33,6 +33,7 @@ describe("mapRunListItem", () => {
|
|||
expect(item.title).toBe("Server supplied title");
|
||||
expect(item.workflow).toBe("fix_build");
|
||||
expect(item.repo).toBe("myrepo");
|
||||
expect(item.sourceDirectory).toBe("/home/user/myrepo");
|
||||
expect(item.elapsed).toBeDefined();
|
||||
expect(item.column).toBe("running");
|
||||
expect(item.lifecycleStatus).toBe("paused");
|
||||
|
|
@ -86,6 +87,7 @@ describe("mapRunSummaryToRunItem", () => {
|
|||
expect(item.title).toBe("Fix the build");
|
||||
expect(item.workflow).toBe("fix_build");
|
||||
expect(item.repo).toBe("myrepo");
|
||||
expect(item.sourceDirectory).toBe("/home/user/myrepo");
|
||||
expect(item.elapsed).toBeDefined();
|
||||
expect(item.lifecycleStatus).toBe("running");
|
||||
});
|
||||
|
|
@ -113,6 +115,7 @@ describe("mapRunSummaryToRunItem", () => {
|
|||
expect(item.title).toBe("Untitled run");
|
||||
expect(item.workflow).toBe("unknown");
|
||||
expect(item.repo).toBe("unknown");
|
||||
expect(item.sourceDirectory).toBeUndefined();
|
||||
});
|
||||
|
||||
test("recognizes canonical blocked and queued run statuses", () => {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ export interface RunItem {
|
|||
comments?: number;
|
||||
question?: string;
|
||||
sandboxId?: string;
|
||||
sandboxWorkingDirectory?: string;
|
||||
sourceDirectory?: string;
|
||||
}
|
||||
|
||||
export type ColumnStatus = "initializing" | "running" | "blocked" | "succeeded" | "failed";
|
||||
|
|
@ -80,7 +82,9 @@ export function mapRunListItem(item: RunListItem): RunItem {
|
|||
resources: item.sandbox?.resources ? `${item.sandbox.resources.cpu} CPU / ${item.sandbox.resources.memory} GB` : undefined,
|
||||
comments: item.pull_request?.comments,
|
||||
question: item.question?.text,
|
||||
sandboxId: item.sandbox?.id,
|
||||
sandboxId: item.sandbox?.id ?? undefined,
|
||||
sandboxWorkingDirectory: item.sandbox?.working_directory ?? undefined,
|
||||
sourceDirectory: item.source_directory ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +99,7 @@ export function mapRunSummaryToRunItem(summary: RunSummary): RunItem {
|
|||
workflow: summary.workflow_slug ?? summary.workflow_name ?? "unknown",
|
||||
lifecycleStatus,
|
||||
lifecycleStatusLabel: lifecycleStatusLabel(summary.status),
|
||||
sourceDirectory: summary.source_directory ?? undefined,
|
||||
elapsed:
|
||||
summary.elapsed_secs != null
|
||||
? formatElapsedSecs(summary.elapsed_secs)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import type {
|
|||
PaginatedRunStageList,
|
||||
PaginatedStageTurnList,
|
||||
RunBilling,
|
||||
RunProjection,
|
||||
ServerSettings,
|
||||
RunSummary,
|
||||
} from "@qltysh/fabro-api-client";
|
||||
|
|
@ -69,6 +70,13 @@ export function useRun(id: string | undefined) {
|
|||
);
|
||||
}
|
||||
|
||||
export function useRunState(id: string | undefined) {
|
||||
return useSWR<RunProjection | null>(
|
||||
id ? queryKeys.runs.state(id) : null,
|
||||
apiNullableFetcher,
|
||||
);
|
||||
}
|
||||
|
||||
export function useRunFiles(id: string | undefined) {
|
||||
return useSWR<PaginatedRunFileList | null>(
|
||||
id ? queryKeys.runs.files(id) : null,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export const queryKeys = {
|
|||
},
|
||||
runs: {
|
||||
detail: (id: string) => `/api/v1/runs/${pathSegment(id)}`,
|
||||
state: (id: string) => `/api/v1/runs/${pathSegment(id)}/state`,
|
||||
files: (id: string) => `/api/v1/runs/${pathSegment(id)}/files`,
|
||||
stages: (id: string) => `/api/v1/runs/${pathSegment(id)}/stages`,
|
||||
graph: (id: string, direction?: "LR" | "TB") =>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import {
|
|||
type PreviewMutationResult,
|
||||
} from "../lib/mutations";
|
||||
import { useRunEvents } from "../lib/run-events";
|
||||
import { useRun, useRunQuestionText } from "../lib/queries";
|
||||
import { useRun, useRunQuestionText, useRunState } from "../lib/queries";
|
||||
import {
|
||||
canArchive,
|
||||
canCancel,
|
||||
|
|
@ -94,13 +94,22 @@ function buildRunDetailRun(summary: RunSummary): RunDetailRun {
|
|||
};
|
||||
}
|
||||
|
||||
function sandboxWorkingDirectoryFromState(
|
||||
state: { sandbox?: { working_directory?: unknown } | null } | null | undefined,
|
||||
): string | undefined {
|
||||
const value = state?.sandbox?.working_directory;
|
||||
return typeof value === "string" && value.trim() ? value : undefined;
|
||||
}
|
||||
|
||||
export function meta({ data }: any) {
|
||||
const run = data?.run;
|
||||
return [{ title: run ? `${run.title} — Fabro` : "Run — Fabro" }];
|
||||
}
|
||||
|
||||
export default function RunDetail({ params }: { params: { id: string } }) {
|
||||
const demoMode = useDemoMode();
|
||||
const runQuery = useRun(params.id);
|
||||
const runStateQuery = useRunState(demoMode ? undefined : params.id);
|
||||
const run = runQuery.data ? buildRunDetailRun(runQuery.data) : null;
|
||||
const statusKind = runQuery.data?.status?.kind;
|
||||
const blockedQuestion = useRunQuestionText(params.id, statusKind === "blocked");
|
||||
|
|
@ -111,7 +120,6 @@ export default function RunDetail({ params }: { params: { id: string } }) {
|
|||
const archiveMutation = useArchiveRun(params.id);
|
||||
const unarchiveMutation = useUnarchiveRun(params.id);
|
||||
const { push, dismiss } = useToast();
|
||||
const demoMode = useDemoMode();
|
||||
const tabs = allTabs.filter((t) => !t.demoOnly || demoMode);
|
||||
const lifecycleToastStateRef = useRef<LifecycleToastState>(INITIAL_LIFECYCLE_TOAST_STATE);
|
||||
|
||||
|
|
@ -170,6 +178,8 @@ export default function RunDetail({ params }: { params: { id: string } }) {
|
|||
const cancelPending = cancelMutation.isMutating;
|
||||
const archivePending = archiveMutation.isMutating;
|
||||
const unarchivePending = unarchiveMutation.isMutating;
|
||||
const sandboxWorkingDirectory =
|
||||
run.sandboxWorkingDirectory ?? sandboxWorkingDirectoryFromState(runStateQuery.data);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -200,6 +210,22 @@ export default function RunDetail({ params }: { params: { id: string } }) {
|
|||
<span className="font-mono text-xs text-fg-muted">{run.elapsed}</span>
|
||||
)}
|
||||
</div>
|
||||
{(run.sourceDirectory || sandboxWorkingDirectory) && (
|
||||
<div className="mt-2 flex min-w-0 flex-wrap gap-x-4 gap-y-1 text-xs text-fg-muted">
|
||||
{run.sourceDirectory && (
|
||||
<span className="min-w-0 max-w-full">
|
||||
<span className="text-fg-3">Source</span>{" "}
|
||||
<code className="font-mono break-all">{run.sourceDirectory}</code>
|
||||
</span>
|
||||
)}
|
||||
{sandboxWorkingDirectory && (
|
||||
<span className="min-w-0 max-w-full">
|
||||
<span className="text-fg-3">Sandbox</span>{" "}
|
||||
<code className="font-mono break-all">{sandboxWorkingDirectory}</code>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 flex-wrap items-center justify-end gap-2">
|
||||
|
|
|
|||
|
|
@ -34,6 +34,54 @@ Every serialized run event envelope, whether streamed over SSE, returned by `fab
|
|||
|
||||
## Run events
|
||||
|
||||
### `run.created`
|
||||
|
||||
Emitted when the run record is created.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "...", "ts": "...", "run_id": "...",
|
||||
"event": "run.created",
|
||||
"properties": {
|
||||
"workflow_slug": "my-workflow",
|
||||
"run_dir": "/home/user/.fabro/storage/scratch/20260428-01JQXYZ",
|
||||
"source_directory": "/home/user/src/my-project",
|
||||
"repo_origin_url": "https://github.com/acme/my-project.git",
|
||||
"base_branch": "main",
|
||||
"pre_run_git": {
|
||||
"display_base_sha": "abc123",
|
||||
"local_dirty": "clean",
|
||||
"push_outcome": {
|
||||
"type": "succeeded",
|
||||
"remote": "origin",
|
||||
"branch": "main"
|
||||
}
|
||||
},
|
||||
"fork_source_ref": null,
|
||||
"checkpoints_disabled": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `settings` | object | Workflow settings snapshot |
|
||||
| `graph` | object | Parsed workflow graph |
|
||||
| `workflow_source` | string? | Workflow source text |
|
||||
| `workflow_config` | string? | Workflow config text |
|
||||
| `labels` | object | Run labels |
|
||||
| `run_dir` | string | Local scratch directory for the run |
|
||||
| `source_directory` | string? | Submitter-side source directory |
|
||||
| `repo_origin_url` | string? | Normalized repository origin URL used by clone-based sandboxes |
|
||||
| `base_branch` | string? | Submitter-side base branch |
|
||||
| `workflow_slug` | string? | Workflow slug |
|
||||
| `db_prefix` | string? | Store prefix used for the run |
|
||||
| `provenance` | object? | Actor and request provenance |
|
||||
| `manifest_blob` | string? | Blob id for the submitted manifest |
|
||||
| `pre_run_git` | object? | Submitter-side pre-run git context and push outcome |
|
||||
| `fork_source_ref` | object? | Source run/checkpoint reference when this run was forked |
|
||||
| `checkpoints_disabled` | boolean | Whether checkpointing was intentionally disabled |
|
||||
|
||||
### `run.started`
|
||||
|
||||
Emitted when the workflow run begins.
|
||||
|
|
@ -1449,7 +1497,10 @@ Emitted after the engine completes sandbox initialization (distinct from `sandbo
|
|||
"properties": {
|
||||
"working_directory": "/workspace/my-project",
|
||||
"provider": "daytona",
|
||||
"identifier": "sandbox-123"
|
||||
"identifier": "sandbox-123",
|
||||
"repo_cloned": true,
|
||||
"clone_origin_url": "https://github.com/acme/my-project.git",
|
||||
"clone_branch": "main"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1459,6 +1510,9 @@ Emitted after the engine completes sandbox initialization (distinct from `sandbo
|
|||
| `working_directory` | string | Working directory inside sandbox |
|
||||
| `provider` | string | Sandbox provider |
|
||||
| `identifier` | string? | Provider-specific sandbox identifier |
|
||||
| `repo_cloned` | boolean? | Whether the provider cloned a repository into the sandbox |
|
||||
| `clone_origin_url` | string? | Repository URL cloned into the sandbox, with credentials removed |
|
||||
| `clone_branch` | string? | Branch requested for the sandbox clone |
|
||||
|
||||
### `sandbox.cleanup.started`
|
||||
|
||||
|
|
|
|||
|
|
@ -4697,9 +4697,6 @@ components:
|
|||
target:
|
||||
type: ["string", "null"]
|
||||
description: Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
|
||||
push:
|
||||
type: ["boolean", "null"]
|
||||
description: Whether to push the new run branches. Defaults to true.
|
||||
|
||||
ForkResponse:
|
||||
description: Response returned after creating a forked run.
|
||||
|
|
@ -4723,9 +4720,6 @@ components:
|
|||
target:
|
||||
type: ["string", "null"]
|
||||
description: Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
|
||||
push:
|
||||
type: ["boolean", "null"]
|
||||
description: Whether to push the new run branches. Defaults to true.
|
||||
|
||||
RewindResponse:
|
||||
description: Response returned after rewind creates a new run.
|
||||
|
|
@ -5195,13 +5189,15 @@ components:
|
|||
RunSandbox:
|
||||
description: Sandbox environment for a run.
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type: ["string", "null"]
|
||||
description: Sandbox identifier.
|
||||
example: sb-a1b2c3d4
|
||||
working_directory:
|
||||
type: ["string", "null"]
|
||||
description: Path where the run executed inside the sandbox.
|
||||
example: /workspace
|
||||
resources:
|
||||
$ref: "#/components/schemas/SandboxResources"
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ Fabro disables Git commit and tag signing for checkpoint commits created inside
|
|||
|
||||
### Metadata branch
|
||||
|
||||
The metadata branch (`fabro/meta/{run_id}`) is an orphan branch that stores structured run data using Git's object storage directly (via `git2`). It is initialized at run start with:
|
||||
The metadata branch (`fabro/meta/{run_id}`) is an orphan branch that stores structured run data using Git's object storage directly. Fabro writes it from inside the sandbox with Git plumbing commands, without checking out a metadata worktree. It is initialized at run start with:
|
||||
|
||||
- **`run.json`** — Current projection snapshot: run spec, start/status records, current checkpoint, conclusion, sandbox, retro state, and other run-level metadata
|
||||
- **`graph.fabro`** — Workflow source for the run
|
||||
|
|
@ -75,7 +75,7 @@ The durable run store also keeps the current checkpoint so `resume`, `inspect`,
|
|||
|
||||
## Worktrees
|
||||
|
||||
Fabro uses Git worktrees to isolate workflow runs from your working directory. When a run starts in a clean Git repository:
|
||||
Fabro uses Git worktrees to isolate workflow runs from your working directory. When a local run starts in a Git repository:
|
||||
|
||||
1. Fabro records the current HEAD as the **base SHA**
|
||||
2. Creates a new branch `fabro/run/{run_id}` at that SHA
|
||||
|
|
@ -85,10 +85,10 @@ Fabro uses Git worktrees to isolate workflow runs from your working directory. W
|
|||
This means your original working directory stays untouched while the agent makes changes in the worktree. When the run completes, Fabro removes the worktree and restores your original directory.
|
||||
|
||||
<Note>
|
||||
If the working directory has uncommitted changes, Fabro skips worktree setup and runs in place, logging a warning. Git checkpointing is disabled in this case.
|
||||
If the working directory has uncommitted changes, the worktree starts from committed `HEAD` and those uncommitted changes are not included. Fabro logs a warning so you can commit, stash, or run explicitly in place when that is what you want.
|
||||
</Note>
|
||||
|
||||
For Daytona sandboxes, the worktree is created inside the remote sandbox instead. The metadata branch is still written to the host repository so that runs can be resumed locally. Both the run branch and the metadata branch are pushed to origin after each checkpoint — the run branch is pushed from the sandbox, while the metadata branch is pushed from the host using a GitHub App installation token.
|
||||
For Docker and Daytona sandboxes, the repository is cloned into the sandbox and checkpoint Git operations run there. Both the run branch and metadata branch are pushed to origin from the sandbox after each checkpoint when pushing is configured.
|
||||
|
||||
## Resuming a run
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ fabro resume <NEW_RUN_ID>
|
|||
|
||||
Use **rewind** when a terminal run should be abandoned and replaced from an earlier point. Use **fork** when you want to try a different approach while keeping the original run as a reference.
|
||||
|
||||
`fabro rewind --list`, `fabro fork --list`, `fabro rewind`, and `fabro fork` are server-backed. The server must be able to read the run's recorded `working_directory`; otherwise these commands fail. Timeline listing reads the metadata branch as-is and does not rebuild a missing metadata branch, so a missing branch appears as an empty timeline.
|
||||
`fabro rewind --list`, `fabro fork --list`, `fabro rewind`, and `fabro fork` are server-backed. Timeline listing reads durable run-store checkpoints; it does not rebuild missing metadata branches.
|
||||
|
||||
See [`fabro fork`](/reference/cli#fabro-fork) for the full command reference.
|
||||
|
||||
|
|
@ -183,11 +183,12 @@ See [`fabro fork`](/reference/cli#fabro-fork) for the full command reference.
|
|||
|
||||
Git checkpointing activates automatically when:
|
||||
|
||||
- The working directory is a clean Git repository (local and Docker sandboxes)
|
||||
- The sandbox is Daytona (metadata branch on the host, commits inside the sandbox)
|
||||
- The run uses a Git repository and checkpointing has not been explicitly disabled
|
||||
- Local runs can create a Git worktree under the run scratch directory
|
||||
- Docker or Daytona can clone the configured GitHub origin into the sandbox
|
||||
|
||||
It is skipped when:
|
||||
|
||||
- The working directory has uncommitted changes
|
||||
- The working directory is not a Git repository
|
||||
- The run uses `--dry-run`
|
||||
- The run is explicitly started in place with checkpointing disabled
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ The local sandbox offers no isolation. Agents can read and modify any file on th
|
|||
|
||||
## Docker
|
||||
|
||||
The Docker sandbox runs all tool operations inside a Docker container. The host working directory is bind-mounted into the container, so file changes are visible on both sides.
|
||||
The Docker sandbox runs all tool operations inside a Docker container. Docker runs use a provider-owned workspace in the container; when the run has a GitHub origin, Fabro clones that repository into the workspace.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
|
@ -76,7 +76,6 @@ The Docker sandbox is configured through the `DockerSandboxConfig`:
|
|||
|---|---|---|
|
||||
| `image` | `fabro-agent:latest` | Docker image to use |
|
||||
| `network_mode` | `bridge` | Docker network mode |
|
||||
| `extra_mounts` | `[]` | Additional `host:container` bind mounts |
|
||||
| `memory_limit` | unlimited | Memory limit in bytes |
|
||||
| `cpu_quota` | unlimited | CPU quota (microseconds per 100ms period) |
|
||||
| `auto_pull` | `true` | Pull the image if not found locally |
|
||||
|
|
@ -199,7 +198,7 @@ Each provider offers a different level of filesystem isolation:
|
|||
| Provider | Isolation | What agents can access |
|
||||
|---|---|---|
|
||||
| `local` | None | The entire host filesystem. Agents can read and modify any file. |
|
||||
| `docker` | Container-level | Only the bind-mounted working directory (`/workspace` by default) and whatever is in the container image. Host files outside the mount are inaccessible. |
|
||||
| `docker` | Container-level | The provider-owned workspace (`/workspace` by default) and whatever is in the container image. Host files are not bind-mounted into the container. |
|
||||
| `daytona` | Full machine | A cloud VM with the repository cloned into `/home/daytona/workspace`. The host filesystem is completely inaccessible. |
|
||||
|
||||
For `local`, Fabro filters sensitive environment variables (those ending in `_API_KEY`, `_SECRET`, `_TOKEN`, `_PASSWORD`, or `_CREDENTIAL`) but does not restrict file access. Use `docker` or `daytona` when running untrusted workflows.
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ worktree_mode = "always"
|
|||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| `worktree_mode` | When to create a git worktree for the run: `always`, `clean` (default — only when the working tree is clean), `dirty` (also when dirty), or `never`. |
|
||||
| `worktree_mode` | Legacy compatibility setting. Local Git runs use a run-scoped worktree by default; the explicit in-place/no-checkpoints path is the supported opt-out. |
|
||||
|
||||
#### `[run.sandbox.env]`
|
||||
|
||||
|
|
|
|||
|
|
@ -382,7 +382,6 @@ fabro fork [OPTIONS] <RUN_ID> [TARGET]
|
|||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `--list` | Show the checkpoint timeline instead of forking |
|
||||
| `--no-push` | Skip pushing new branches to the remote |
|
||||
| `--server <server>` | Fabro server target: http(s) URL or absolute Unix socket path |
|
||||
|
||||
### `fabro graph`
|
||||
|
|
@ -770,7 +769,6 @@ fabro rewind [OPTIONS] <RUN_ID> [TARGET]
|
|||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `--list` | Show the checkpoint timeline instead of rewinding |
|
||||
| `--no-push` | Skip force-pushing rewound refs to the remote |
|
||||
| `--server <server>` | Fabro server target: http(s) URL or absolute Unix socket path |
|
||||
|
||||
### `fabro rm`
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ Large durable values, event streams, checkpoints, diffs, conclusions, and retros
|
|||
|
||||
## Reconstructed and export-only layouts
|
||||
|
||||
Reconstructed metadata branches and `fabro dump` exports now use the same core layout:
|
||||
Metadata branch snapshots and `fabro dump` exports now use the same core layout:
|
||||
|
||||
- `run.json` for the current projection snapshot, including the current checkpoint
|
||||
- `graph.fabro` for workflow source
|
||||
|
|
|
|||
|
|
@ -641,10 +641,6 @@ pub(crate) struct RewindArgs {
|
|||
/// Show the checkpoint timeline instead of rewinding
|
||||
#[arg(long)]
|
||||
pub(crate) list: bool,
|
||||
|
||||
/// Skip force-pushing rewound refs to the remote
|
||||
#[arg(long)]
|
||||
pub(crate) no_push: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
|
|
@ -662,10 +658,6 @@ pub(crate) struct ForkArgs {
|
|||
/// Show the checkpoint timeline instead of forking
|
||||
#[arg(long)]
|
||||
pub(crate) list: bool,
|
||||
|
||||
/// Skip pushing new branches to the remote
|
||||
#[arg(long)]
|
||||
pub(crate) no_push: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ pub(crate) async fn run(args: &ForkArgs, styles: &Styles, base_ctx: &CommandCont
|
|||
let response = client
|
||||
.fork_run(&run_id, ForkRequest {
|
||||
target: args.target.clone(),
|
||||
push: Some(!args.no_push),
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ pub(crate) async fn run(
|
|||
let result = client
|
||||
.rewind_run(&run_id, RewindRequest {
|
||||
target: Some(target),
|
||||
push: Some(!args.no_push),
|
||||
})
|
||||
.await?;
|
||||
let response = result.response;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ fn help() {
|
|||
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
|
||||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--list Show the checkpoint timeline instead of forking
|
||||
--no-push Skip pushing new branches to the remote
|
||||
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
||||
|
|
@ -59,7 +58,7 @@ fn fork_latest_prints_new_run_and_resume_hint() {
|
|||
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&setup.repo_dir);
|
||||
cmd.args(["fork", &setup.run.run_id, "--no-push"]);
|
||||
cmd.args(["fork", &setup.run.run_id]);
|
||||
|
||||
let (snapshot, output) = run_and_format(&mut cmd, &git_filters(&context));
|
||||
assert_snapshot!(snapshot, @"
|
||||
|
|
@ -87,7 +86,7 @@ fn fork_from_earlier_checkpoint_uses_expected_sha() {
|
|||
let output = context
|
||||
.command()
|
||||
.current_dir(&setup.repo_dir)
|
||||
.args(["fork", &setup.run.run_id, "@2", "--json", "--no-push"])
|
||||
.args(["fork", &setup.run.run_id, "@2", "--json"])
|
||||
.output()
|
||||
.expect("fork should execute");
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ fn rewind_replacement_run_id(
|
|||
let rewind = context
|
||||
.command()
|
||||
.current_dir(&setup.repo_dir)
|
||||
.args(["rewind", &setup.run.run_id, "@2", "--no-push", "--json"])
|
||||
.args(["rewind", &setup.run.run_id, "@2", "--json"])
|
||||
.output()
|
||||
.expect("rewind should execute");
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ fn help() {
|
|||
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
|
||||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||||
--list Show the checkpoint timeline instead of rewinding
|
||||
--no-push Skip force-pushing rewound refs to the remote
|
||||
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||||
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
||||
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
||||
|
|
@ -84,7 +83,7 @@ fn rewind_target_updates_metadata_and_resume_hint() {
|
|||
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&setup.repo_dir);
|
||||
cmd.args(["rewind", &setup.run.run_id, "@2", "--no-push"]);
|
||||
cmd.args(["rewind", &setup.run.run_id, "@2"]);
|
||||
|
||||
let (snapshot, output) = run_and_format(&mut cmd, &git_filters(&context));
|
||||
assert_snapshot!(snapshot, @"
|
||||
|
|
@ -129,7 +128,7 @@ fn rewind_archives_source_and_records_superseded_by() {
|
|||
|
||||
let mut cmd = context.command();
|
||||
cmd.current_dir(&setup.repo_dir);
|
||||
cmd.args(["rewind", &setup.run.run_id, "@2", "--no-push"]);
|
||||
cmd.args(["rewind", &setup.run.run_id, "@2"]);
|
||||
let output = cmd.output().expect("rewind should execute");
|
||||
assert!(
|
||||
output.status.success(),
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ fn fork_run_json(context: &TestContext, repo_dir: &Path, source_run_id: &str) ->
|
|||
let output = context
|
||||
.command()
|
||||
.current_dir(repo_dir)
|
||||
.args(["fork", source_run_id, "--json", "--no-push"])
|
||||
.args(["fork", source_run_id, "--json"])
|
||||
.timeout(std::time::Duration::from_secs(15))
|
||||
.output()
|
||||
.expect("fork command should execute");
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ pub enum SandboxSpec {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum WorkdirStrategy {
|
||||
LocalDirectory,
|
||||
LocalWorktree,
|
||||
|
|
@ -132,40 +132,12 @@ impl SandboxSpec {
|
|||
|
||||
pub fn workdir_strategy(
|
||||
&self,
|
||||
worktree_mode: WorktreeMode,
|
||||
git_is_clean: bool,
|
||||
checkpoint_present: bool,
|
||||
_worktree_mode: WorktreeMode,
|
||||
_git_is_clean: bool,
|
||||
_checkpoint_present: bool,
|
||||
) -> WorkdirStrategy {
|
||||
if checkpoint_present {
|
||||
return match self {
|
||||
Self::Local { .. } => WorkdirStrategy::LocalDirectory,
|
||||
#[allow(
|
||||
unreachable_patterns,
|
||||
reason = "Feature-gated variants make this fallback arm reachable on some builds."
|
||||
)]
|
||||
_ => WorkdirStrategy::Cloud,
|
||||
};
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::Local { .. } => match worktree_mode {
|
||||
WorktreeMode::Always => WorkdirStrategy::LocalWorktree,
|
||||
WorktreeMode::Clean => {
|
||||
if git_is_clean {
|
||||
WorkdirStrategy::LocalWorktree
|
||||
} else {
|
||||
WorkdirStrategy::LocalDirectory
|
||||
}
|
||||
}
|
||||
WorktreeMode::Dirty => {
|
||||
if git_is_clean {
|
||||
WorkdirStrategy::LocalDirectory
|
||||
} else {
|
||||
WorkdirStrategy::LocalWorktree
|
||||
}
|
||||
}
|
||||
WorktreeMode::Never => WorkdirStrategy::LocalDirectory,
|
||||
},
|
||||
Self::Local { .. } => WorkdirStrategy::LocalWorktree,
|
||||
#[allow(
|
||||
unreachable_patterns,
|
||||
reason = "Feature-gated variants make this fallback arm reachable on some builds."
|
||||
|
|
@ -238,3 +210,29 @@ impl SandboxSpec {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn local_runs_use_worktrees_for_all_worktree_modes() {
|
||||
let spec = SandboxSpec::Local {
|
||||
working_directory: PathBuf::from("/repo"),
|
||||
};
|
||||
|
||||
for mode in [
|
||||
WorktreeMode::Always,
|
||||
WorktreeMode::Clean,
|
||||
WorktreeMode::Dirty,
|
||||
WorktreeMode::Never,
|
||||
] {
|
||||
for git_is_clean in [true, false] {
|
||||
assert_eq!(
|
||||
spec.workdir_strategy(mode, git_is_clean, false),
|
||||
WorkdirStrategy::LocalWorktree
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,8 @@ pub(crate) async fn resolve_run(
|
|||
|run| run.workflow_slug.clone(),
|
||||
|run| run.workflow_name.clone(),
|
||||
|run| run.created_at,
|
||||
|run| run.created_at.to_rfc3339(),
|
||||
|run| run.repo_origin_url.clone(),
|
||||
) {
|
||||
Ok(run) => (StatusCode::OK, Json(run.clone())).into_response(),
|
||||
Err(ResolveRunError::InvalidSelector | ResolveRunError::AmbiguousPrefix { .. }) => {
|
||||
|
|
@ -944,8 +946,9 @@ mod runs {
|
|||
|
||||
fn sandbox(id: &str, cpu: i64, memory: i64) -> RunSandbox {
|
||||
RunSandbox {
|
||||
id: id.into(),
|
||||
resources: Some(SandboxResources { cpu, memory }),
|
||||
id: Some(id.to_string()),
|
||||
working_directory: Some("/workspace".to_string()),
|
||||
resources: Some(SandboxResources { cpu, memory }),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,19 +30,33 @@ impl fmt::Display for ResolveRunError {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_run_by_selector<'a, T, FRunId, FWorkflowSlug, FWorkflowName, FCreatedAt, K>(
|
||||
pub(crate) fn resolve_run_by_selector<
|
||||
'a,
|
||||
T,
|
||||
FRunId,
|
||||
FWorkflowSlug,
|
||||
FWorkflowName,
|
||||
FCreatedAt,
|
||||
FCreatedAtLabel,
|
||||
FRepoOriginUrl,
|
||||
K,
|
||||
>(
|
||||
runs: &'a [T],
|
||||
selector: &str,
|
||||
run_id: FRunId,
|
||||
workflow_slug: FWorkflowSlug,
|
||||
workflow_name: FWorkflowName,
|
||||
created_at: FCreatedAt,
|
||||
created_at_label: FCreatedAtLabel,
|
||||
repo_origin_url: FRepoOriginUrl,
|
||||
) -> Result<&'a T, ResolveRunError>
|
||||
where
|
||||
FRunId: Fn(&T) -> String,
|
||||
FWorkflowSlug: Fn(&T) -> Option<String>,
|
||||
FWorkflowName: Fn(&T) -> Option<String>,
|
||||
FCreatedAt: Fn(&T) -> K,
|
||||
FCreatedAtLabel: Fn(&T) -> String,
|
||||
FRepoOriginUrl: Fn(&T) -> Option<String>,
|
||||
K: Ord,
|
||||
{
|
||||
let selector = selector.trim();
|
||||
|
|
@ -59,7 +73,22 @@ where
|
|||
count if count > 1 => {
|
||||
return Err(ResolveRunError::AmbiguousPrefix {
|
||||
selector: selector.to_string(),
|
||||
matches: id_matches.iter().map(|run| run_id(run)).collect(),
|
||||
matches: id_matches
|
||||
.iter()
|
||||
.map(|run| {
|
||||
let workflow = workflow_name(run)
|
||||
.or_else(|| workflow_slug(run))
|
||||
.unwrap_or_else(|| "-".to_string());
|
||||
let origin = repo_origin_url(run).unwrap_or_else(|| "-".to_string());
|
||||
format!(
|
||||
"{} created_at={} workflow={} origin={}",
|
||||
run_id(run),
|
||||
created_at_label(run),
|
||||
workflow,
|
||||
origin
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -2902,14 +2902,18 @@ async fn board_run_metadata(
|
|||
}
|
||||
|
||||
if let Some(sandbox) = run_state.sandbox {
|
||||
let mut sandbox_metadata = serde_json::Map::new();
|
||||
sandbox_metadata.insert(
|
||||
"working_directory".to_string(),
|
||||
serde_json::json!(sandbox.working_directory),
|
||||
);
|
||||
if let Some(identifier) = sandbox.identifier {
|
||||
metadata.insert(
|
||||
"sandbox".to_string(),
|
||||
serde_json::json!({
|
||||
"id": identifier,
|
||||
}),
|
||||
);
|
||||
sandbox_metadata.insert("id".to_string(), serde_json::json!(identifier));
|
||||
}
|
||||
metadata.insert(
|
||||
"sandbox".to_string(),
|
||||
serde_json::Value::Object(sandbox_metadata),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((_, record)) =
|
||||
|
|
@ -3060,6 +3064,8 @@ async fn resolve_run(
|
|||
|run| run.workflow_slug.clone(),
|
||||
|run| run.workflow_name.clone(),
|
||||
|run| run.run_id.created_at(),
|
||||
|run| run.run_id.created_at().to_rfc3339(),
|
||||
|run| run.repo_origin_url.clone(),
|
||||
) {
|
||||
Ok(run) => (StatusCode::OK, Json(run.clone())).into_response(),
|
||||
Err(err @ (ResolveRunError::InvalidSelector | ResolveRunError::AmbiguousPrefix { .. })) => {
|
||||
|
|
@ -7158,11 +7164,7 @@ async fn rewind_run(
|
|||
Ok(target) => target,
|
||||
Err(err) => return err.into_response(),
|
||||
};
|
||||
let input = operations::RewindInput {
|
||||
run_id: id,
|
||||
target,
|
||||
push: request.push.unwrap_or(true),
|
||||
};
|
||||
let input = operations::RewindInput { run_id: id, target };
|
||||
match Box::pin(operations::rewind(
|
||||
&state.store,
|
||||
&input,
|
||||
|
|
@ -7226,7 +7228,6 @@ async fn fork_run(
|
|||
let input = operations::ForkRunInput {
|
||||
source_run_id: id,
|
||||
target,
|
||||
push: request.push.unwrap_or(true),
|
||||
};
|
||||
match Box::pin(operations::fork_run(&state.store, &input)).await {
|
||||
Ok(outcome) => (
|
||||
|
|
@ -10150,6 +10151,18 @@ slug = "fabro"
|
|||
detail.contains(&run_id_b),
|
||||
"detail should mention second run: {detail}"
|
||||
);
|
||||
assert!(
|
||||
detail.contains("created_at="),
|
||||
"detail should include creation timestamps: {detail}"
|
||||
);
|
||||
assert!(
|
||||
detail.contains("workflow="),
|
||||
"detail should include workflow names: {detail}"
|
||||
);
|
||||
assert!(
|
||||
detail.contains("origin="),
|
||||
"detail should include origin URLs: {detail}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -13959,6 +13972,10 @@ provider = "local"
|
|||
|
||||
assert_eq!(item["pull_request"]["number"].as_u64(), Some(42));
|
||||
assert_eq!(item["sandbox"]["id"].as_str(), Some("sb-test"));
|
||||
assert_eq!(
|
||||
item["sandbox"]["working_directory"].as_str(),
|
||||
Some("/sandbox/workdir")
|
||||
);
|
||||
assert_eq!(item["question"]["text"].as_str(), Some("Ship it?"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use crate::records::{Checkpoint, RunSpec};
|
|||
pub struct ForkRunInput {
|
||||
pub source_run_id: RunId,
|
||||
pub target: Option<ForkTarget>,
|
||||
pub push: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
@ -391,7 +390,6 @@ mod tests {
|
|||
let outcome = fork_run(&store, &ForkRunInput {
|
||||
source_run_id,
|
||||
target: None,
|
||||
push: false,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use crate::event::{self, Event};
|
|||
pub struct RewindInput {
|
||||
pub run_id: RunId,
|
||||
pub target: Option<ForkTarget>,
|
||||
pub push: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
@ -57,7 +56,6 @@ pub async fn rewind(
|
|||
let forked = Box::pin(fork::fork_run(store, &ForkRunInput {
|
||||
source_run_id: input.run_id,
|
||||
target: input.target.clone(),
|
||||
push: input.push,
|
||||
}))
|
||||
.await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,16 +31,55 @@ use crate::git::RUN_BRANCH_PREFIX;
|
|||
use crate::handler::llm::{AgentApiBackend, AgentCliBackend, BackendRouter};
|
||||
use crate::handler::{HandlerRegistry, default_registry, sandbox_cancel_token};
|
||||
use crate::run_options::{GitCheckpointOptions, RunOptions};
|
||||
use crate::sandbox_git::GIT_REMOTE;
|
||||
use crate::sandbox_metadata::{SandboxGitRuntime, metadata_branch_name};
|
||||
use crate::services::{EngineServices, RunServices};
|
||||
|
||||
struct WorktreePlan {
|
||||
branch_name: String,
|
||||
base_sha: String,
|
||||
base_sha: Option<String>,
|
||||
worktree_path: PathBuf,
|
||||
skip_branch_creation: bool,
|
||||
}
|
||||
|
||||
async fn resolve_worktree_base_sha(
|
||||
sandbox: &dyn Sandbox,
|
||||
plan: &WorktreePlan,
|
||||
) -> Result<String, Error> {
|
||||
if let Some(base_sha) = plan.base_sha.as_ref() {
|
||||
return Ok(base_sha.clone());
|
||||
}
|
||||
|
||||
let result = sandbox
|
||||
.exec_command(
|
||||
&format!("{GIT_REMOTE} rev-parse HEAD"),
|
||||
10_000,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| Error::engine(format!("git rev-parse HEAD failed: {err}")))?;
|
||||
if result.exit_code != 0 {
|
||||
let output = result.stderr.trim();
|
||||
let output = if output.is_empty() {
|
||||
result.stdout.trim()
|
||||
} else {
|
||||
output
|
||||
};
|
||||
return Err(Error::engine(format!(
|
||||
"git rev-parse HEAD failed (exit {}): {}",
|
||||
result.exit_code, output
|
||||
)));
|
||||
}
|
||||
|
||||
let base_sha = result.stdout.trim();
|
||||
if base_sha.is_empty() {
|
||||
return Err(Error::engine("git rev-parse HEAD returned no commit sha"));
|
||||
}
|
||||
Ok(base_sha.to_string())
|
||||
}
|
||||
|
||||
async fn run_hooks(
|
||||
hook_runner: Option<&HookRunner>,
|
||||
hook_context: &HookContext,
|
||||
|
|
@ -69,9 +108,9 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option<WorktreePlan> {
|
|||
let base_sha = fork_source.checkpoint_sha.clone();
|
||||
options.run_options.display_base_sha = Some(base_sha.clone());
|
||||
return Some(WorktreePlan {
|
||||
branch_name: format!("{RUN_BRANCH_PREFIX}{}", options.run_id),
|
||||
base_sha,
|
||||
worktree_path: RunScratch::new(&options.run_options.run_dir).worktree_dir(),
|
||||
branch_name: format!("{RUN_BRANCH_PREFIX}{}", options.run_id),
|
||||
base_sha: Some(base_sha),
|
||||
worktree_path: RunScratch::new(&options.run_options.run_dir).worktree_dir(),
|
||||
skip_branch_creation: false,
|
||||
});
|
||||
}
|
||||
|
|
@ -83,7 +122,7 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option<WorktreePlan> {
|
|||
options.run_options.display_base_sha = Some(base_sha.clone());
|
||||
return Some(WorktreePlan {
|
||||
branch_name: run_branch.clone(),
|
||||
base_sha: base_sha.clone(),
|
||||
base_sha: Some(base_sha.clone()),
|
||||
worktree_path: RunScratch::new(&options.run_options.run_dir)
|
||||
.worktree_dir(),
|
||||
skip_branch_creation: true,
|
||||
|
|
@ -123,21 +162,19 @@ fn resolve_worktree_plan(options: &mut InitOptions) -> Option<WorktreePlan> {
|
|||
if let Some(fork_source) = options.run_options.fork_source_ref.as_ref() {
|
||||
(
|
||||
format!("{RUN_BRANCH_PREFIX}{}", options.run_id),
|
||||
fork_source.checkpoint_sha.clone(),
|
||||
Some(fork_source.checkpoint_sha.clone()),
|
||||
)
|
||||
} else {
|
||||
let Some(base_sha) = options
|
||||
.run_options
|
||||
.pre_run_git
|
||||
.as_ref()
|
||||
.and_then(|git| git.display_base_sha.clone())
|
||||
else {
|
||||
options.run_options.display_base_sha = None;
|
||||
return None;
|
||||
};
|
||||
(format!("{RUN_BRANCH_PREFIX}{}", options.run_id), base_sha)
|
||||
(
|
||||
format!("{RUN_BRANCH_PREFIX}{}", options.run_id),
|
||||
options
|
||||
.run_options
|
||||
.pre_run_git
|
||||
.as_ref()
|
||||
.and_then(|git| git.display_base_sha.clone()),
|
||||
)
|
||||
};
|
||||
options.run_options.display_base_sha = Some(base_sha.clone());
|
||||
options.run_options.display_base_sha = base_sha.clone();
|
||||
Some(WorktreePlan {
|
||||
branch_name,
|
||||
base_sha,
|
||||
|
|
@ -440,13 +477,6 @@ pub async fn initialize(
|
|||
resolve_devcontainer(&mut options).await?;
|
||||
|
||||
let worktree_plan = resolve_worktree_plan(&mut options);
|
||||
if let Some(plan) = worktree_plan.as_ref() {
|
||||
options.run_options.git = Some(GitCheckpointOptions {
|
||||
base_sha: Some(plan.base_sha.clone()),
|
||||
run_branch: Some(plan.branch_name.clone()),
|
||||
meta_branch: Some(metadata_branch_name(&options.run_id.to_string())),
|
||||
});
|
||||
}
|
||||
|
||||
let sandbox_event_callback: SandboxEventCallback = {
|
||||
let emitter = Arc::clone(&options.emitter);
|
||||
|
|
@ -465,12 +495,19 @@ pub async fn initialize(
|
|||
.ensure_git_available(&*inner)
|
||||
.await
|
||||
.map_err(|err| Error::engine(format!("sandbox git unavailable: {err}")))?;
|
||||
let base_sha = resolve_worktree_base_sha(&*inner, plan).await?;
|
||||
options.run_options.display_base_sha = Some(base_sha.clone());
|
||||
options.run_options.git = Some(GitCheckpointOptions {
|
||||
base_sha: Some(base_sha.clone()),
|
||||
run_branch: Some(plan.branch_name.clone()),
|
||||
meta_branch: Some(metadata_branch_name(&options.run_id.to_string())),
|
||||
});
|
||||
let mut worktree = WorktreeSandbox::new(inner, WorktreeOptions {
|
||||
branch_name: plan.branch_name.clone(),
|
||||
base_sha: plan.base_sha.clone(),
|
||||
worktree_path: plan.worktree_path.to_string_lossy().into_owned(),
|
||||
branch_name: plan.branch_name.clone(),
|
||||
base_sha,
|
||||
worktree_path: plan.worktree_path.to_string_lossy().into_owned(),
|
||||
skip_branch_creation: plan.skip_branch_creation,
|
||||
setup_intent: Some(git_setup_intent(&options.run_options)),
|
||||
setup_intent: Some(git_setup_intent(&options.run_options)),
|
||||
});
|
||||
worktree.set_event_callback(Arc::clone(&options.emitter).worktree_callback());
|
||||
match worktree.initialize().await {
|
||||
|
|
@ -715,6 +752,7 @@ mod tests {
|
|||
use fabro_graphviz::graph::{AttrValue, Edge, Graph, Node};
|
||||
use fabro_interview::AutoApproveInterviewer;
|
||||
use fabro_sandbox::SandboxSpec;
|
||||
use fabro_sandbox::config::WorktreeMode;
|
||||
use fabro_store::Database;
|
||||
use fabro_types::{RunId, WorkflowSettings, fixtures};
|
||||
use fabro_vault::{SecretType, Vault};
|
||||
|
|
@ -839,6 +877,64 @@ mod tests {
|
|||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_worktree_plan_uses_local_worktree_without_pre_run_git_context() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let run_dir = temp.path().join("run");
|
||||
std::fs::create_dir_all(&run_dir).unwrap();
|
||||
let store = memory_store();
|
||||
let mut options = InitOptions {
|
||||
run_id: test_run_id(),
|
||||
run_store: {
|
||||
let inner = store.create_run(&test_run_id()).await.unwrap();
|
||||
inner.into()
|
||||
},
|
||||
dry_run: false,
|
||||
emitter: Arc::new(crate::event::Emitter::new(test_run_id())),
|
||||
sandbox: SandboxSpec::Local {
|
||||
working_directory: std::env::current_dir().unwrap(),
|
||||
},
|
||||
llm: LlmSpec {
|
||||
model: "test-model".to_string(),
|
||||
provider: fabro_llm::Provider::Anthropic,
|
||||
fallback_chain: Vec::new(),
|
||||
mcp_servers: Vec::new(),
|
||||
dry_run: true,
|
||||
},
|
||||
interviewer: Arc::new(AutoApproveInterviewer),
|
||||
lifecycle: crate::run_options::LifecycleOptions {
|
||||
setup_commands: vec![],
|
||||
setup_command_timeout_ms: 1_000,
|
||||
devcontainer_phases: vec![],
|
||||
},
|
||||
run_options: test_settings(&run_dir),
|
||||
workflow_path: None,
|
||||
workflow_bundle: None,
|
||||
hooks: fabro_hooks::HookSettings { hooks: vec![] },
|
||||
sandbox_env: SandboxEnvSpec {
|
||||
devcontainer_env: HashMap::new(),
|
||||
toml_env: HashMap::new(),
|
||||
github_permissions: None,
|
||||
origin_url: None,
|
||||
},
|
||||
vault: None,
|
||||
devcontainer: None,
|
||||
git: None,
|
||||
worktree_mode: Some(WorktreeMode::Always),
|
||||
run_control: None,
|
||||
registry_override: None,
|
||||
artifact_sink: None,
|
||||
checkpoint: None,
|
||||
seed_context: None,
|
||||
};
|
||||
|
||||
let plan = resolve_worktree_plan(&mut options);
|
||||
|
||||
assert!(plan.is_some());
|
||||
assert!(options.run_options.display_base_sha.is_none());
|
||||
assert!(options.run_options.git.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn initialize_prepares_sandbox_and_uses_persisted_run_dir() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
|
|
@ -67,7 +67,7 @@ import type { TimelineEntryResponse } from '../models';
|
|||
export const RunsApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* @summary Archive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -189,7 +189,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
/**
|
||||
* Creates a new workflow run in `submitted` status from a self-contained manifest.
|
||||
* @summary Create Run
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -231,7 +231,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
* Creates a pull request for a completed run on GitHub and persists the record on the server.
|
||||
* @summary Create Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -318,10 +318,10 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
};
|
||||
},
|
||||
/**
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* @summary Fork Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -541,7 +541,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
* Merges the stored pull request for a run on GitHub.
|
||||
* @summary Merge Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -625,7 +625,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
/**
|
||||
* Validates and renders a workflow manifest as SVG without creating a run.
|
||||
* @summary Render Workflow Graph
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -827,10 +827,10 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
};
|
||||
},
|
||||
/**
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* @summary Rewind Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -872,7 +872,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
/**
|
||||
* Validates a workflow manifest without creating a run.
|
||||
* @summary Validate Workflow Manifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -914,7 +914,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
* Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable.
|
||||
* @summary Start Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -954,7 +954,7 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
};
|
||||
},
|
||||
/**
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* @summary Unarchive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -1043,7 +1043,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
const localVarAxiosParamCreator = RunsApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* @summary Archive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -1084,7 +1084,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
/**
|
||||
* Creates a new workflow run in `submitted` status from a self-contained manifest.
|
||||
* @summary Create Run
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1098,7 +1098,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
* Creates a pull request for a completed run on GitHub and persists the record on the server.
|
||||
* @summary Create Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1123,10 +1123,10 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* @summary Fork Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1195,7 +1195,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
* Merges the stored pull request for a run on GitHub.
|
||||
* @summary Merge Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1221,7 +1221,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
/**
|
||||
* Validates and renders a workflow manifest as SVG without creating a run.
|
||||
* @summary Render Workflow Graph
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1284,10 +1284,10 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* @summary Rewind Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1300,7 +1300,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
/**
|
||||
* Validates a workflow manifest without creating a run.
|
||||
* @summary Validate Workflow Manifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1314,7 +1314,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
* Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable.
|
||||
* @summary Start Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1325,7 +1325,7 @@ export const RunsApiFp = function(configuration?: Configuration) {
|
|||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* @summary Unarchive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -1360,7 +1360,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
const localVarFp = RunsApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* @summary Archive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -1392,7 +1392,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
/**
|
||||
* Creates a new workflow run in `submitted` status from a self-contained manifest.
|
||||
* @summary Create Run
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1403,7 +1403,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
* Creates a pull request for a completed run on GitHub and persists the record on the server.
|
||||
* @summary Create Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1422,10 +1422,10 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
return localVarFp.deleteRun(id, force, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* @summary Fork Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1479,7 +1479,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
* Merges the stored pull request for a run on GitHub.
|
||||
* @summary Merge Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1499,7 +1499,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
/**
|
||||
* Validates and renders a workflow manifest as SVG without creating a run.
|
||||
* @summary Render Workflow Graph
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1547,10 +1547,10 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
return localVarFp.retrieveRunGraphSource(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* @summary Rewind Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1560,7 +1560,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
/**
|
||||
* Validates a workflow manifest without creating a run.
|
||||
* @summary Validate Workflow Manifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1571,7 +1571,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
* Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable.
|
||||
* @summary Start Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1579,7 +1579,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
return localVarFp.startRun(id, startRunRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* @summary Unarchive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -1606,7 +1606,7 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
|
|||
*/
|
||||
export class RunsApi extends BaseAPI {
|
||||
/**
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* Marks a terminal run (`succeeded`, `failed`, or `dead`) as `archived`. Archived runs are hidden from default listings and are read-only until unarchived. Idempotent on already-archived runs. Returns 409 if the run is not terminal.
|
||||
* @summary Archive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -1641,7 +1641,7 @@ export class RunsApi extends BaseAPI {
|
|||
/**
|
||||
* Creates a new workflow run in `submitted` status from a self-contained manifest.
|
||||
* @summary Create Run
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1653,7 +1653,7 @@ export class RunsApi extends BaseAPI {
|
|||
* Creates a pull request for a completed run on GitHub and persists the record on the server.
|
||||
* @summary Create Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {CreateRunPullRequestRequest} createRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1674,10 +1674,10 @@ export class RunsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* Creates a new run from a checkpoint of the source run. The source run is left untouched.
|
||||
* @summary Fork Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {ForkRequest} [forkRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1736,7 +1736,7 @@ export class RunsApi extends BaseAPI {
|
|||
* Merges the stored pull request for a run on GitHub.
|
||||
* @summary Merge Run Pull Request
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {MergeRunPullRequestRequest} mergeRunPullRequestRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1758,7 +1758,7 @@ export class RunsApi extends BaseAPI {
|
|||
/**
|
||||
* Validates and renders a workflow manifest as SVG without creating a run.
|
||||
* @summary Render Workflow Graph
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {RenderWorkflowGraphRequest} renderWorkflowGraphRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1811,10 +1811,10 @@ export class RunsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* Creates a new run from an earlier checkpoint of a terminal source run, archives the source run, and records `run.superseded_by` on the source after archive succeeds. Returns 207 when the new run was created but the source archive step failed.
|
||||
* @summary Rewind Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {RewindRequest} [rewindRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1825,7 +1825,7 @@ export class RunsApi extends BaseAPI {
|
|||
/**
|
||||
* Validates a workflow manifest without creating a run.
|
||||
* @summary Validate Workflow Manifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {RunManifest} runManifest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1837,7 +1837,7 @@ export class RunsApi extends BaseAPI {
|
|||
* Starts a submitted run, queuing it for execution. Provide `resume=true` to resume an interrupted run from checkpoint. Returns 409 if the run is not startable.
|
||||
* @summary Start Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {StartRunRequest} [startRunRequest]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
|
|
@ -1846,7 +1846,7 @@ export class RunsApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* Restores an archived run to its prior terminal status. Idempotent on runs that are terminal but not archived (returns the current status without emitting an event). Returns 409 if the run is active.
|
||||
* @summary Unarchive Run
|
||||
* @param {string} id Unique run identifier (ULID).
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
@ -1867,4 +1867,3 @@ export class RunsApi extends BaseAPI {
|
|||
return RunsApiFp(this.configuration).unpauseRun(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,5 @@ export interface ForkRequest {
|
|||
* Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
|
||||
*/
|
||||
'target'?: string | null;
|
||||
/**
|
||||
* Whether to push the new run branches. Defaults to true.
|
||||
*/
|
||||
'push'?: boolean | null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,5 @@ export interface RewindRequest {
|
|||
* Optional checkpoint target such as `@2`, `build`, or `build@1`. Defaults to the latest checkpoint.
|
||||
*/
|
||||
'target'?: string | null;
|
||||
/**
|
||||
* Whether to push the new run branches. Defaults to true.
|
||||
*/
|
||||
'push'?: boolean | null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,11 @@ export interface RunSandbox {
|
|||
/**
|
||||
* Sandbox identifier.
|
||||
*/
|
||||
'id': string;
|
||||
'id'?: string | null;
|
||||
/**
|
||||
* Path where the run executed inside the sandbox.
|
||||
*/
|
||||
'working_directory'?: string | null;
|
||||
'resources'?: SandboxResources;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue