mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-07 03:00:29 +00:00
parent
942bb3f2b8
commit
5878cfd04a
7 changed files with 614 additions and 10 deletions
420
run.json
420
run.json
File diff suppressed because one or more lines are too long
1
stages/004-preflight_lint@1/output.log
Normal file
1
stages/004-preflight_lint@1/output.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126
|
||||
8
stages/004-preflight_lint@1/script_timing.json
Normal file
8
stages/004-preflight_lint@1/script_timing.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126",
|
||||
"exit_code": 0,
|
||||
"duration_ms": 145385,
|
||||
"termination": "exited",
|
||||
"output_bytes": 0,
|
||||
"live_streaming": false
|
||||
}
|
||||
6
stages/004-preflight_lint@1/status.json
Normal file
6
stages/004-preflight_lint@1/status.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"outcome": "succeeded",
|
||||
"notes": "Script completed: cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1",
|
||||
"failure_reason": null,
|
||||
"timestamp": "2026-05-29T18:04:00.685163Z"
|
||||
}
|
||||
156
stages/005-implement@1/prompt.md
Normal file
156
stages/005-implement@1/prompt.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
Goal: ---
|
||||
title: "feat: Add Environment REST CRUD API"
|
||||
type: feat
|
||||
status: active
|
||||
date: 2026-05-28
|
||||
---
|
||||
|
||||
# feat: Add Environment REST CRUD API
|
||||
|
||||
## Summary
|
||||
|
||||
Add server-owned Environment CRUD under `/api/v1/environments`, modeled after
|
||||
Automations and backed by the existing `EnvironmentStore`. The API manages only
|
||||
the server-side environment catalog in `environments/*.toml`; client-side
|
||||
environment definitions in `workflow.toml`, `.fabro/project.toml`, or run inputs
|
||||
continue to work and are not managed by this API.
|
||||
|
||||
## API Contract
|
||||
|
||||
- Add OpenAPI paths:
|
||||
- `GET /api/v1/environments`
|
||||
- `POST /api/v1/environments`
|
||||
- `GET /api/v1/environments/{id}`
|
||||
- `PUT /api/v1/environments/{id}`
|
||||
- `DELETE /api/v1/environments/{id}`
|
||||
- Mirror Automations semantics:
|
||||
- List returns `{ data: Environment[], meta: { total } }`, sorted by id.
|
||||
- Create body includes `id`; replace body omits `id`; path id is authoritative.
|
||||
- `GET` and `PUT` return `ETag: "<revision>"`.
|
||||
- `PUT` and `DELETE` require `If-Match`.
|
||||
- Use existing Automation-style statuses: `400`, `404`, `409`, `422`, `428`, `500`.
|
||||
- Stale revisions return `409` to match Automations.
|
||||
- Add API-specific Environment request/response schemas so REST `image.dockerfile`
|
||||
accepts only inline content or `null`.
|
||||
- Existing workflow/settings schemas keep supporting Dockerfile `path`.
|
||||
- REST requests with Dockerfile `path` return `422` and must not read
|
||||
server-local files.
|
||||
- Do not add `PATCH` in v1.
|
||||
|
||||
## Implementation Changes
|
||||
|
||||
- OpenAPI and generated clients:
|
||||
- Update `docs/public/api-reference/fabro-api.yaml` with an `Environments` tag,
|
||||
an `EnvironmentId` parameter, CRUD paths, list envelope, and inline-only API
|
||||
image schema.
|
||||
- Regenerate Rust API types and the TypeScript Axios client.
|
||||
- Keep the existing `EnvironmentSettings` schema intact for workflow settings.
|
||||
- Server:
|
||||
- Add `lib/crates/fabro-server/src/server/handler/environments.rs`, following
|
||||
`automations.rs` for routes, auth, ETag parsing, and error mapping.
|
||||
- Merge the routes into real API routes; do not add demo routes unless an
|
||||
existing convention requires it.
|
||||
- Convert API request DTOs into `EnvironmentDraft` / `EnvironmentSettings` only
|
||||
after rejecting Dockerfile path sources.
|
||||
- Map `EnvironmentStoreError` similarly to Automations: duplicate, protected,
|
||||
and stale as `409`; missing as `404`; validation as `422`; internal
|
||||
storage/parse/io as curated `500`.
|
||||
- After successful create, replace, or delete, refresh cached manifest run
|
||||
settings from the current `EnvironmentStore` catalog so `/system/info` and
|
||||
default run settings reflect the updated catalog.
|
||||
- Domain and API types:
|
||||
- Use a meaningful API DTO boundary rather than treating REST and TOML as
|
||||
identical Dockerfile-source surfaces.
|
||||
- Reuse `fabro-environment::Environment` for persisted domain behavior where
|
||||
the wire shape matches; keep API-only request schemas distinct where
|
||||
inline-only Dockerfile behavior differs.
|
||||
|
||||
## Implementation Units
|
||||
|
||||
- [ ] **Unit 1: Define the OpenAPI contract**
|
||||
- Add the environment CRUD paths, schemas, and path parameter.
|
||||
- Ensure the spec distinguishes REST-safe inline Dockerfile sources from the
|
||||
existing workflow/settings Dockerfile source schema.
|
||||
- Verification: OpenAPI route conformance can see the new paths and generated
|
||||
clients expose an `EnvironmentsApi`.
|
||||
|
||||
- [ ] **Unit 2: Add server environment handlers**
|
||||
- Implement a new handler module mirroring the Automation CRUD handler shape.
|
||||
- Enforce authentication, id parsing, ETag/If-Match behavior, and error mapping.
|
||||
- Reject REST Dockerfile path sources before calling `EnvironmentStore`.
|
||||
- Verification: server API tests prove CRUD behavior and failure responses.
|
||||
|
||||
- [ ] **Unit 3: Refresh derived server state after mutations**
|
||||
- Ensure successful environment create, replace, and delete refresh any cached
|
||||
manifest run settings derived from `EnvironmentStore::catalog_layer()`.
|
||||
- Preserve existing client-side environment precedence and behavior.
|
||||
- Verification: a test proves newly created server environments affect the
|
||||
resolved server default run environment where applicable.
|
||||
|
||||
- [ ] **Unit 4: Regenerate clients and add contract tests**
|
||||
- Regenerate `fabro-api` and `lib/packages/fabro-api-client`.
|
||||
- Add Rust server integration tests and keep OpenAPI conformance passing.
|
||||
- Verification: generated Rust and TypeScript surfaces compile and expose the
|
||||
new environment operations.
|
||||
|
||||
## Test Plan
|
||||
|
||||
- Add server API tests in
|
||||
`lib/crates/fabro-server/tests/it/api/environments.rs` and register the module.
|
||||
- Cover:
|
||||
- List returns seeded environments and correct total.
|
||||
- Create persists `environments/{id}.toml`, returns `201`, and is visible via
|
||||
list/get.
|
||||
- Get returns current `ETag` matching `revision`.
|
||||
- Replace with valid `If-Match` updates the file, returns a new revision, and
|
||||
updates the `ETag`.
|
||||
- Replace/delete without `If-Match` return `428`.
|
||||
- Stale replace/delete return `409`.
|
||||
- Duplicate create returns `409`.
|
||||
- Invalid id/header returns `400`.
|
||||
- Domain validation failures return `422`.
|
||||
- Dockerfile `path` over REST returns `422` and does not persist or expose file
|
||||
contents.
|
||||
- Delete removes a non-default environment; deleting `default` returns a
|
||||
protected conflict.
|
||||
- Unauthenticated environment routes return `401`.
|
||||
- Creating an environment referenced by server default run settings refreshes
|
||||
cached manifest run settings.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- This API manages server-owned environments only; client-defined catalogs remain
|
||||
file/request scoped.
|
||||
- Built-in seed behavior follows the current store: seeded environments are
|
||||
listed, create conflicts with existing ids, and `default` is protected from
|
||||
delete.
|
||||
- Create responses match Automations and do not need an `ETag`; clients can use
|
||||
the returned `revision` or call `GET`.
|
||||
- Inline-only Dockerfile policy applies only to REST CRUD, not local TOML
|
||||
configuration.
|
||||
|
||||
## Sources
|
||||
|
||||
- `docs/public/api-reference/fabro-api.yaml`
|
||||
- `lib/crates/fabro-server/src/server/handler/automations.rs`
|
||||
- `lib/crates/fabro-environment/src/store.rs`
|
||||
- `lib/crates/fabro-environment/src/model.rs`
|
||||
- `docs/public/execution/environments.mdx`
|
||||
|
||||
|
||||
## Completed stages
|
||||
- **toolchain**: succeeded
|
||||
- Script: `command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1`
|
||||
- Output:
|
||||
```
|
||||
cargo 1.95.0 (f2d3ce0bd 2026-03-21)
|
||||
```
|
||||
- **preflight_compile**: succeeded
|
||||
- Script: `cargo check -q --workspace 2>&1`
|
||||
- Output: (empty)
|
||||
- **preflight_lint**: succeeded
|
||||
- Script: `cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1`
|
||||
- Output: (empty)
|
||||
|
||||
|
||||
Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.
|
||||
6
stages/005-implement@1/provider_used.json
Normal file
6
stages/005-implement@1/provider_used.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"mode": "agent",
|
||||
"provider": "openai",
|
||||
"model": "gpt-5.5",
|
||||
"reasoning_effort": "xhigh"
|
||||
}
|
||||
27
stages/005-implement@1/response.md
Normal file
27
stages/005-implement@1/response.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Implemented Environment REST CRUD API end-to-end.
|
||||
|
||||
Summary:
|
||||
- Added OpenAPI `Environments` tag, `/api/v1/environments` CRUD paths, `EnvironmentId` parameter, list envelope, and REST-safe inline-only Dockerfile schemas.
|
||||
- Added server handler module: `lib/crates/fabro-server/src/server/handler/environments.rs`.
|
||||
- Mounted real API routes only; no demo routes added.
|
||||
- Added API DTO boundary that rejects REST Dockerfile `path` sources with `422` before calling `EnvironmentStore`.
|
||||
- Added ETag / `If-Match` behavior matching Automations.
|
||||
- Added `EnvironmentStoreError` → `ApiError` mapping with stale/duplicate/protected as `409`.
|
||||
- Refreshes cached manifest run settings after create/replace/delete so `/system/info` reflects catalog mutations.
|
||||
- Tightened `fabro-environment` create/replace behavior to validate dense settings before persistence.
|
||||
- Regenerated Rust API types and TypeScript Axios client; TS now exposes `EnvironmentsApi`.
|
||||
- Added server integration tests, OpenAPI/SDK conformance test, and Rust API JSON/type-boundary tests.
|
||||
|
||||
Validation run:
|
||||
- Red TDD check: new environment API tests initially failed with `404` before routes existed.
|
||||
- `cargo nextest run -p fabro-server --features test-support --test it api::environments`
|
||||
- `cargo nextest run -p fabro-server --features test-support --test it openapi_conformance`
|
||||
- `cargo test -p fabro-api --test environment_round_trip`
|
||||
- `cargo nextest run -p fabro-environment`
|
||||
- `cargo check -q --workspace`
|
||||
- `cargo +nightly-2026-04-14 fmt --check --all`
|
||||
- `cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings`
|
||||
- `cd lib/packages/fabro-api-client && bun run generate`
|
||||
- `cd lib/packages/fabro-api-client && bun run typecheck`
|
||||
|
||||
Note: I installed `openjdk-21-jre-headless` in the container because `openapi-generator-cli` requires `java` for TypeScript client generation.
|
||||
Loading…
Add table
Reference in a new issue