§4.5 of the groups/project-tool design. A group declares a route ONCE; the
tree apply fans it out into one concrete per-app_id route on every descendant
app — instantiation, not inheritance (the cross-app isolation boundary is
unchanged; expanded rows stay app-owned).
- Migration 0053: `route_templates` table (group-owned) + `routes.from_template`
provenance column + index.
- `template_repo.rs`: tx-aware CRUD + chain-load (mirrors group_repo/route_repo).
- Manifest: `[group]` `[[route_templates]]`; build_bundle emits them; rejected
on an app node (422).
- Apply: group nodes reconcile template rows (create/update/delete via the
plan); tree Phase B expands each chain template into a concrete route per
descendant app node — placeholders {app_slug}/{env}/{var:NAME} resolved per
app (unknown var/placeholder = hard error), script bound nearest-owner-wins.
Idempotent via from_template (diffed create/update-as-replace/delete; re-apply
is no-churn, --prune reaps expansions). Collision with a hand-declared route,
or between two templates, is a hard error. Each RESOLVED expansion is
validated like a hand-declared route (reserved-path, path/host parse,
host-claim) so a {var:}-injected path or unclaimed strict host can't slip in.
- Plan: route-template diff at the group node + blast-radius (descendant app
count in this apply); state_token folds each template (edit trips StateMoved).
- Authz: declaring → GroupScriptsWrite; an app receiving expansions →
AppWriteRoute (gated even with no hand-declared routes).
- Tests: tests/templates.rs (fan-out + per-slug + idempotent-stable-ids +
prune-reap + collision-rejected); placeholder/validation unit tests; schema
golden reblessed.
Reviewed (no CRITICAL/HIGH; isolation core verified sound). Closed the two
validation-parity MEDIUMs (host-claim + reserved-path/pattern on expansions)
and added an out-of-apply-descendant prune warning. Scope: expansion targets
app nodes in the tree apply (not yet cross-repo descendants); {var:NAME} uses
committed vars. M4b (trigger templates) reuses this engine next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
998 B
Rust
49 lines
998 B
Rust
//! Integration-test binary for the `pic` CLI.
|
|
//!
|
|
//! Every `#[test]` in this binary routes through `common::fixture()`, a
|
|
//! `LazyLock` that spawns picloud once on a private port and reuses it
|
|
//! across all journey modules. Mirrors the dashboard Playwright suite,
|
|
//! which spins backend + Vite up once for 63 specs.
|
|
//!
|
|
//! Gated on `DATABASE_URL`. To run:
|
|
//!
|
|
//! docker compose up -d postgres
|
|
//! DATABASE_URL=postgres://picloud:picloud@127.0.0.1:15432/picloud \
|
|
//! cargo test -p picloud-cli --test cli -- --include-ignored
|
|
|
|
mod common;
|
|
|
|
mod admins;
|
|
mod api_keys;
|
|
mod apply;
|
|
mod apps;
|
|
mod auth;
|
|
mod config;
|
|
mod dead_letters;
|
|
mod email_queue;
|
|
mod enabled;
|
|
mod env_overlay;
|
|
mod extension_points;
|
|
mod group_modules;
|
|
mod group_scripts;
|
|
mod group_secrets;
|
|
mod groups;
|
|
mod init;
|
|
mod invoke;
|
|
mod logs;
|
|
mod output;
|
|
mod ownership;
|
|
mod plan;
|
|
mod prune;
|
|
mod pull;
|
|
mod roles;
|
|
mod routes;
|
|
mod scripts;
|
|
mod secrets;
|
|
mod staleness;
|
|
mod templates;
|
|
mod tree;
|
|
mod tree_shape;
|
|
mod triggers;
|
|
mod vars;
|