* docs: add design doc for automatic git-to-windmill pull sync
* docs: add migration plan and implementation phases to git-sync pull design
* feat(git-sync): add auto_pull settings schema and pull enqueue primitive
Adds AutoPullSettings/AutoPullMode/AutoPullStatus on GitRepositorySettings
(workspace_settings.git_sync JSONB), the GIT_SYNC_PULL_SCRIPT_PATH constant,
and should_pull/effective_poll_interval_s helpers with unit tests. Exports the
EE enqueue_git_pull_job primitive. Foundation for repo→Windmill auto-pull.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): poll repos and auto-pull new commits into the workspace
Phase 1 of automatic repo → Windmill sync. A monitor task (EE-licensed,
single-replica via advisory lock) git ls-remotes each auto-pull-enabled
repository ~every minute and enqueues a pull when the tracked branch moves,
reusing the {workspace_id}:git_sync concurrency key so pulls serialize with
in-flight push commits.
- windmill-store: background (no-authed) resolver get_git_repo_head_for_autopull
that resolves the repo resource (incl. $var: refs) and ls-remotes; GitHub-App
repos are skipped here and will sync via webhooks (phase 2).
- monitor.rs: poll/reconcile/persist with optimistic sha advance and failure
status; targeted jsonb update so concurrent settings edits aren't clobbered.
- edit_git_sync_repository: preserve server-owned auto_pull state on UI save.
- openapi: AutoPullSettings/AutoPullMode/AutoPullStatus + auto_pull field.
- frontend: per-repo "Automatically deploy changes from Git" toggle with last
sync status; demote the GitHub Actions link to an advanced CI option.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): wire webhook lifecycle + receiver; share reconcile logic
OSS side of phase 2 auto-pull webhooks:
- edit_git_sync_repository creates/removes the repo webhook on save (EE-gated,
best-effort → falls back to polling).
- monitor poller now delegates to the shared windmill_git_sync reconcile/persist
helpers (also used by the webhook receiver), removing duplicated logic.
- export the shared reconcile/persist/failure helpers; bump EE ref.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for phase 3 in-app PR creation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): show webhook vs polling status on the auto-pull toggle
When a repo has an active webhook (auto_pull.webhook_id set), the status line
reads "instant via webhook"; otherwise it reads the ~1-minute polling cadence.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): post PR diff check on dry-run completion (phase 4)
Worker completion hook in process_completed_job: when a DeploymentCallback job
carrying the __git_sync_pr_check marker finishes, parse the dry-run SyncResponse
and patch the GitHub check run with the diff summary (success/neutral/failure).
Export enqueue_git_pull_dry_run; bump EE ref.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref (drop unused GHES webhook_secret)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert(git-sync): defer phase 4 PR diff checks (OSS side)
Remove the worker completion hook that posted the PR check run, drop the
enqueue_git_pull_dry_run re-export and the orphaned sqlx cache, bump EE ref.
Phases 1-3 (polling, webhooks, in-app PR creation) are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Revert "revert(git-sync): defer phase 4 PR diff checks (OSS side)"
This reverts commit 0137d3ca48.
* chore(git-sync): point EE ref at restored phase 4 commit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for clone_ref dry-run
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump init-repository hub script to v28784
Picks up the clone_ref param (windmill-integrations#158) so the phase 4 PR-check
dry-run can clone the PR head. Backward compatible; manual pull/push and the
automated pull/poller/webhook all move to the same published version.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for auto-pull admin-permissioning fix
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for superadmin pull fallback
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): refresh auto-pull tooltip; bump EE ref for webhook secret encryption
The auto-pull toggle tooltip claimed GitHub App repos would sync via
webhooks "in a future update"; webhook delivery now works, so describe
the webhook-vs-polling behavior accurately. Bump the EE ref to pick up
encrypting the webhook HMAC secret at rest.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(git-sync): poll app-backed repos in auto/polling mode
The auto-pull poller skipped app-backed repos (the ls-remote head check
can't authenticate a tokenless URL), so auto- and polling-mode app repos
never synced when their webhook wasn't live. Wire the poller to fetch the
head via the GitHub API for app repos and reconcile. Bump the EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(git-sync): auto-pull UI — direction split, delivery mode, fallback notice
Reorganize the repository card into two clearly labeled directions:
"Push to Git on deploy (Windmill → Git)" and "Pull from Git (Git →
Windmill)". In the pull section:
- new connections default to auto-pull enabled (webhook with polling
fallback); existing repos load with auto-pull off and are unchanged
- a Delivery selector chooses "Webhook with polling fallback" or
"Polling only (air-gapped)"
- a notice surfaces webhook_error when delivery falls back to polling
- a reminder to remove any pre-existing GitHub Action that pushed into
Windmill, to avoid conflicting double-syncs
Adds the webhook_error field to AutoPullSettings (+ openapi) and bumps
the EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(git-sync): clearer push indicator + gate webhook delivery to app repos
- Push-on-deploy is shown with a check icon + concise line (via the
shared GitSyncModeDisplay, restyled from the oversized "Sync:" text);
the setup wizard reuses it without the check (pre-save preview).
- The delivery-mode selector only shows for GitHub App-backed repos;
token-based repos show a "webhooks require the GitHub App (managed or
GHES)" note with a docs link and poll instead. Bumps the EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(git-sync): fork auto-sync (phase 5) + live deploy check (phase 6)
Phase 5 — fork auto-sync configured at the parent (replaces the *-to-forks
GitHub Actions):
- Add fork_open_prs + fork_pull_sync to GitRepositorySettings (openapi + UI).
- UI: two "Forks of this workspace" toggles in the repo card, gated on
app-backed and not-a-fork; serialize the flags on save.
- On fork creation, strip the inherited auto_pull block (and fork_* flags) from
the copied git_sync repo: a fork must not carry the parent's webhook id (it
would delete the parent's hook on disable) or self-poll on top of the parent's
fan-out. Push-direction config + installation are still inherited unchanged.
Phase 6 — live deploy status check on the commit (Cloudflare-style): an
in-progress "Windmill" check on the head commit that flips to "Deployed N
changes"; completion handled by the generalized git-sync check hook.
Bump EE ref for the phase 5-6 EE implementation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(git-sync): bump EE ref for PAT auto-pull mode normalization
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): address PR review findings
- webhook_secret: redact from the settings API response and Debug output (still
persisted encrypted); it's a server-only HMAC key the UI never needs.
- poller: honor each repo's effective poll interval (relaxed ~10 min when a
webhook is live) instead of probing every ~60s tick.
- settings save: roll back a just-created webhook if the settings transaction
doesn't commit, so a failed save can't orphan a hook.
- auto-pull head check: fail SSH remotes with an actionable message (background
polling has no SSH identity) instead of a confusing ls-remote error.
- deploy/PR check summary: a pull result carrying neither changes nor a settings
diff now falls back to the unsummarized path instead of a false "in sync".
- UI: reset isGithubApp on resource change / failed fetch so webhook + fork
controls can't show for the wrong repo.
- tests: cover parse_git_sync_changes and format_change_list edge cases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): correct feature gating for OSS builds
- monitor.rs: keep the AUTO_PULL_LAST_POLL static, slack const, and
poll_git_auto_pull_inner all behind #[cfg(feature = "private")] (an inserted
static had split the cfg off the function, ungating it in OSS builds).
- edit_git_sync_repository: the webhook create/rollback block references
windmill_common::git_sync_ee (private module), so gate it on
all(enterprise, private) instead of enterprise only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(sqlx): cache workspace_diff query pulled in from origin/main
Re-merged origin/main (advanced past the earlier merge); regenerate the offline
sqlx entry for the new workspace_comparison test query so SQLX_OFFLINE builds
(cargo_test) pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): address Codex review findings (webhook cleanup on delete)
- Deleting a git-sync repository now tears down its managed GitHub webhook
(deletion bypassed the sync_repo_webhook lifecycle, orphaning the hook so
GitHub kept delivering to the instance).
- Worker completion hook rolls back the optimistic auto-pull sha on job failure
(OSS side of the EE change) + caches the new marker query. Bump EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): delete repo webhook after the removal commits
Codex re-review nits:
- delete_git_sync_repository deleted the webhook before the settings transaction
committed; a failed save would then leave the repo pointing at a hook that no
longer exists (sync_repo_webhook treats a set webhook_id as live and won't
recreate it). Capture the hook id, commit the DB removal, then delete the hook.
- Reword a fork-copy comment to drop drafting-history wording per AGENTS.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): reconcile the edit-path webhook after the settings commit
Codex nit: edit_git_sync_repository ran sync_repo_webhook before the transaction
committed. The rollback only covered created hooks, but sync_repo_webhook also
deletes a hook on disable/switch-to-polling — a commit failure then left the DB
with a webhook_id whose hook was already gone (and it wouldn't be recreated).
Save + commit first, then reconcile the webhook against the durable config and
persist any hook id/secret change (best-effort). Bump EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): preserve webhook secret on whole-config save + default on visible add
Codex nits:
- edit_git_sync_config saved the client config verbatim, so the webhook_secret
redacted from the GET response would be dropped (breaking delivery). Preserve
server-owned auto-pull state (webhook id/secret, synced sha, last status) per
repo from the existing settings, matching edit_git_sync_repository.
- addSyncRepository (the visible add path) didn't set the auto_pull default, so
new sync repos added from the UI came up with auto-deploy off. Match
addRepository's default (webhook + polling fallback).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* refactor(git-sync): drop fork_pull_sync (parent-level keep-forks-in-sync)
Removes the "Keep forks in sync with the tracked branch" toggle and its
fan-out. Pulling the tracked branch straight into every fork was the
inconsistent piece; the consistent model is per-fork branch sync (each
fork tracks its own wm-fork/** branch), which is a separate follow-up.
fork_open_prs is kept. Also tightens the fork toggle-section spacing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): detect dev workspaces in CLI fork branch derivation
isForkWorkspace / computeGitSyncDeployBranch keyed off the wm-fork- id
prefix. Dev workspaces are forks with a custom, prefix-less id, so their
wm-fork/** branch was never derived or created. Detect them via
parent_workspace_id too (which the backend already passes), mirroring the
backend's `parent.is_some() || wm-fork- prefix` rule.
Pairs with the hub-script clone-flag fix (windmill-integrations#163); both
take effect once the CLI is released and the pinned version is bumped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): reconcile webhooks on full-config save
edit_git_sync_config preserved server-owned webhook fields but never
created or deleted the managed GitHub webhook, so enabling auto-pull
through the whole-config endpoint only polled, and disabling or removing
a repo left an orphan hook still delivering. Mirror the per-repository
endpoint: after the commit is durable, reconcile every saved repo's
webhook (sync_repo_webhook) and delete the hooks of repos the save
removed, including the clear-whole-config case. Addresses the Codex nit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): address Codex nits (webhook orphan on cleared auto_pull, fork detection)
- edit_git_sync_config: also delete a repo's old webhook when the save drops
the repo OR clears its auto_pull. Webhook fields are only preserved onto a
Some auto_pull, so a save that present-but-clears a repo would otherwise
orphan its hook.
- GitSyncRepositoryCard: isFork now uses parent_workspace_id OR the wm-fork-
prefix (was AND), matching the backend/CLI rule, so prefix-less dev
workspaces are detected as forks and don't show the parent fork-PR toggle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* docs(git-sync): update design doc for the dropped fork_pull_sync
Phase 5 documented "Keep forks in sync with the tracked branch"
(fork_pull_sync) and its fan-out as implemented; that feature was removed.
Rewrite the section to reflect what ships (fork_open_prs), note the drop +
the per-fork-branch follow-up, and remove the stale fan-out mentions
elsewhere. Addresses the Codex nit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): redact webhook secrets from workspace export; fix doc endpoints
- Export (P1): strip the server-owned auto_pull state (webhook secret/id/error
+ synced sha + last pull status) from git_sync before it is written into an
export's settings.json for both settings formats. The HMAC webhook secret
must never leave the server (matching the GET-settings redaction), and a
re-imported workspace must not inherit another install's hook/sync state.
- Docs: the webhook receiver is a single per-workspace endpoint
/api/w/{workspace}/github_app/webhook (host-aware for managed + self-managed);
update the stale push_webhook/{id} and instance-global /api/github_app/webhook
references.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): skip deleted/archived workspaces in the auto-pull poller
The poller scanned workspace_settings directly, so an archived (soft-deleted)
or renamed-away workspace — whose settings row persists — kept polling and
could enqueue a pull into a dead workspace. Join workspace and require
NOT deleted. The EE webhook receiver gets the same filter (ee ref bumped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): never trust client-supplied server-owned auto-pull fields
Both write endpoints (edit_git_sync_repository, edit_git_sync_config)
persisted caller-supplied auto_pull.webhook_id / webhook_secret /
webhook_error / last_synced_sha / last_pull_status when adding a repo or
newly enabling auto-pull, letting a client inject a webhook id/secret or
fake sync state. Strip those server-owned fields from the request up front;
existing repos re-derive them from the DB (carried over), new ones start
clean and the server (re)creates the webhook.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): merge webhook fields post-commit instead of clobbering the row
The post-commit webhook reconcile in edit_git_sync_repository and
edit_git_sync_config wrote the whole pre-reconcile git_sync snapshot back
after the main save committed. A concurrent git-sync edit or poller status
write that landed in the gap could then be dropped by the stale snapshot.
Re-read the current row and merge only the reconciled webhook id/secret/error
for the repos the reconcile actually changed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): parent-managed fork sync + PR-on-deploy toggles
Fork sync (push-on-merge-to-forks parity): a parent-level
auto_pull.sync_forks toggle routes changes on each fork's wm-fork/** branch
into that fork workspace, via the parent's existing webhook and one extra
fork-heads listing per poll tick (git ls-remote pattern for token repos,
git/matching-refs for app-backed). Fork state is a server-written
status-only auto_pull blob on the fork's own repo entry; the fork's card
shows a read-only "managed in the parent workspace" line with its branch
and last pull status. Dev workspaces (prefix-less ids) use the same branch
parsing (unit-tested in windmill-common).
PR-on-deploy: opening PRs for Windmill-pushed branches moves into the
deploy pipeline, per repo toggle (promotion_open_prs on the promotion
repo; parent-level fork_open_prs for fork deploys). The push job carries a
marker and the job-completion hook derives the pushed branch (helper
unit-tested against the CLI formula) and opens the PR outbound, so it
works without inbound webhooks; the webhook-side wm_deploy PR arm is
removed. The documented open-pr-* GitHub Actions remain valid alternatives
(PR creation is idempotent).
Fork guards: promotion mode, enabled auto-pull, and fork_open_prs are
rejected on fork workspaces (they are parent-managed; a fork's deploys
always target its wm-fork/** branch) and the promotion card is hidden in a
fork's settings. Enabling auto-pull now also requires EE, and the
post-commit webhook reconcile persists the normalized delivery mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): dev workspaces sync with their environment-label branch
A dev workspace's git branch is its environment label verbatim (dev/
staging, default dev) — a first-class env branch like the documented
push-on-merge-staging layout — instead of the wm-fork/** form. The label
rides the deploy job args (backend → hub script → CLI
--dev-workspace-label), the PR completion hook derives the same head, the
webhook/poller route label branches into the matching dev-workspace child
(poller lists them alongside wm-fork/* via extra ls-remote refs / per-label
API lookups), and manual pulls from the UI pass clone_ref accordingly. The
CLI refuses to deploy when the label branch equals the checked-out tracked
branch, which would otherwise commit fork content straight to it.
Because the branch is keyed on the label, the label is now immutable after
creation: set at create/attach only, the set_dev_workspace_label endpoint
is removed and the settings tab shows it read-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* 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
* feat(git-sync): PR deploy-preview comment, clearer check copy, app-only hints
- The PR diff completion hook maintains one managed comment on the PR
(Cloudflare deploy-preview style: workspace, status, commit, collapsible
change list), upserted per synchronize via a hidden marker. The check run
stays for required-check gating.
- A settings difference in the diff summary is worded by cause: the PR
changes wmill.yaml, vs pre-existing drift between the repo's wmill.yaml
and the workspace, vs undetermined (neutral wording).
- Deploy-status check titles name the target workspace ("Deployed 2
change(s) to staging"), since GitHub shows a head commit's checks on any
PR containing it and a bare "Deployed" read as if the PR had deployed.
- Token-based repos see a hint pointing at the open-pr-on-commit /
open-pr-on-fork-commit workflows where the app-only PR toggles would be;
an API-set toggle on a non-app repo now logs a warning naming the
fallback; the design doc lists app-only features and their degradation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): EE-gate auto-pull UI, fork pull clone_ref, no-op push PR gate
- CE: the auto-pull and fork-PR toggles are disabled with an EE badge, and
new sync repos only default them on when licensed (basic git sync is
available on CE since #8493, but auto-pull is EE and the backend rejects it)
- The pull modal passes clone_ref for wm-fork- forks (wm-fork/<tracked>/<id>)
so a manual pull fetches the fork branch instead of the tracked branch head
- PR-on-deploy skips no-op pushes: when the push script reports pushed=false
(e.g. the deploy was caused by an auto-pull), the completion hook no longer
ensures a PR, so closed PRs aren't recreated by the sync loop
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore: refresh package-lock after main merge (windmill-utils-internal 1.8.2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* test: auto-pull e2e integration tests; fix PR comment table formatting
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): runtime license gate for auto-pull saves; user/group promotion-branch parity
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): explain in-sync PR verdicts with the repo's sync filter scope
A PR that only touches files outside the repository's include paths gets
"In sync", which reads as a wrong verdict; the check summary (and managed
comment) now name the filters, e.g. "Only files matching this repository's
sync filters deploy on merge: `f/**` (excluding `f/pat/**`)."
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): clearer card copy/structure; surface PR-creation failures
- Fork sync toggle renamed and kept in the pull section; the fork PR toggle
moves to the push section with a note that push settings apply to forks
- Fork/dev workspaces' push section names their actual branch instead of the
tracked-branch line; promotion repos hide the pull direction (promotion
pushes deploy branches on top of a sync-mode setup)
- Promotion mode line describes the wm_deploy/** branch + merge-to-promote
flow; workflow-fallback hints lead with the how-to and link to the docs;
test connection button demoted from accent per brand guidelines
- New server-owned open_pr_error on repo settings: the deploy completion hook
records why a PR couldn't be opened (e.g. app permission not yet approved)
and clears it on the next success; shown as a warning under the PR toggles
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix: cfg-gate scope-note helper (dead code on OSS builds)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): license-gate preserved auto-pull; attach strips parent-only settings
- edit_git_sync_repository re-checks the runtime Enterprise gate against the
EFFECTIVE repo state after preservation: the older-client arm copies the
existing auto_pull back, which the request-side check never saw
- attach_dev_workspace now mirrors the fork-creation copy on the attached
workspace's own git sync: promotion repos dropped, auto_pull/fork PRs/PR
error stripped, and any managed webhook deleted after commit (the attached
workspace is parent-managed and must not keep pulling its old tracked branch)
- integration test: attaching an auto-pull-enabled workspace strips it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): detach clears standalone parent; reject label == tracked branch
- detach_dev_workspace clears parent_workspace_id for prefix-less (attached
standalone) workspaces so they stop classifying as forks and deploying to
wm-fork/** branches; wm-fork- re-designated forks keep their parent; cache
invalidations mirror attach
- dev-workspace create/attach reject an environment label that equals a
git-sync repository's tracked branch (prod's or the candidate's): deploys
would target the very branch the repo syncs from, and the CLI guard would
fail every push job after the fact
- CLI unit tests: prefix-less fork beats wm_deploy derivation; isForkWorkspace
parent-id argument
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(git-sync): bump hub script pins (push 28786, pull 28785)
Published from windmill-integrations #163 with windmill-cli@1.753.1-gitsync.0:
dev-workspace label deploys, fork-of-dev rooting, fork checkout on the
existing remote branch, and the pushed-flag result.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): no parent-only defaults on fork repos; rename strips webhook state
- addSyncRepository skips the auto_pull/fork_open_prs defaults on fork/dev
workspaces where the backend rejects them (saving a new sync repo from an
EE fork 400'd deterministically)
- change_workspace_id strips webhook id/secret/error from the copied git_sync
and deletes the stale GitHub hooks post-commit: they deliver to the old
(archived) workspace URL, so the new workspace would report a live webhook
while polling at the relaxed interval; next save re-registers cleanly
- EE: PR diff checks for contributor-fork PRs clone the synthetic
pull/<n>/head ref (head.ref doesn't exist in the base repo)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(git-sync): bump pull script pin to hub/28787 (synthetic PR ref support)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): targeted jsonb update for open_pr_error (no full-blob clobber)
The full read-modify-write raced the poller's concurrent last_synced_sha /
last_pull_status writes on the same column; mirror the EE status writer and
update only the matching repository element's open_pr_error key.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* style(git-sync): inline EE badge on gated toggles (matches settings nav)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): both directions in page/card descriptions; clearer promotion flow
- Page header and sync-card description mention the pull direction, not only
push-on-deploy
- Promotion description walks the actual flow (wm_deploy/** branch, merge to
promote, sync the target workspace) and points at the PR toggle / workflow;
the Git Promotion docs link now also shows on configured cards, not only in
the empty state
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): resolve branch-less resources' default branch for fork sync
A git resource without an explicit branch polled as the bare "HEAD" ref,
which the fork/dev-label fan-out cannot scope (wm-fork/<branch>/*), so fork
sync silently never ran on polling-only repos. Resolve the remote's default
branch name with `ls-remote --symref HEAD` (one call for name + head sha);
"HEAD" only remains when resolution fails. The polling e2e test now uses a
branch-less resource to cover this shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): runtime license gate for in-app PR creation
promotion_open_prs/fork_open_prs are rejected on save without an Enterprise
plan (like auto_pull), and the deploy completion hook re-checks the plan
before opening PRs so flags stored while licensed stop driving GitHub calls
after a lapse.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): app-aware pull defaults, always webhook delivery, token-repo guidance
- Pull-from-Git defaults on only for app-backed repos (applied when the
selected resource resolves); polling is opt-in for token repositories,
with a warning alert recommending the GitHub App (instant pull + in-app
PRs) or the sync GitHub workflow
- App repos always use webhook delivery with polling fallback: the delivery
selector is gone and a stored polling mode is normalized back to auto
- Post-save modal reflects the auto-pull state instead of telling the user
to turn on a toggle that is already on
- Non-app PR hints recommend the GitHub App explicitly
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): single info box for token-repo pull guidance
Merges the instant-pull recommendation with the GitHub Action conflict note,
shown only for non-app repos; app repos need neither, and the redundant
'instant webhook sync requires' line is gone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): keep the GitHub Action conflict note on app repos
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): anchor docs links to their exact sections
GitHub App references point at integrations/git_repository#github-app, the
workflow hints at deploy_gh_gl#github-actions-setup, and the sync workflow
at git_sync#github-actions (all anchors verified against the live docs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): fork workflow hint links to git_sync#github-actions
open-pr-on-fork-commit is documented on the git_sync page, not deploy_gh_gl.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): fork PRs are opt-in on new connections too
Only auto-pull and fork sync default on for new app-backed connections;
opening pull requests stays a deliberate per-repo decision.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): default the managed PR on for new app-backed promotion repos
A promotion deploy's wm_deploy/** branch exists to be merged; without a PR
it's an orphaned branch. Fork PRs stay opt-in. Also scope the sync-repo
auto-pull default to sync mode so promotion repos can't pick it up.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* docs(git-sync): GHES self-managed app permission setup
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* docs(git-sync): frame permission update against GitHub Actions, not polling
Existing installations don't have polling; their git-to-Windmill direction
runs on GitHub Actions today, so the approval text describes the update as
replacing those workflows and notes every feature is opt-in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): gate GitHub PRs on Windmill CI test results (WIN-2051)
Add a "Windmill CI tests" GitHub check run on any PR against the tracked
branch, so a workspace-fork customer can mark it a required status check and
have Windmill's own CI tests (the `// test:` annotation) block the PR —
replacing the GitHub Action that polls the CI-test results API.
Driven by the pull_request webhook (same event as the Phase 4 diff check): a
new git_sync_ci_test_check table tracks one check per (fork workspace, PR head
commit); a per-ci_test-job completion hook (result_processor) and the git-sync
poller sweep (monitor) conclude it. Reuses the Checks: write grant from #9552;
token repos keep the documented Action. See docs Phase 7.
EE logic lives in windmill-ee-private (git_sync_ee.rs); see ee-repo-ref.txt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(sqlx): drop two cache entries orphaned by the merge
Both queries were rewritten on main; nothing in the sources produces them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(migration): describe the CI-test check as webhook-opened, not deploy-opened
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore: bump ee-repo-ref (skipped CI-test check on unmapped PRs)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(git-sync): skipped check for unmapped PRs; correct the installation rationale
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): cascade CI-test check rows with their workspaces; review fixes (EE ref)
Deleting a fork (the normal end of the one-fork-per-PR flow) or its parent
must not fail on the check table's foreign keys.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): CI-test check waits for the head's deploy; ignore retired tests (EE ref)
Design doc: GitLab scope, pending-while-deploying and retired-test rules.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): CI-test verdict matches jobs against current test references (EE ref)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): record the pushed head as synced; CI-test check waits for it (EE ref)
The push completion hook records the commit the deploy push script reports as
the branch's synced sha, mirroring what a pull records. The check row stores
head_ref. Requires the hub push script version that reports the sha; the pin
is bumped once it is published.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): CI-test check row keeps the newest check run when creates race (EE ref)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): queue CI tests before a deploy or dependency job completes
The dispatch was detached, so a finished deploy did not yet imply its tests
existed. The git-sync PR check reads "deploy settled" as "tests queued", so the
dispatch is awaited at all three sites; errors are still only logged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore(ci-tests): sqlx cache for the pull-success readiness clause; bump EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(ci-tests): readiness waits for pull success, failed deploy fails the check; sqlx cache; EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): queue resource-triggered CI tests before the update is acknowledged
Same ordering guarantee as scripts and flows: the git-sync PR check
treats a finished pull as "the deploy's tests exist". Also carries the
sqlx cache for the tested-item liveness filter and the EE ref.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): dispatch on flow/resource create, sweep outside the auto-pull lock, pushed-head map
- create_flow and create_resource queue CI tests the way the update paths do
- the check sweep runs after the advisory unlock so GitHub calls never extend
the auto-pull tick
- AutoPullSettings gains last_pushed_sha (settings round-trips keep it); the
EE push hook writes it instead of last_synced_sha so auto-pull still pulls a
commit someone else pushed under ours
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): last_pushed_sha is server-owned auto-pull state
Clear it from client-supplied settings, carry it over from the existing
entry on save, and keep it out of workspace exports, like the other sync
state fields. Correct the two doc comments that still described the
pushed head as the synced head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* feat(git-sync): say which pull toggles the PR checks depend on
The repo webhook created by the pull toggle is what delivers pull request
events, and fork sync is what lets a fork PR's externally pushed commits
reach the fork workspace before its CI tests check is read.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* refactor(git-sync): CI check readiness reads a synced-head event log
New git_sync_synced_head table: one row per commit a workspace reflects
on a branch, written by the pull completion hook on success (from the
pull marker's branch and sha) and by the push completion hook from the
push script's reported sha. The check reads that instead of inferring
sync state from the auto-pull settings jsonb, so AutoPullSettings gets
no pushed-sha field and the settings gates for it go away. Pull success
now also runs the auto-pull completion hook.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): queue CI tests before the deploy push is enqueued; schema summary
The synced-head row a push writes is what makes the PR check read the
fork's results, so the tests must be queued before the push job exists.
Reordered at the script create, lockfile, and resource sites (flows
already push from their dependency job). Lists the two new tables in
summarized_schema.txt.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(ci-tests): newest synced head decides readiness; EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(ci-tests): newest synced head decides readiness
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): synced heads keyed by repository; check row keeps the repo resource path
Both tables are unreleased, so their migrations gain the column in place.
The push hook reads the push job's repository from its args again.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): CI check rows keyed by repository; EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore(git-sync): pin the deploy push script to hub/28956
The version whose main returns the push outcome {pushed, sha, branch},
which the completion hook records as the branch's synced head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore(ci-tests): tooltips name GitHub for the CI check; drop the sanitization test; docs and EE ref
The sanitization test guarded a field that no longer exists and now only
re-exercised pre-existing behavior. The design doc carries the repository
key and the row-before-create order.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(ci-tests): re-fire resets the run id; pinned-script reason on timeout; EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(git-sync): record the commit a pull checked out; skip a rebased push
The pull script now reports the sha and branch its clone landed on, so
the synced head is what was applied rather than the head observed when
the pull was enqueued (the marker stays the fallback). A push the script
had to rebase sits on commits the workspace has not pulled, so it is not
recorded; the pull those commits trigger records the head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore(git-sync): pin the push script to hub/28958 and the pull script to hub/28957
The versions whose results report a rebased push and the commit a pull
checked out.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(ci-tests): queued-pull wait and kept failure on re-fire; EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore(ci-tests): sqlx cache for the prune and skipped-guard queries; EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(ci-tests): a failed head passes only on newer runs; EE ref
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* refactor(ci-tests): the PR check runs the head's own suite
The synced-head row records the CI test runs dispatched for the head
(tests_dispatched_at, ci_test_job_ids); the check reads exactly those.
windmill-git-sync now owns the check lifecycle (it can enqueue jobs) and
depends on windmill-dep-map for the dispatcher. The deploy-site changes
that ordered per-item dispatch before the push existed only for the old
workspace-wide verdict and are restored to main's form; flow and resource
creation keep the per-item dispatch they lacked.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): suite runs are not debounced, run as the workspace admin, and a dead dispatch claim is retaken
EE ref f30eaf8: the head's suite no longer shares debounce keys with
deploy-triggered runs (a superseded run dropped out of the verdict), runs as
the workspace admin an auto pull resolves rather than the sync job's identity
(which retention could remove), and a dispatch claim that never recorded ids
is retaken after 5 minutes. sqlx cache and design doc updated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): the head's suite expands wildcard references
EE ref 53d77c3; sqlx cache updated for the reference and script queries.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): bound the failed-deployment gate; keep a current head's check row
EE ref 63523d5; sqlx cache updated for the two changed queries.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): the head's suite runs as the fork's owner; poster_workspace_id
EE ref a74af94. The unreleased check-table migration renames
github_workspace_id to poster_workspace_id (the workspace whose credential
posts the check); sqlx cache, schema summary and design doc follow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(git-sync): the PR check's suite runs as the fork's owner
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): CE-only build, UI pull-script pin, detached sweep
- The four check-lifecycle re-exports exist only on enterprise builds; gating
them on private alone broke the CE image (private without enterprise).
- hubPaths.json's gitInitRepo follows the server's pull-script pin (28957),
as every previous bump did and as the git-sync integration test asserts.
- The monitor spawns the CI-check sweep detached (EE ref f96aa31).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore: bump EE ref to a1d7a1e
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ci-tests): advance the PR check off the worker's completion loop
Concluding a check calls GitHub; the completion loop finishes jobs serially,
so the evaluation is spawned detached (idempotent, retried by the poller).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* docs(git-sync): check-row-before-create order, retarget events; EE ref 40079e0
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* chore: update ee-repo-ref to a4da009a5eae72bd55f34de41ba7929b53d53c9b
This commit updates the EE repository reference after PR #662 was merged in windmill-ee-private.
Previous ee-repo-ref: 4845e823bfaa28d3258b4c2fb1e9e2781dde382f
New ee-repo-ref: a4da009a5eae72bd55f34de41ba7929b53d53c9b
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* test: assert the unpacked repo symlink without following it
`unpack_keeps_a_link_that_stays_in_the_repo` read through the link it had just
unpacked. Windows stores a symlink's target verbatim and its object manager
rejects the `/` in a POSIX one, so `read_to_string` came back with
`ERROR_INVALID_NAME` and the release's `cargo_test_windows` job was red.
Pin what the function is responsible for on every platform — the link is kept
and materialized — and read through it only where a POSIX relative target
resolves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
* test: key the cli sync-map fixtures with the platform separator
A sync map is keyed with the platform separator on both sides — `FSFSElement`
walks the tree with `path.join`, and the remote `ZipFSElement` starts at
`"." + SEP` and joins from there — while an `!inline` reference is always
forward-slash. `lock_dedup.ts` follows that convention; the fixtures did not,
so on Windows they built a map shape the CLI never produces and 12 of them
failed. `getTypeStrFromPath` is the same story: it matches
`"dependencies" + SEP`, and the test handed it a forward-slashed path.
Build the fixture keys through the separator, leaving the `!inline` references
and the `present` map forward-slash, as `sync.ts` hands them over.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
* ci: skip the discord comment relay when the thread lookup returns none
A rate-limited or unauthorized Discord response carries no thread list, and
under `bash -e` that aborted the step — jq cannot iterate null, nor parse the
HTML error page Cloudflare answers a 429 with — before it reached the "thread
not found, skipping" branch right below. Three comment relays failed that way
on the 1.794.0 head.
Keep the step green for both, but tell them apart: a response with no thread
list is a delivery that was dropped for a reason worth seeing, so it warns with
the body it got, while a PR that genuinely has no thread stays quiet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P3WRtxdKNGdomWX9vaAYGx
* chore: bump git-sync hub scripts to windmill-cli 1.794.0
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: resolve the head commit of GitHub App repos in the git repo viewer
`get_git_commit_hash` ran `git ls-remote` against the raw resource URL.
A GitHub-App-backed repository stores a tokenless URL, so the probe failed
with "could not read Username" and the viewer never got past its first
step. Resolve the head over the GitHub REST API with a server-side
installation token instead, reusing the lookup the auto-pull poller
already uses for app repos. Non-app repositories keep the ls-remote path.
Also picks up the EE-side allowlist fix that lets the clone hub script
request an installation token.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 63c67e2a2db198af26a0334f5be14af7d9987eb1
This commit updates the EE repository reference after PR #732 was merged in windmill-ee-private.
Previous ee-repo-ref: 2a260961fa0a9bb5631c17e2f718cb8efb4f9aa2
New ee-repo-ref: 63c67e2a2db198af26a0334f5be14af7d9987eb1
Automated by sync-ee-ref workflow.
* fix: honour the app-repo head lookup's not-app-backed result
`get_app_repo_head_for_autopull` documents `Ok(None)` as "this repo is not
app-backed, use the ls-remote path", which is what the other two callers do.
Fall through to `ls-remote` on `None` instead of turning it into a 500, and
drop the handler's own `is_github_app` read now that the callee's answer is
honoured.
Also bumps ee-repo-ref to pick up route-safe ref handling in that lookup.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: serve GitHub App repositories as an archive instead of a token
The viewer's clone script asked the server for an installation token and put
it in the clone URL. That token is installation-wide and carries the App's
full permissions, so minting one requires a workspace admin, and the viewer
was therefore admin-only for app-backed repositories.
The server now streams a tarball of the commit instead, authorized by read
access to the git_repository resource, so no GitHub credential reaches the
job.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: run delegate_to_git_repo playbooks from GitHub App repositories
An Ansible job's runnable_path is the user's own script, which no entry in
the git-sync script allowlist can match, so `delegate_to_git_repo` could
never obtain a token for an app-backed repo. It also gave up entirely on
agent workers, whose connection has no database to mint one from.
A playbook run only reads a working tree: the clone is followed by one
rev-parse for a log line, and nothing after that touches git. So take the
same archive route the viewer uses, extracting the commit's tarball into the
job's repository directory. No GitHub credential reaches the worker, and
agent workers work because the route is HTTP.
Archive entries are joined onto the target by hand so a crafted archive
cannot write outside the job directory.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: drop the now-immutable secret_url binding
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: point the repo viewer at the archive-based clone script
hub/28905 reads app-backed repositories through the server's archive route
instead of minting an installation token, which the backend in this release
no longer grants it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: stream repository archives to disk rather than into memory
The archive download went through `AuthedClient::get`, whose client caps a
request at 20 seconds and whose response was then buffered whole. A
repository is arbitrarily large, so that cut off slow downloads and put every
job on the worker at risk of running the process out of memory.
Add `get_streaming`, the read counterpart to the streaming upload path, and
write the response out chunk by chunk.
Extraction now creates each entry's parent directory: a tar carries directory
entries only by convention, and the traversal guard now has tests, one of
which caught the missing parent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: require admin to read an app-backed repository
A `git_repository` resource names the repository rather than holding a
credential for it, so read access to one authorizes nothing: anyone who can
write a resource path can point one at any repository the GitHub App
installation reaches, then read their own resource. The head lookup now
requires admin for app-backed repos, matching the archive route and the
repository picker, which already limits itself to workspaces where the
caller is an admin.
Repos that aren't app-backed are untouched and stay open to any reader.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: describe the repo viewer's hub script as it stands
The file read as a patch waiting to be applied, against a hub version two
releases stale. Describe what the published script does, including the
archive route app-backed repositories now take.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: run the archive fetch under the job poller, off the job directory
Three defects in the delegate path's fetch:
The download and extraction ran outside the job poller that the git clone
paths go through, so a cancelled or timed-out run kept streaming and
extracting an arbitrarily large repository while holding the worker. There is
no wall-clock bound on the download itself, by design, which is exactly why
it needs the poller.
The archive was written to a fixed name inside the job directory, where
`create_file_resources` has already laid down the run's own files at paths
the playbook chooses. A run naming a file `repo_archive.tar.gz` had it
truncated and then deleted. It goes to a per-job temp path now.
Link entries were unpacked with their target unchecked. `Entry::unpack`
writes the link verbatim, so a link out of the tree plus a later entry
descending through it writes wherever it points. Targets now face the same
containment check as entry paths.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: keep repo symlinks, refuse only writes that go through them
The link check rejected any target containing `..`, which is ordinary in a
repository — `docs/x -> ../README.md` resolves inside the tree, and a git
checkout keeps it. Rejecting it failed the whole extraction for repositories
the clone path handles, and app-backed repos have no clone path to fall back
to.
Targets are preserved as git preserves them. What would let one escape is a
later entry written at or underneath the link, so that is what is refused.
Extraction also polls an abort flag now: a `spawn_blocking` task outlives the
join handle its caller drops, so a cancelled job left it unpacking in the
background.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: refuse hard links in a repository archive
Leaving link targets verbatim is right for symlinks — git checks them out
that way, and an escape needs a second entry descending through the link,
which is refused. A hard link is not like that: unpacking one creates it
against a target resolved there and then, so an escaping target is useful on
its own.
No git tree can express a hard link, so an archive carrying one did not come
from a repository. Refuse it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 21f79bbbd39ae89665d1a89738630978616aa309
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: update ee-repo-ref to 37695a769b25d16b34107eedc1076793a8b388c8
This commit updates the EE repository reference after PR #737 was merged in windmill-ee-private.
Previous ee-repo-ref: 21f79bbbd39ae89665d1a89738630978616aa309
New ee-repo-ref: 37695a769b25d16b34107eedc1076793a8b388c8
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: do not apply workspace display name on git-sync pull
The workspace display name is stored in settings.yaml and was re-applied on
every pull via changeWorkspaceName. Because settings.yaml is shared across the
branches of a repo, a workspace could have its name overwritten by another
workspace that syncs the same repo. Keep name in settings.yaml for reference
(written on push) but stop applying it on pull.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: consolidate workspace-name rationale to one comment (review nit)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: bump git-sync hub scripts to windmill-cli 1.769.1
Repin GIT_SYNC_PULL_SCRIPT_PATH (28795->28808), LATEST_GIT_SYNC_SCRIPT_PATH
(28796->28809) and frontend gitInitRepo to the hub scripts bundling
windmill-cli@1.769.1, so backend automatic git pulls no longer apply the
workspace display name (the CLI fix in this PR only reaches auto-pull via the
pinned hub script bundle).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): enable per-item promotion mode on dev workspaces
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style: keep unrelated git-sync Alert copy at its original wrapping
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): fall back to parent_path on empty deploy path + bump ee ref
computeGitSyncDeployBranch used ?? so a backend-serialized empty path (rename out of the repo filter) skipped the deploy branch and could commit to the tracked base; use || to fall back to parent_path like the backend. Bumps ee-repo-ref for the single-object promotion_open_prs fix (windmill-ee-private#679).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): route dev-promotion non-branchable objects off the tracked base
user/group objects (and any unresolvable ref) returned null in promotion mode, so a dev-workspace deploy pushed them straight to the parent's tracked branch. Fall back to the dev's env-label branch instead; the backend opens no PR for them (isolated, not promoted).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): dev-workspace promotion via a toggle on the inherited repo
A dev workspace reuses the single repo it inherited from prod: a 'Promote to prod via Git' toggle flips it between sync mode (deploys to the dev branch) and promotion mode (per-item wm_deploy/** PRs to prod), with a per-item/per-folder sub-toggle. Removes the redundant separate-promotion-repo setup for dev workspaces.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(git-sync): dev-promotion regression test + widen git_sync_e2e path filter
Adds a CLI integration case covering dev-workspace promotion (script -> wm_deploy branch; user/group -> env-label branch, main never touched). Widens the git-sync-test.yml relevance filter to the deploy-branch derivation, git-sync guard, and CLI git-deploy files so the e2e suite runs on PRs like this one.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): gate dev promotion toggle on EE, fix card mode + workflow path filters
Codex review: (1) show the dev promotion toggle only under an active EE license and revert the optimistic save if the backend rejects it; (2) derive the dev card's display mode from use_individual_branch so promotion copy shows in promotion mode; (3) mirror the new relevance paths into the workflow's top-level push/pull_request filters so it actually triggers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): only use the single-card dev promotion UX when the dev has one repo
Codex review: an attached dev workspace keeps its own repositories rather than inheriting prod's. Gating the single-card + toggle + hidden-secondaries UX on repositories.length <= 1 makes a multi-repo attached dev fall back to the normal layout, so no active repo is hidden and an unrelated repo isn't presented as prod's promotion target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): runtime EE-plan gate for promotion mode, consistent with auto-pull/PR
Codex review: promotion mode only had the CE compile rejection, while auto-pull and PR creation runtime-gate on the active plan (check_git_sync_ee_license). Add check_promotion_license and call it from both edit_git_sync_config and edit_git_sync_repository, plus the matching CE rejection on edit_git_sync_config so the two endpoints are symmetric. Promotion is now gated like every other git-sync EE setting.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): dev promotion must reuse the parent workspace's repository
Codex review: repository count doesn't prove a dev inherited prod's repo — an attached dev keeps its own. check_dev_promotion_targets_parent_repo resolves the promotion repo's URL and rejects enabling promotion unless it matches one the parent (prod) tracks, so branches/PRs can't target an unrelated repository. Called from both git-sync edit endpoints.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): dev promotion save-time check uses shared parent-repo matcher (url+branch)
Delegates to windmill_common::git_sync_ee::dev_promotion_target_matches_parent so the settings gate and the deploy-time safety net share one url+branch identity check. Bumps ee-repo-ref for the EE deploy-time enforcement.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref for private resolve_repo_url_and_branch
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref for promotion-target matcher authz doc
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump hub scripts to gitsync-cli versions, fix promotion tooltips
Point LATEST_GIT_SYNC_SCRIPT_PATH (28790 -> 28796) and
GIT_SYNC_PULL_SCRIPT_PATH / gitInitRepo (28789 -> 28795) at the hub
versions pinning windmill-cli@1.763.1-gitsync.0, which carries the
dev-workspace promotion routing. Slugs unchanged, so the GitHub-App
token check and hub script cache are unaffected.
Tooltips: enabling promotion pushes a PR-ready wm_deploy/** branch;
Windmill only opens the pull request itself when automatic pull
requests are enabled. Reword both toggles to stop promising a PR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): dev promotion mirrors to the env-label branch, PR toggles exclusive by branch type
Bump ee-repo-ref for the dispatcher changes: a promotion dev's deploys
now also push to its env-label branch (one extra mirror job per batch,
users/groups mirror-only), and `fork_open_prs` no longer applies to a
dev in promotion mode where `promotion_open_prs` governs.
Frontend: the fork-PR toggle tooltip states its actual coverage
(wm-fork/** and the dev branch of a dev workspace) and that a promotion
dev's own pull request toggle takes over for wm_deploy/** branches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): reject dev promotion on pre-28796 pinned sync scripts
An older pinned sync script bundles a CLI that force-disables per-item
branches on every fork, so enabling promotion on a dev workspace with
such a pin would silently keep deploying to the env-label branch. Both
git-sync edit endpoints now reject the combination with an actionable
error; the EE dispatchers (via ee-repo-ref bump) demote inherited
configs to promotion-off semantics so markers, branch keys and the
mirror match the branch the CLI actually pushes. Roots and auto-managed
repositories are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): serialize dev promotion toggle saves
The promotion and per-folder toggles persist immediately via whole-repo
saves; leaving them interactive while one is pending lets rapid flips
race, and the earlier save (enabling runs extra backend checks) can
commit last, silently reversing the state the UI shows. Both toggles now
disable while a save is in flight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): lock auto-PR toggle during promotion save, rename-out branch routing
Frontend: the automatic-PR toggle is revealed by the promotion toggle's
in-flight save; an edit made mid-save was absorbed into the saved
baseline without reaching the backend. It now disables during that save.
EE (ee-repo-ref bump): dispatcher debounce/concurrency keys and PR
markers follow the CLI's parent_path fallback for rename-out items, so
their wm_deploy/** branches debounce per-branch and open their PR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): condense comments to durable constraints
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 8bf73f803158bcbf7b8d55a36f4a1ebfcc1bbcd9
This commit updates the EE repository reference after PR #679 was merged in windmill-ee-private.
Previous ee-repo-ref: c2cd718cb53d234f909f485bd7cd43ed9605ffd1
New ee-repo-ref: 8bf73f803158bcbf7b8d55a36f4a1ebfcc1bbcd9
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
The image prebundles the hub scripts listed in hubPaths.json by their
pinned (immutable) version. windmill-integrations republished the four
image-cached slack scripts with form-data bumped 4.0.5 -> 4.0.6; point
hubPaths.json at the new versions so freshly built images cache the fixed
lock instead of the stale one.
Fixes part of #10219
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): avoid percent-encoded colon in git-sync hub script path
The git-sync init/detection hub script slug contained a colon stored as
`%3A` in the run-by-path URL. The generated API client re-encodes path
params with encodeURI, turning `%3A` into `%253A` (double-encoding). Some
hardened reverse proxies / WAFs reject double URL-encoding and return a
bare 400 before the request reaches Windmill, breaking git-sync repository
detection on those instances.
The hub resolves scripts by numeric id and ignores the slug, so dropping
the colon from the slug is behavior-neutral (same script, same id-keyed
worker cache) while producing a colon-free run URL.
Also force-cache GIT_SYNC_PULL_SCRIPT_PATH at build alongside
LATEST_GIT_SYNC_SCRIPT_PATH so the backend-driven pull script is always
baked into the image for airgapped workers, instead of relying on an
incidental hubPaths.json overlap.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref for git-init slug match fix
Pulls in windmill-ee-private#676 so the EE is_git_init_script check matches
the colon-free git-init hub slug (GitHub App token grant for git-sync jobs).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref for git-init slug helper + test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to a3adea1ffb406e709cc480871df58fab6c51aca1
This commit updates the EE repository reference after PR #676 was merged in windmill-ee-private.
Previous ee-repo-ref: cef4e008ef62dec434aa9bb3ec783db8aff6a1c1
New ee-repo-ref: a3adea1ffb406e709cc480871df58fab6c51aca1
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* docs: add design doc for automatic git-to-windmill pull sync
* docs: add migration plan and implementation phases to git-sync pull design
* feat(git-sync): add auto_pull settings schema and pull enqueue primitive
Adds AutoPullSettings/AutoPullMode/AutoPullStatus on GitRepositorySettings
(workspace_settings.git_sync JSONB), the GIT_SYNC_PULL_SCRIPT_PATH constant,
and should_pull/effective_poll_interval_s helpers with unit tests. Exports the
EE enqueue_git_pull_job primitive. Foundation for repo→Windmill auto-pull.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): poll repos and auto-pull new commits into the workspace
Phase 1 of automatic repo → Windmill sync. A monitor task (EE-licensed,
single-replica via advisory lock) git ls-remotes each auto-pull-enabled
repository ~every minute and enqueues a pull when the tracked branch moves,
reusing the {workspace_id}:git_sync concurrency key so pulls serialize with
in-flight push commits.
- windmill-store: background (no-authed) resolver get_git_repo_head_for_autopull
that resolves the repo resource (incl. $var: refs) and ls-remotes; GitHub-App
repos are skipped here and will sync via webhooks (phase 2).
- monitor.rs: poll/reconcile/persist with optimistic sha advance and failure
status; targeted jsonb update so concurrent settings edits aren't clobbered.
- edit_git_sync_repository: preserve server-owned auto_pull state on UI save.
- openapi: AutoPullSettings/AutoPullMode/AutoPullStatus + auto_pull field.
- frontend: per-repo "Automatically deploy changes from Git" toggle with last
sync status; demote the GitHub Actions link to an advanced CI option.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): wire webhook lifecycle + receiver; share reconcile logic
OSS side of phase 2 auto-pull webhooks:
- edit_git_sync_repository creates/removes the repo webhook on save (EE-gated,
best-effort → falls back to polling).
- monitor poller now delegates to the shared windmill_git_sync reconcile/persist
helpers (also used by the webhook receiver), removing duplicated logic.
- export the shared reconcile/persist/failure helpers; bump EE ref.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for phase 3 in-app PR creation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): show webhook vs polling status on the auto-pull toggle
When a repo has an active webhook (auto_pull.webhook_id set), the status line
reads "instant via webhook"; otherwise it reads the ~1-minute polling cadence.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(git-sync): post PR diff check on dry-run completion (phase 4)
Worker completion hook in process_completed_job: when a DeploymentCallback job
carrying the __git_sync_pr_check marker finishes, parse the dry-run SyncResponse
and patch the GitHub check run with the diff summary (success/neutral/failure).
Export enqueue_git_pull_dry_run; bump EE ref.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref (drop unused GHES webhook_secret)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert(git-sync): defer phase 4 PR diff checks (OSS side)
Remove the worker completion hook that posted the PR check run, drop the
enqueue_git_pull_dry_run re-export and the orphaned sqlx cache, bump EE ref.
Phases 1-3 (polling, webhooks, in-app PR creation) are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Revert "revert(git-sync): defer phase 4 PR diff checks (OSS side)"
This reverts commit 0137d3ca48.
* chore(git-sync): point EE ref at restored phase 4 commit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for clone_ref dry-run
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump init-repository hub script to v28784
Picks up the clone_ref param (windmill-integrations#158) so the phase 4 PR-check
dry-run can clone the PR head. Backward compatible; manual pull/push and the
automated pull/poller/webhook all move to the same published version.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for auto-pull admin-permissioning fix
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(git-sync): bump EE ref for superadmin pull fallback
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(git-sync): refresh auto-pull tooltip; bump EE ref for webhook secret encryption
The auto-pull toggle tooltip claimed GitHub App repos would sync via
webhooks "in a future update"; webhook delivery now works, so describe
the webhook-vs-polling behavior accurately. Bump the EE ref to pick up
encrypting the webhook HMAC secret at rest.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(git-sync): poll app-backed repos in auto/polling mode
The auto-pull poller skipped app-backed repos (the ls-remote head check
can't authenticate a tokenless URL), so auto- and polling-mode app repos
never synced when their webhook wasn't live. Wire the poller to fetch the
head via the GitHub API for app repos and reconcile. Bump the EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(git-sync): auto-pull UI — direction split, delivery mode, fallback notice
Reorganize the repository card into two clearly labeled directions:
"Push to Git on deploy (Windmill → Git)" and "Pull from Git (Git →
Windmill)". In the pull section:
- new connections default to auto-pull enabled (webhook with polling
fallback); existing repos load with auto-pull off and are unchanged
- a Delivery selector chooses "Webhook with polling fallback" or
"Polling only (air-gapped)"
- a notice surfaces webhook_error when delivery falls back to polling
- a reminder to remove any pre-existing GitHub Action that pushed into
Windmill, to avoid conflicting double-syncs
Adds the webhook_error field to AutoPullSettings (+ openapi) and bumps
the EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(git-sync): clearer push indicator + gate webhook delivery to app repos
- Push-on-deploy is shown with a check icon + concise line (via the
shared GitSyncModeDisplay, restyled from the oversized "Sync:" text);
the setup wizard reuses it without the check (pre-save preview).
- The delivery-mode selector only shows for GitHub App-backed repos;
token-based repos show a "webhooks require the GitHub App (managed or
GHES)" note with a docs link and poll instead. Bumps the EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(git-sync): fork auto-sync (phase 5) + live deploy check (phase 6)
Phase 5 — fork auto-sync configured at the parent (replaces the *-to-forks
GitHub Actions):
- Add fork_open_prs + fork_pull_sync to GitRepositorySettings (openapi + UI).
- UI: two "Forks of this workspace" toggles in the repo card, gated on
app-backed and not-a-fork; serialize the flags on save.
- On fork creation, strip the inherited auto_pull block (and fork_* flags) from
the copied git_sync repo: a fork must not carry the parent's webhook id (it
would delete the parent's hook on disable) or self-poll on top of the parent's
fan-out. Push-direction config + installation are still inherited unchanged.
Phase 6 — live deploy status check on the commit (Cloudflare-style): an
in-progress "Windmill" check on the head commit that flips to "Deployed N
changes"; completion handled by the generalized git-sync check hook.
Bump EE ref for the phase 5-6 EE implementation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(git-sync): bump EE ref for PAT auto-pull mode normalization
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): address PR review findings
- webhook_secret: redact from the settings API response and Debug output (still
persisted encrypted); it's a server-only HMAC key the UI never needs.
- poller: honor each repo's effective poll interval (relaxed ~10 min when a
webhook is live) instead of probing every ~60s tick.
- settings save: roll back a just-created webhook if the settings transaction
doesn't commit, so a failed save can't orphan a hook.
- auto-pull head check: fail SSH remotes with an actionable message (background
polling has no SSH identity) instead of a confusing ls-remote error.
- deploy/PR check summary: a pull result carrying neither changes nor a settings
diff now falls back to the unsummarized path instead of a false "in sync".
- UI: reset isGithubApp on resource change / failed fetch so webhook + fork
controls can't show for the wrong repo.
- tests: cover parse_git_sync_changes and format_change_list edge cases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): correct feature gating for OSS builds
- monitor.rs: keep the AUTO_PULL_LAST_POLL static, slack const, and
poll_git_auto_pull_inner all behind #[cfg(feature = "private")] (an inserted
static had split the cfg off the function, ungating it in OSS builds).
- edit_git_sync_repository: the webhook create/rollback block references
windmill_common::git_sync_ee (private module), so gate it on
all(enterprise, private) instead of enterprise only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(sqlx): cache workspace_diff query pulled in from origin/main
Re-merged origin/main (advanced past the earlier merge); regenerate the offline
sqlx entry for the new workspace_comparison test query so SQLX_OFFLINE builds
(cargo_test) pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): address Codex review findings (webhook cleanup on delete)
- Deleting a git-sync repository now tears down its managed GitHub webhook
(deletion bypassed the sync_repo_webhook lifecycle, orphaning the hook so
GitHub kept delivering to the instance).
- Worker completion hook rolls back the optimistic auto-pull sha on job failure
(OSS side of the EE change) + caches the new marker query. Bump EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): delete repo webhook after the removal commits
Codex re-review nits:
- delete_git_sync_repository deleted the webhook before the settings transaction
committed; a failed save would then leave the repo pointing at a hook that no
longer exists (sync_repo_webhook treats a set webhook_id as live and won't
recreate it). Capture the hook id, commit the DB removal, then delete the hook.
- Reword a fork-copy comment to drop drafting-history wording per AGENTS.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): reconcile the edit-path webhook after the settings commit
Codex nit: edit_git_sync_repository ran sync_repo_webhook before the transaction
committed. The rollback only covered created hooks, but sync_repo_webhook also
deletes a hook on disable/switch-to-polling — a commit failure then left the DB
with a webhook_id whose hook was already gone (and it wouldn't be recreated).
Save + commit first, then reconcile the webhook against the durable config and
persist any hook id/secret change (best-effort). Bump EE ref.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): preserve webhook secret on whole-config save + default on visible add
Codex nits:
- edit_git_sync_config saved the client config verbatim, so the webhook_secret
redacted from the GET response would be dropped (breaking delivery). Preserve
server-owned auto-pull state (webhook id/secret, synced sha, last status) per
repo from the existing settings, matching edit_git_sync_repository.
- addSyncRepository (the visible add path) didn't set the auto_pull default, so
new sync repos added from the UI came up with auto-deploy off. Match
addRepository's default (webhook + polling fallback).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* refactor(git-sync): drop fork_pull_sync (parent-level keep-forks-in-sync)
Removes the "Keep forks in sync with the tracked branch" toggle and its
fan-out. Pulling the tracked branch straight into every fork was the
inconsistent piece; the consistent model is per-fork branch sync (each
fork tracks its own wm-fork/** branch), which is a separate follow-up.
fork_open_prs is kept. Also tightens the fork toggle-section spacing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): detect dev workspaces in CLI fork branch derivation
isForkWorkspace / computeGitSyncDeployBranch keyed off the wm-fork- id
prefix. Dev workspaces are forks with a custom, prefix-less id, so their
wm-fork/** branch was never derived or created. Detect them via
parent_workspace_id too (which the backend already passes), mirroring the
backend's `parent.is_some() || wm-fork- prefix` rule.
Pairs with the hub-script clone-flag fix (windmill-integrations#163); both
take effect once the CLI is released and the pinned version is bumped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): reconcile webhooks on full-config save
edit_git_sync_config preserved server-owned webhook fields but never
created or deleted the managed GitHub webhook, so enabling auto-pull
through the whole-config endpoint only polled, and disabling or removing
a repo left an orphan hook still delivering. Mirror the per-repository
endpoint: after the commit is durable, reconcile every saved repo's
webhook (sync_repo_webhook) and delete the hooks of repos the save
removed, including the clear-whole-config case. Addresses the Codex nit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): address Codex nits (webhook orphan on cleared auto_pull, fork detection)
- edit_git_sync_config: also delete a repo's old webhook when the save drops
the repo OR clears its auto_pull. Webhook fields are only preserved onto a
Some auto_pull, so a save that present-but-clears a repo would otherwise
orphan its hook.
- GitSyncRepositoryCard: isFork now uses parent_workspace_id OR the wm-fork-
prefix (was AND), matching the backend/CLI rule, so prefix-less dev
workspaces are detected as forks and don't show the parent fork-PR toggle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* docs(git-sync): update design doc for the dropped fork_pull_sync
Phase 5 documented "Keep forks in sync with the tracked branch"
(fork_pull_sync) and its fan-out as implemented; that feature was removed.
Rewrite the section to reflect what ships (fork_open_prs), note the drop +
the per-fork-branch follow-up, and remove the stale fan-out mentions
elsewhere. Addresses the Codex nit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): redact webhook secrets from workspace export; fix doc endpoints
- Export (P1): strip the server-owned auto_pull state (webhook secret/id/error
+ synced sha + last pull status) from git_sync before it is written into an
export's settings.json for both settings formats. The HMAC webhook secret
must never leave the server (matching the GET-settings redaction), and a
re-imported workspace must not inherit another install's hook/sync state.
- Docs: the webhook receiver is a single per-workspace endpoint
/api/w/{workspace}/github_app/webhook (host-aware for managed + self-managed);
update the stale push_webhook/{id} and instance-global /api/github_app/webhook
references.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): skip deleted/archived workspaces in the auto-pull poller
The poller scanned workspace_settings directly, so an archived (soft-deleted)
or renamed-away workspace — whose settings row persists — kept polling and
could enqueue a pull into a dead workspace. Join workspace and require
NOT deleted. The EE webhook receiver gets the same filter (ee ref bumped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): never trust client-supplied server-owned auto-pull fields
Both write endpoints (edit_git_sync_repository, edit_git_sync_config)
persisted caller-supplied auto_pull.webhook_id / webhook_secret /
webhook_error / last_synced_sha / last_pull_status when adding a repo or
newly enabling auto-pull, letting a client inject a webhook id/secret or
fake sync state. Strip those server-owned fields from the request up front;
existing repos re-derive them from the DB (carried over), new ones start
clean and the server (re)creates the webhook.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): merge webhook fields post-commit instead of clobbering the row
The post-commit webhook reconcile in edit_git_sync_repository and
edit_git_sync_config wrote the whole pre-reconcile git_sync snapshot back
after the main save committed. A concurrent git-sync edit or poller status
write that landed in the gap could then be dropped by the stale snapshot.
Re-read the current row and merge only the reconciled webhook id/secret/error
for the repos the reconcile actually changed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): parent-managed fork sync + PR-on-deploy toggles
Fork sync (push-on-merge-to-forks parity): a parent-level
auto_pull.sync_forks toggle routes changes on each fork's wm-fork/** branch
into that fork workspace, via the parent's existing webhook and one extra
fork-heads listing per poll tick (git ls-remote pattern for token repos,
git/matching-refs for app-backed). Fork state is a server-written
status-only auto_pull blob on the fork's own repo entry; the fork's card
shows a read-only "managed in the parent workspace" line with its branch
and last pull status. Dev workspaces (prefix-less ids) use the same branch
parsing (unit-tested in windmill-common).
PR-on-deploy: opening PRs for Windmill-pushed branches moves into the
deploy pipeline, per repo toggle (promotion_open_prs on the promotion
repo; parent-level fork_open_prs for fork deploys). The push job carries a
marker and the job-completion hook derives the pushed branch (helper
unit-tested against the CLI formula) and opens the PR outbound, so it
works without inbound webhooks; the webhook-side wm_deploy PR arm is
removed. The documented open-pr-* GitHub Actions remain valid alternatives
(PR creation is idempotent).
Fork guards: promotion mode, enabled auto-pull, and fork_open_prs are
rejected on fork workspaces (they are parent-managed; a fork's deploys
always target its wm-fork/** branch) and the promotion card is hidden in a
fork's settings. Enabling auto-pull now also requires EE, and the
post-commit webhook reconcile persists the normalized delivery mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): dev workspaces sync with their environment-label branch
A dev workspace's git branch is its environment label verbatim (dev/
staging, default dev) — a first-class env branch like the documented
push-on-merge-staging layout — instead of the wm-fork/** form. The label
rides the deploy job args (backend → hub script → CLI
--dev-workspace-label), the PR completion hook derives the same head, the
webhook/poller route label branches into the matching dev-workspace child
(poller lists them alongside wm-fork/* via extra ls-remote refs / per-label
API lookups), and manual pulls from the UI pass clone_ref accordingly. The
CLI refuses to deploy when the label branch equals the checked-out tracked
branch, which would otherwise commit fork content straight to it.
Because the branch is keyed on the label, the label is now immutable after
creation: set at create/attach only, the set_dev_workspace_label endpoint
is removed and the settings tab shows it read-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* 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
* feat(git-sync): PR deploy-preview comment, clearer check copy, app-only hints
- The PR diff completion hook maintains one managed comment on the PR
(Cloudflare deploy-preview style: workspace, status, commit, collapsible
change list), upserted per synchronize via a hidden marker. The check run
stays for required-check gating.
- A settings difference in the diff summary is worded by cause: the PR
changes wmill.yaml, vs pre-existing drift between the repo's wmill.yaml
and the workspace, vs undetermined (neutral wording).
- Deploy-status check titles name the target workspace ("Deployed 2
change(s) to staging"), since GitHub shows a head commit's checks on any
PR containing it and a bare "Deployed" read as if the PR had deployed.
- Token-based repos see a hint pointing at the open-pr-on-commit /
open-pr-on-fork-commit workflows where the app-only PR toggles would be;
an API-set toggle on a non-app repo now logs a warning naming the
fallback; the design doc lists app-only features and their degradation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): EE-gate auto-pull UI, fork pull clone_ref, no-op push PR gate
- CE: the auto-pull and fork-PR toggles are disabled with an EE badge, and
new sync repos only default them on when licensed (basic git sync is
available on CE since #8493, but auto-pull is EE and the backend rejects it)
- The pull modal passes clone_ref for wm-fork- forks (wm-fork/<tracked>/<id>)
so a manual pull fetches the fork branch instead of the tracked branch head
- PR-on-deploy skips no-op pushes: when the push script reports pushed=false
(e.g. the deploy was caused by an auto-pull), the completion hook no longer
ensures a PR, so closed PRs aren't recreated by the sync loop
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore: refresh package-lock after main merge (windmill-utils-internal 1.8.2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* test: auto-pull e2e integration tests; fix PR comment table formatting
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): runtime license gate for auto-pull saves; user/group promotion-branch parity
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): explain in-sync PR verdicts with the repo's sync filter scope
A PR that only touches files outside the repository's include paths gets
"In sync", which reads as a wrong verdict; the check summary (and managed
comment) now name the filters, e.g. "Only files matching this repository's
sync filters deploy on merge: `f/**` (excluding `f/pat/**`)."
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): clearer card copy/structure; surface PR-creation failures
- Fork sync toggle renamed and kept in the pull section; the fork PR toggle
moves to the push section with a note that push settings apply to forks
- Fork/dev workspaces' push section names their actual branch instead of the
tracked-branch line; promotion repos hide the pull direction (promotion
pushes deploy branches on top of a sync-mode setup)
- Promotion mode line describes the wm_deploy/** branch + merge-to-promote
flow; workflow-fallback hints lead with the how-to and link to the docs;
test connection button demoted from accent per brand guidelines
- New server-owned open_pr_error on repo settings: the deploy completion hook
records why a PR couldn't be opened (e.g. app permission not yet approved)
and clears it on the next success; shown as a warning under the PR toggles
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix: cfg-gate scope-note helper (dead code on OSS builds)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): license-gate preserved auto-pull; attach strips parent-only settings
- edit_git_sync_repository re-checks the runtime Enterprise gate against the
EFFECTIVE repo state after preservation: the older-client arm copies the
existing auto_pull back, which the request-side check never saw
- attach_dev_workspace now mirrors the fork-creation copy on the attached
workspace's own git sync: promotion repos dropped, auto_pull/fork PRs/PR
error stripped, and any managed webhook deleted after commit (the attached
workspace is parent-managed and must not keep pulling its old tracked branch)
- integration test: attaching an auto-pull-enabled workspace strips it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): detach clears standalone parent; reject label == tracked branch
- detach_dev_workspace clears parent_workspace_id for prefix-less (attached
standalone) workspaces so they stop classifying as forks and deploying to
wm-fork/** branches; wm-fork- re-designated forks keep their parent; cache
invalidations mirror attach
- dev-workspace create/attach reject an environment label that equals a
git-sync repository's tracked branch (prod's or the candidate's): deploys
would target the very branch the repo syncs from, and the CLI guard would
fail every push job after the fact
- CLI unit tests: prefix-less fork beats wm_deploy derivation; isForkWorkspace
parent-id argument
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(git-sync): bump hub script pins (push 28786, pull 28785)
Published from windmill-integrations #163 with windmill-cli@1.753.1-gitsync.0:
dev-workspace label deploys, fork-of-dev rooting, fork checkout on the
existing remote branch, and the pushed-flag result.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): no parent-only defaults on fork repos; rename strips webhook state
- addSyncRepository skips the auto_pull/fork_open_prs defaults on fork/dev
workspaces where the backend rejects them (saving a new sync repo from an
EE fork 400'd deterministically)
- change_workspace_id strips webhook id/secret/error from the copied git_sync
and deletes the stale GitHub hooks post-commit: they deliver to the old
(archived) workspace URL, so the new workspace would report a live webhook
while polling at the relaxed interval; next save re-registers cleanly
- EE: PR diff checks for contributor-fork PRs clone the synthetic
pull/<n>/head ref (head.ref doesn't exist in the base repo)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* chore(git-sync): bump pull script pin to hub/28787 (synthetic PR ref support)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): targeted jsonb update for open_pr_error (no full-blob clobber)
The full read-modify-write raced the poller's concurrent last_synced_sha /
last_pull_status writes on the same column; mirror the EE status writer and
update only the matching repository element's open_pr_error key.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* style(git-sync): inline EE badge on gated toggles (matches settings nav)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): both directions in page/card descriptions; clearer promotion flow
- Page header and sync-card description mention the pull direction, not only
push-on-deploy
- Promotion description walks the actual flow (wm_deploy/** branch, merge to
promote, sync the target workspace) and points at the PR toggle / workflow;
the Git Promotion docs link now also shows on configured cards, not only in
the empty state
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): resolve branch-less resources' default branch for fork sync
A git resource without an explicit branch polled as the bare "HEAD" ref,
which the fork/dev-label fan-out cannot scope (wm-fork/<branch>/*), so fork
sync silently never ran on polling-only repos. Resolve the remote's default
branch name with `ls-remote --symref HEAD` (one call for name + head sha);
"HEAD" only remains when resolution fails. The polling e2e test now uses a
branch-less resource to cover this shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): runtime license gate for in-app PR creation
promotion_open_prs/fork_open_prs are rejected on save without an Enterprise
plan (like auto_pull), and the deploy completion hook re-checks the plan
before opening PRs so flags stored while licensed stop driving GitHub calls
after a lapse.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): app-aware pull defaults, always webhook delivery, token-repo guidance
- Pull-from-Git defaults on only for app-backed repos (applied when the
selected resource resolves); polling is opt-in for token repositories,
with a warning alert recommending the GitHub App (instant pull + in-app
PRs) or the sync GitHub workflow
- App repos always use webhook delivery with polling fallback: the delivery
selector is gone and a stored polling mode is normalized back to auto
- Post-save modal reflects the auto-pull state instead of telling the user
to turn on a toggle that is already on
- Non-app PR hints recommend the GitHub App explicitly
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): single info box for token-repo pull guidance
Merges the instant-pull recommendation with the GitHub Action conflict note,
shown only for non-app repos; app repos need neither, and the redundant
'instant webhook sync requires' line is gone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): keep the GitHub Action conflict note on app repos
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): anchor docs links to their exact sections
GitHub App references point at integrations/git_repository#github-app, the
workflow hints at deploy_gh_gl#github-actions-setup, and the sync workflow
at git_sync#github-actions (all anchors verified against the live docs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(git-sync): fork workflow hint links to git_sync#github-actions
open-pr-on-fork-commit is documented on the git_sync page, not deploy_gh_gl.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* fix(git-sync): fork PRs are opt-in on new connections too
Only auto-pull and fork sync default on for new app-backed connections;
opening pull requests stays a deliberate per-repo decision.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* feat(git-sync): default the managed PR on for new app-backed promotion repos
A promotion deploy's wm_deploy/** branch exists to be merged; without a PR
it's an orphaned branch. Fork PRs stay opt-in. Also scope the sync-repo
auto-pull default to sync mode so promotion repos can't pick it up.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* docs(git-sync): GHES self-managed app permission setup
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* docs(git-sync): frame permission update against GitHub Actions, not polling
Existing installations don't have polling; their git-to-Windmill direction
runs on GitHub Actions today, so the approval text describes the update as
replacing those workflows and notes every feature is opt-in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PP5gBSPfo1YtkL1sWVAjJm
* copy(workspaces): drop 'cosmetic' qualifier from dev-workspace label UI
* chore: update ee-repo-ref to 9b2a6375f838436cf68cff449cc9bc621cca5281
This commit updates the EE repository reference after PR #632 was merged in windmill-ee-private.
Previous ee-repo-ref: 99eef24e2f0402b9a997cde5f67be52ee5d54b0e
New ee-repo-ref: 9b2a6375f838436cf68cff449cc9bc621cca5281
Automated by sync-ee-ref workflow.
* fix(git-sync): reject '/' in fork and dev workspace ids
* fix(git-sync): bound auto-pull git probes with a per-command timeout
* fix(git-sync): persist webhook reconcile via targeted jsonb updates
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat(cache): allow overriding hub base url via env in `cache` mode
The `windmill cache hubPaths.json` prebuild step (run in the Dockerfile) never
connects to the DB, so HUB_BASE_URL stays at its compiled default
(https://hub.windmill.dev) — unlike server/worker modes which load it from the
DB global setting. This made it impossible to point the prebuild cache step at
a private or staging hub.
Read HUB_BASE_URL from the environment at the start of cache_hub_scripts and
store it into the existing HUB_BASE_URL ArcSwap (the same static the hub fetch
functions read). No effect unless the env var is set and non-empty; server and
worker modes are unchanged (they still use the DB setting).
This also enables validating hub-script dependency changes end-to-end against a
local fake-hub before pushing to the real hub.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(security): re-pin cached hub scripts to patched versions
windmill-integrations#133 was merged and pushed to the hub, minting new
versions with regenerated (CVE-free) lockfiles. Bump the hubPaths.json pins so
the prebuild cache step (`windmill cache`) fetches the patched lockfiles instead
of the old vulnerable ones (the hub serves each version_id immutably, so the old
pins keep returning the vulnerable deps until bumped).
- slackErrorHandler 19741 -> 28241
- slackRecoveryHandler 9080 -> 28239
- slackSuccessHandler 28220 -> 28240
- smtpReport 9086 -> 28242
- appReport 28076 -> 28243 (puppeteer screenshot script)
- gitInitRepo 28219 -> 28229 (already-fixed hub version; pin was stale)
Validated end-to-end against the real hub: `windmill cache` with these pins
produces a clean cache_nomount/bun (axios 1.16.1, form-data 4.0.5,
follow-redirects 1.16.0, nodemailer 8.0.10, ws 8.21.0, svelte 5.55.8,
devalue 5.8.1; basic-ftp and ip-address no longer pulled). No vulnerable
versions remain.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Reduce slim image vulnerability surface
* chore(docker): drop apt-get upgrade -y from slim images
apt-get upgrade hurts build reproducibility (same Dockerfile + same
commit at different times produces divergent images) and trips hadolint
DL3005. The freshness it buys is dominated by simply rebuilding against
the periodically-refreshed debian:bookworm-slim base image.
The --no-install-recommends and apt-list cleanup wins are kept.
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Fixes silent timeouts and partial-tree rendering when loading large git
repositories into the in-app viewer (Tony Hoang report: 400+ host_vars
files, 32 roles).
Three coordinated fixes:
1. Frontend (GitRepoViewer.svelte): drop the 60s clone timeout. Long-poll
getJobUpdates until the job completes, with a 30 min hard cap and a
user-cancel button. Stream live job logs into the viewer with a link
to the full job page. After success, verify the
.windmill_clone_complete marker before flipping pathExists, so a
partial S3 directory is no longer rendered as a complete tree.
2. Backend (check_s3_folder_exists, EE): new optional marker_file query
param. When set, the handler short-circuits to head() on the marker
object instead of "any object under prefix exists". The frontend now
always passes marker_file=.windmill_clone_complete.
3. Hub script: cloneRepoToS3forGitRepoViewer points at hub/28216, which
uploads files via a bounded-concurrency pool (16 workers), emits
throttled progress logs, and writes .windmill_clone_complete as its
last action. docs/clone_repo_and_upload_to_instance_storage.bun.ts is
the source for that hub publish; docs/git-repo-viewer-hub-script.md
explains the change.
Also drops three unused legacy hubPaths entries
(cloneRepoToS3forGitRepoViewer_0..2) — none were referenced from
anywhere in the codebase.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* improve logging for github app operations
* ee ref
* chore: update ee-repo-ref to 0b9e92f9e089293c6d523b77ed2c11edbc7a99c0
This commit updates the EE repository reference after PR #489 was merged in windmill-ee-private.
Previous ee-repo-ref: b259642e7f36b83a991034d5b28ae616f94ee5fc
New ee-repo-ref: 0b9e92f9e089293c6d523b77ed2c11edbc7a99c0
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* fix: GitHub Enterprise Server (GHE) support for self-managed GitHub Apps
- Fix GHE installation URL: use /github-apps/ path instead of /apps/ for non-github.com hosts
- Fix double decodeURIComponent on OAuth state param (URLSearchParams already decodes)
- Add client_id to self-managed GitHub App validation
- Bump hub scripts to GHE-compatible versions (sync, test, init, clone)
- Bump LATEST_GIT_SYNC_SCRIPT_PATH to hub/28176
- Rename "GitHub Enterprise App" → "GitHub App" in UI labels (it works for both)
- Formatting fixes in GhesAppSettings.svelte and gh_success page
EE ref: windmill-labs/windmill-ee-private@09c9ed1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update SQLx metadata
* fix: handle GHE Cloud (*.ghe.com) app installation URL path
GHE Cloud uses /apps/ like github.com, not /github-apps/ like self-hosted GHES.
Docs: https://docs.github.com/en/enterprise-cloud@latest/apps/using-github-apps/installing-a-github-app-from-a-third-party
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle GHE Cloud (*.ghe.com) installation URL and update ee-repo-ref
GHE Cloud uses /apps/ like github.com, not /github-apps/ like self-hosted GHES.
Docs: https://docs.github.com/en/enterprise-cloud@latest/apps/using-github-apps/installing-a-github-app-from-a-third-party
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update hubPaths to deprecate 28176 and use 28180 as latest sync script
Aligns with main's LATEST_GIT_SYNC_SCRIPT_PATH bump in PR #8532.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update ee-repo-ref to 6bb0ff0 (includes GHE fixes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* test: add E2E git sync integration tests with Gitea
Add 7 end-to-end tests that verify the full git sync pipeline:
deploy objects in Windmill → DeploymentCallback job runs hub sync script →
correct files appear in a Gitea git repository.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: auto-manage git sync script version locked to Windmill release
- Add LATEST_GIT_SYNC_SCRIPT_PATH constant as single source of truth
- Backend auto-fills empty script_path with latest on save
- New repos use empty script_path (auto-managed by backend)
- Existing repos with pinned versions show warning with opt-in button
- cache_hub_scripts always caches the latest constant
- Rename hubPaths.json gitSync entries to deprecated_ prefix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update ee-repo-ref.txt for git-sync-tests branch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update ee-repo-ref.txt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove upgrade_git_sync_script_paths from save path
Empty script_path is now resolved to latest at job dispatch time in EE,
not on save. Users opt in via the UI button.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use Option<String> for git sync script_path
None means auto-managed (uses LATEST_GIT_SYNC_SCRIPT_PATH),
Some(path) means pinned to a specific script. Resolution happens
at job dispatch time via effective_script_path().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: separate git sync tests into dedicated CI workflow
- Remove git_sync_test from default integration test suite
- Move gitea service to dedicated docker-compose.git-sync.yml
- Add run_git_sync.sh script
- New workflow triggers on changes to git sync crate, hub paths,
ee-repo-ref, or the test files themselves
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add Rust integration tests for git sync filtering logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: run git sync E2E tests via cargo run instead of docker image
Build from source and run Windmill directly, start Gitea as a
standalone container. Tests run against localhost — no pre-built
Docker image needed, works on PRs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add relevance check before running git sync E2E tests
Only run the expensive build+test when actually relevant:
- Direct git sync file changes: always run
- ee-repo-ref.txt changed: check if EE diff touches windmill-git-sync/
- Unrelated changes to workspaces.rs or other files: skip
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove broad path triggers from git sync workflow
Remove workspaces.rs and wmill_integration_test_utils.py from path
triggers - they change too often for unrelated reasons. Keep only
git-sync-specific paths + ee-repo-ref.txt (filtered by check-relevance).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: rewrite git sync E2E tests with full coverage and fix test infra
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: remove accidentally committed gen files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: remove unit/integration tests for git sync filtering (covered by E2E)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use correct build features and pass license key to test step in CI
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add workflow_dispatch trigger to git sync test workflow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update commit reference in ee-repo-ref.txt
* fix: update stats_oss stubs to match EE telemetry signature changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: disable -D warnings for git sync e2e build step
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: allow test connection button in auto-managed git sync mode
The test connection button was disabled and runTestJob() bailed out
when script_path was unset. The test job uses a separate hub script
(gitSyncTest), not the sync script, so the guard was wrong.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update ee-repo-ref to include auto-managed script_path fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use full SHA in ee-repo-ref.txt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback
- Initialize current_count before loop in wait_for_sync_jobs
- Clean up temp directories in clone helpers with addCleanup
- Fail CI startup steps if Gitea/Windmill never become ready
- Assert exact job count in exclude_path test
- Remove docs/git-sync-tests-plan.md (stale planning doc)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove orphaned git_sync.sql fixture
No longer referenced after Rust integration tests were removed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: assert old file removal in rename test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update ee-repo-ref to 612d96a66f9d0cfdae335ef3eb4881f3444ce7cd
This commit updates the EE repository reference after PR #442 was merged in windmill-ee-private.
Previous ee-repo-ref: a05004a7c82f3d1ee5f6863bb9f5a33827d30032
New ee-repo-ref: 612d96a66f9d0cfdae335ef3eb4881f3444ce7cd
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: add force branch param to git sync settings
* update hub path
* chore: update ee-repo-ref to a797dd4d619cdab737e133ce593f2f8582ba21de
This commit updates the EE repository reference after PR #430 was merged in windmill-ee-private.
Previous ee-repo-ref: 373c5870ae5499c8c8a22cb92b2fd3a64a651183
New ee-repo-ref: a797dd4d619cdab737e133ce593f2f8582ba21de
Automated by sync-ee-ref workflow.
* UI nits
* nit
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix(backend): pass parent_path for trigger renames in git sync
When renaming/moving a trigger path, the old path was not included in
the deployment metadata, so git sync never deleted the old file. This
adds parent_path to all 9 trigger DeployedObject variants and computes
it in update_trigger when the path changes.
Fixes#8014
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix path change with common prefix issue
* update ref
* chore: update ee-repo-ref to cb25312072c15c0e9cc375ebc824d41995a52898
This commit updates the EE repository reference after PR #428 was merged in windmill-ee-private.
Previous ee-repo-ref: 7225f7423311f58015a2fab61248c9d89888aef6
New ee-repo-ref: cb25312072c15c0e9cc375ebc824d41995a52898
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* Improve minio on flake.nix
* Add first asset parsing logic for ansible
* Correct html gt sign
* Decouple s3 file picker from drawer
* Factor duplicate code into snippet
* Update S3FilePickerInner to be compatible
* Fix pane shrinking issue
* Git repo viewer
* Change GitRepoViewer
* Endpoints for git repo visualizer
* Move git repo viewer to its own component
* Add button to populate git repo viewer
* Update parser yaml for new ansisble features (repo viewer)
* Reflect parser changes for ansible
* Add button to add the git repo mode of declaration for ansible
* Factor function
* Playbook + inventories into the drawer
* Add button to add inventories from s3
* Move tests to lib.rs
* Inventory loading from s3
* Move get github app token logic to be reused by ansible
* Update parser and ansible executor
* Use the correct path for inventories
* Add nushell to flake for wasm builds
* Add published parser
* Update hubPaths with clone and upload to s3
* Update ee-repo to the branch ref
* Fix npm run check
* Update cargo.lock
* Change labels on buttons
* Remove debug log
* Update ee-repo-ref
* Fix ee issues
* Update ee-repo ref
* Fix typo
* Fix ee
* Update ee-repo-ref
* Fix missing imports
* Unused var
* Fix typo
* Layout improvents
* Fix typos
* Remove unused function and log
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* Add create_ephemeral workspace endpoint
* Add cli devShell
* List ephemeral workspaces + improve endpoint
* Add postgres function to clone a workspace (to be revisited)
* Clone workspace using the postgres function
* Add first iteration of ephemeral workspaces command
* Update display of forked workspaces
* Remove SQLX_OFFLINE
* Add UI to create ephemeral workspace
* Add option to exclude repository from being inherited to forks
* WIP: reworking cloning logic
* Fix cloning
* Fix redirect after creating fork
* Clean up cloning behaviour
* Rename ephemeral to fork
* emove ephemeral_workspaces table in favour of columns in workspaces
* Fix display of forked workspaces
* Fix skip inherit git sync repo setting
* Fix fork invite display + creating fork as user
* Fix SideMenu bug
* Fix alignment
* Simplify migrations
* Update deletion of workspaces
* Delete forked workspace from cli
* Deleting fork workspaces from the UI as non-admin
* Update cli sync and fork creation to adapt to branches and forks
* Update fork prefix
* Remove skip tracking toggle
* Fix npm check warnings
* Fix last npm check
* fix: force stdin to Stdio::null for all user code execution (#6575)
Set stdin to Stdio::null for all Commands that execute user code across all supported languages to prevent unwanted input consumption. This affects Python, Deno, Bash, PowerShell, Go, Rust, PHP, Ruby, Java, C#, Ansible, Nu, and Bun executors.
The dedicated worker handler was intentionally left unchanged as it requires stdin for inter-process communication.
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* Update ee-repo ref
* Update SQLx metadata
* Fix typos
---------
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>