feat(git-sync): nested fork routing + fork-of-dev branch rooting

A fork of a dev workspace now roots its wm-fork/** branch on the dev's
environment-label branch (the content it diverged from) and its PR merges
back into that branch: the backend passes parent_dev_workspace_label with
the deploy (parent row joined in both enqueue paths), the CLI gains
--parent-dev-workspace-label and checks it before the wm-fork- prefix
fallback when rooting a fork-of-a-fork branch, and the PR completion hook
uses it as the PR base.

Fork sync routing covers the whole live descendant chain of the
webhook/poller workspace (recursive, depth-capped) instead of direct
children only, and fork_open_prs is resolved at the root ancestor — only
the root can hold auto-pull config, so grandchild forks sync through it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
This commit is contained in:
hugocasa
2026-07-07 18:13:52 +02:00
co-authored by Claude Opus 4.8
parent fe2d169083
commit c407d2cc48
11 changed files with 240 additions and 97 deletions
+11 -6
View File
@@ -509,14 +509,19 @@ label branch equals the checked-out tracked branch (which would otherwise
commit fork content straight to it).
`reconcile_fork_branch_pull` (windmill-git-sync EE) is the shared routing core:
resolve the branch to a **live child of this parent** (`parent_workspace_id`
match + `NOT deleted`, so a crafted branch name can't route a pull into an
unrelated workspace) — the `wm-fork/<base>/<suffix>` form via
`parse_fork_branch` (windmill-common), or an environment-label branch via a
dev-workspace label lookup — then load the fork's own repo entry and run the
resolve the branch to a **live descendant of this workspace** (recursive
`parent_workspace_id` walk + `NOT deleted`, so a crafted branch name can't
route a pull into an unrelated workspace) — the `wm-fork/<base>/<suffix>` form
via `parse_fork_branch` (windmill-common), or an environment-label branch via
a dev-workspace label lookup — then load the fork's own repo entry and run the
shared `reconcile_and_enqueue_pull` with the fork's per-ref dedup state and a
`clone_ref` override so the pull job clones the fork branch instead of the
resource's tracked branch.
resource's tracked branch. Descendants (not just direct children) because
**forks of a dev workspace** also sync through the root's webhook/poller —
only the root can hold auto-pull config. A fork-of-dev roots its `wm-fork/**`
branch on the dev's label branch and its PR merges back into it (the backend
passes `parent_dev_workspace_label` with the deploy; `fork_open_prs` is
resolved at the root ancestor).
State lives with the fork: its repo entry carries a server-written status-only
`auto_pull` blob (`last_synced_sha` keyed by the fork branch, `last_pull_status`;