diff --git a/backend/windmill-api-schedule/src/lib.rs b/backend/windmill-api-schedule/src/lib.rs index 35dc8b4f15..e2d495eaad 100644 --- a/backend/windmill-api-schedule/src/lib.rs +++ b/backend/windmill-api-schedule/src/lib.rs @@ -242,17 +242,19 @@ async fn create_schedule( let mut tx: Transaction<'_, Postgres> = user_db.begin(&authed).await?; - // Writing into a fork never sets operational state: force `enabled = false` - // so a cloned / synced / merged / UI-created schedule can't fire alongside - // the parent's. The fork owner re-enables locally via `setenabled`. This is - // the schedule analog of the trigger rule in + // A git-sync/merge/create write into a fork never sets operational state: + // force `enabled = false` so a cloned / synced / merged / UI-created schedule + // can't fire alongside the parent's. The fork owner re-enables locally via + // `setenabled`. Schedule analog of the trigger rule in // `windmill-trigger::handler::workspace_is_fork`; the read half (parent-value - // substitution on fork export) lives in `workspaces_export.rs`. + // substitution on fork export) lives in `workspaces_export.rs`. Read fork-ness + // on the non-RLS `db` pool (like the other two sites) so the determination is + // complete regardless of the caller's folder perms. let target_is_fork: bool = sqlx::query_scalar!( "SELECT parent_workspace_id IS NOT NULL FROM workspace WHERE id = $1", w_id ) - .fetch_optional(&mut *tx) + .fetch_optional(&db) .await? .flatten() .unwrap_or(false); diff --git a/backend/windmill-trigger/src/handler.rs b/backend/windmill-trigger/src/handler.rs index dcbea7e7c3..3591c88931 100644 --- a/backend/windmill-trigger/src/handler.rs +++ b/backend/windmill-trigger/src/handler.rs @@ -34,15 +34,16 @@ use windmill_git_sync::handle_deployment_metadata; /// True when the workspace is a fork (`parent_workspace_id IS NOT NULL`). /// -/// Operational state (`mode`) belongs to the parent workspace: a write into a -/// fork (git-sync push, merge deploy, clone, or even a plain UI create) must -/// never set it. On create we force `disabled` so a fork trigger can't compete -/// with the parent's listener; on update we preserve the fork's existing value. -/// The only mutator of a fork trigger's mode is the `setmode` endpoint (which -/// carries its own conflict warning). This is the write half of the rule whose -/// read half lives in `workspaces_export.rs` (parent-value substitution on -/// fork export), and it is the single authority shared by both the git-sync -/// round-trip and the in-app compare-workspaces merge. +/// Operational state (`mode`) belongs to the parent workspace: a git-sync / +/// merge / clone / UI-create write into a fork must never set it. On create we +/// force `disabled` so a fork trigger can't compete with the parent's listener; +/// on update we preserve the fork's existing value. `setmode` is the intended +/// explicit mutator of a fork's mode (and carries its own conflict warning) — +/// runtime error handling may still auto-disable an errored trigger, which is +/// orthogonal to this rule. This is the write half whose read half lives in +/// `workspaces_export.rs` (parent-value substitution on fork export), and it is +/// the single authority shared by both the git-sync round-trip and the in-app +/// compare-workspaces merge. async fn workspace_is_fork(db: &DB, workspace_id: &str) -> Result { let is_fork: Option = sqlx::query_scalar("SELECT parent_workspace_id IS NOT NULL FROM workspace WHERE id = $1") diff --git a/docs/fork-triggers.md b/docs/fork-triggers.md index 68b4a10293..308d21f1be 100644 --- a/docs/fork-triggers.md +++ b/docs/fork-triggers.md @@ -57,8 +57,13 @@ The rule that makes both the normal-git PR merge and the in-app merge behave: > **A trigger's `mode` (and a schedule's `enabled`) belongs to the parent > workspace. Git-sync *reads* the parent's value into a fork's synced file and -> *never writes* a fork's value back. The only mutator of a fork's operational -> state is the `setmode` / `setenabled` endpoint.** +> *never writes* a fork's value back. No git-sync / merge / create / update +> write sets a fork's operational state; the `setmode` / `setenabled` endpoint +> is the intended explicit mutator.** + +(Runtime error handling can still auto-disable an errored trigger or schedule — +that's orthogonal to this rule, which governs git-sync/merge/create/update +writes.) This is enforced in two halves, keyed off `parent_workspace_id IS NOT NULL` (the column, not the `wm-fork-*` naming convention — it stays consistent with