fabro/lib/crates/fabro-server/Cargo.toml
Bryan Helmkamp fac0b10244
feat(server): preserve comments in setup_register TOML edits
`setup_register` in `web_auth.rs` used to round-trip the user's
settings file through `toml::Value` + `toml::to_string_pretty`, which
strips every comment, blank line, and explicit key ordering on the
way out. A user who'd hand-commented their `~/.fabro/settings.toml`
would see all of that lost on the next GitHub App registration.

Switches the edit path to `toml_edit::DocumentMut`, which preserves
prefix decoration (comments, blank lines) on every key. Adds
`toml_edit = "0.22"` as a workspace dependency (already pulled in
transitively via `toml 0.8`) and declares it in `fabro-server`.

Implementation notes:

- New `ensure_nested_table(doc, &["server", "web"])` walks a dotted
  path and `or_insert`s missing intermediate tables without touching
  existing ones.
- New `set_preserving_decor(table, key, value)` replaces an entry's
  value while copying the old key's `leaf_decor` forward. Without
  that workaround, `toml_edit::Table::insert` drops the prefix
  decoration of the replaced key -- which would strip a top-of-file
  comment attached to `_version = 1` or any other value we update.
- `_version` is only inserted when missing; it's always `1` today, so
  rewriting it every time is unnecessary and would trample its decor.
- `merge_settings_keys` now takes `&mut toml_edit::DocumentMut`
  instead of `&mut toml::Value`. The flow in `setup_register` parses
  the file on disk into a `DocumentMut`, applies the merge, and
  writes `doc.to_string()` back.

Adds a new test
`merge_settings_keys_preserves_comments_and_unrelated_keys` that
round-trips a fixture file containing:

- A top-of-file comment attached to `_version`
- A comment above `[server.storage]`
- A comment above a pre-existing `[server.integrations.slack]` table
- Unrelated keys in `[server.storage]`, `[server.integrations.slack]`,
  and `[run.model]`

and asserts that every comment and every unrelated key survives the
merge, that the new GitHub App keys are present, and that the final
output still parses as a valid v2 `SettingsFile` via
`fabro_config::ConfigLayer::parse`.

Also strengthens the existing
`merge_settings_keys_writes_v2_server_integrations_github` test with
a round-trip parse of the emitted TOML through `ConfigLayer::parse`
to ensure the output is real v2 config, not just a JSON-shaped blob.

3,765 workspace tests pass (+1 new). `cargo fmt --check --all` and
`cargo clippy --workspace -- -D warnings` are clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 19:18:10 -04:00

85 lines
2.4 KiB
TOML

[package]
name = "fabro-server"
edition.workspace = true
version.workspace = true
publish = false
license.workspace = true
description = "HTTP server for Fabro pipelines"
[lib]
doctest = false
[lints]
workspace = true
[dependencies]
fabro-spa = { path = "../fabro-spa" }
fabro-config = { path = "../fabro-config" }
fabro-graphviz = { path = "../fabro-graphviz" }
fabro-hooks = { path = "../fabro-hooks" }
fabro-interview = { path = "../fabro-interview" }
fabro-slack = { path = "../fabro-slack" }
fabro-workflow = { path = "../fabro-workflow" }
fabro-validate = { path = "../fabro-validate" }
fabro-sandbox = { path = "../fabro-sandbox", features = ["daytona"] }
fabro-github = { path = "../fabro-github" }
fabro-agent = { path = "../fabro-agent" }
fabro-llm = { path = "../fabro-llm" }
fabro-model = { path = "../fabro-model" }
fabro-proc = { path = "../fabro-proc" }
fabro-retro = { path = "../fabro-retro" }
fabro-types = { path = "../fabro-types" }
fabro-util = { path = "../fabro-util" }
fabro-api = { path = "../fabro-api" }
fabro-store = { path = "../fabro-store" }
chrono.workspace = true
futures-util.workspace = true
axum.workspace = true
axum-extra.workspace = true
cookie.workspace = true
dirs.workspace = true
tower = "0.5"
tower-http = { version = "0.6", features = ["trace"] }
tokio-stream = { workspace = true, features = ["sync"] }
base64.workspace = true
jsonwebtoken.workspace = true
tokio.workspace = true
tokio-rustls = "0.26"
rustls = { version = "0.23", default-features = false, features = ["std", "ring"] }
rustls-pemfile = "2"
rustls-pki-types = "1"
hyper = "1"
hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1", "http2"] }
tower-service = "0.3"
x509-parser = "0.16"
serde.workspace = true
serde_json.workspace = true
serde_yaml = "0.9"
anyhow.workspace = true
clap.workspace = true
toml.workspace = true
toml_edit.workspace = true
tracing.workspace = true
ulid.workspace = true
uuid.workspace = true
hmac.workspace = true
sha2.workspace = true
hex.workspace = true
reqwest.workspace = true
rand.workspace = true
bytes = "1"
tempfile = "3"
object_store.workspace = true
mime_guess.workspace = true
regex.workspace = true
semver.workspace = true
walkdir.workspace = true
multer = "3"
[dev-dependencies]
tokio = { workspace = true, features = ["test-util", "macros"] }
tower = "0.5"
http-body-util = "0.1"
openapiv3 = "2"
serde_yaml = "0.9"
fabro-sandbox = { path = "../fabro-sandbox" }