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
This commit is contained in:
hugocasa
2026-07-08 11:29:23 +02:00
co-authored by Claude Fable 5
parent cc552f06e2
commit c8ff4e6096
5 changed files with 82 additions and 13 deletions
@@ -686,9 +686,14 @@ export function createGitSyncContext(workspace: string) {
collapsed: false,
// New connections default to pulling changes from Git (webhook with a
// polling fallback), forks included. Existing repos load without
// auto_pull and stay off.
auto_pull: { enabled: true, mode: 'auto', sync_forks: true },
fork_open_prs: true
// auto_pull and stay off. Auto-pull is EE-only (the backend rejects an
// enabled setting on CE), so only default it on when licensed.
...(get(enterpriseLicense)
? {
auto_pull: { enabled: true, mode: 'auto', sync_forks: true },
fork_open_prs: true
}
: {})
})
gitSyncTestJobs.push({
jobId: '',
@@ -17,7 +17,7 @@
import DetectionFlow from './DetectionFlow.svelte'
import { sendUserToast } from '$lib/toast'
import { fade } from 'svelte/transition'
import { workspaceStore, userWorkspaces } from '$lib/stores'
import { workspaceStore, userWorkspaces, enterpriseLicense } from '$lib/stores'
import type { GitSyncRepository } from './GitSyncContext.svelte'
import GitSyncModeDisplay from './GitSyncModeDisplay.svelte'
import Toggle from '$lib/components/Toggle.svelte'
@@ -632,6 +632,8 @@
{:else}
<Toggle
checked={repo.auto_pull?.enabled ?? false}
disabled={!$enterpriseLicense}
eeOnly
options={{
right: 'Automatically deploy changes from Git',
rightTooltip:
@@ -721,6 +723,8 @@
{#if isGithubApp}
<Toggle
checked={repo.fork_open_prs ?? false}
disabled={!$enterpriseLicense}
eeOnly
options={{
right: 'Open a pull request when a fork deploys',
rightTooltip:
@@ -12,7 +12,7 @@
Edit3
} from 'lucide-svelte'
import GitDiffPreview from '../GitDiffPreview.svelte'
import { JobService } from '$lib/gen'
import { JobService, ResourceService } from '$lib/gen'
import { workspaceStore, userWorkspaces } from '$lib/stores'
import { sendUserToast } from '$lib/toast'
import hubPaths from '$lib/hubPaths.json'
@@ -147,9 +147,29 @@
const workspace = $workspaceStore
if (!workspace) return
// A dev workspace pulls from its environment-label branch (dev/staging),
// not the resource's tracked branch.
// A dev workspace pulls from its environment-label branch (dev/staging)
// and a fork from its wm-fork/<tracked>/<id> branch, not the resource's
// tracked branch. clone_ref falls back to the tracked branch in the
// pull script when the override branch doesn't exist yet.
const currentWs = $userWorkspaces?.find((w) => w.id === workspace)
const isFork = workspace.startsWith('wm-fork-') || Boolean(currentWs?.parent_workspace_id)
let cloneRef: string | undefined = undefined
if (currentWs?.is_dev_workspace) {
cloneRef = currentWs.dev_workspace_label ?? 'dev'
} else if (isFork) {
try {
const resource = await ResourceService.getResource({
workspace,
path: gitRepoResourcePath
})
const trackedBranch = (resource.value as any)?.branch
if (trackedBranch) {
cloneRef = `wm-fork/${trackedBranch}/${workspace.replace(/^wm-fork-/, '')}`
}
} catch (e) {
console.warn('Could not resolve tracked branch for fork pull:', e)
}
}
const payload = {
workspace_id: workspace,
repo_url_resource_path: gitRepoResourcePath,
@@ -159,9 +179,7 @@
settings_json: JSON.stringify(uiState),
use_promotion_overrides:
currentGitSyncSettings?.repositories?.[repoIndex!]?.use_individual_branch === true,
...(currentWs?.is_dev_workspace
? { clone_ref: currentWs.dev_workspace_label ?? 'dev' }
: {})
...(cloneRef ? { clone_ref: cloneRef } : {})
}
const jobId = await JobService.runScriptByPath({