mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
fix: redeploy older app version from deployment history (#9826)
* fix: redeploy older app version from deployment history Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: apply restored app version to low-code editor on redeploy Redeploying an older app version from Deployment History fired the restore callback (toast shown) but the canvas kept displaying the current version, and Deploy then shipped that current value. AppEditor seeds its working state from `appDraftHandle.draft ?? app`, preferring the per-path autosave over the freshly restored `app` prop. The remount triggered by the restore therefore re-read the stale pre-restore draft. `reloadDeployed` already clears the draft before remounting for the reset-to-deployed flow; `onRestore` was missing the same step. Drop the autosave in `onRestore` so the remounted editor seeds from the restored value. Raw apps are unaffected: RawAppEditor binds `files` directly (no draft precedence), and `extractRawApp` mutates that bound state in place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(raw-apps): convert savedNewAppPath event forwarding to a callback prop `svelte-check` (CI `npm check`) failed with one error: forwarding the `savedNewAppPath` createEventDispatcher event through the runes-mode RawAppEditor → RawAppEditorHeader chain types as "not assignable to never". This is the same legacy-forwarding-through-runes pattern already removed for `restore` in this PR — `on:savedNewAppPath` would likewise be dropped at runtime, breaking navigation to the new path after a deploy that renames the app. Replace the `on:savedNewAppPath` forwarding with an `onSavedNewAppPath` callback prop threaded page → RawAppEditor → RawAppEditorHeader, matching `onRestore`. The header now invokes the callback instead of dispatching, and its now-unused createEventDispatcher is removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -83,7 +83,8 @@
|
||||
onResetToDeployed,
|
||||
loadedFromDraft = false,
|
||||
othersDraftsCount = 0,
|
||||
onOpenOthersDrafts
|
||||
onOpenOthersDrafts,
|
||||
onRestore
|
||||
}: AppEditorProps = $props()
|
||||
|
||||
migrateApp(untrack(() => app))
|
||||
@@ -890,7 +891,7 @@
|
||||
{loadedFromDraft}
|
||||
{othersDraftsCount}
|
||||
{onOpenOthersDrafts}
|
||||
on:restore
|
||||
{onRestore}
|
||||
{policy}
|
||||
{fromHub}
|
||||
bind:this={appEditorHeader}
|
||||
|
||||
@@ -112,6 +112,10 @@
|
||||
loadedFromDraft?: boolean
|
||||
othersDraftsCount?: number
|
||||
onOpenOthersDrafts?: () => void
|
||||
// Restoring an older deployment from the history drawer. A callback prop
|
||||
// (not `on:restore` forwarding): forwarding a `createEventDispatcher`
|
||||
// event up through these runes-mode components silently drops it.
|
||||
onRestore?: (restoredApp: any) => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -137,7 +141,8 @@
|
||||
onResetToDeployed,
|
||||
loadedFromDraft = false,
|
||||
othersDraftsCount = 0,
|
||||
onOpenOthersDrafts
|
||||
onOpenOthersDrafts,
|
||||
onRestore
|
||||
}: Props = $props()
|
||||
|
||||
/** Mirror of the path the user is editing in the pen popover. Initialized
|
||||
@@ -862,7 +867,7 @@
|
||||
|
||||
<Drawer bind:open={historyBrowserDrawerOpen} size="1200px">
|
||||
<DrawerContent title="Deployment History" on:close={() => (historyBrowserDrawerOpen = false)}>
|
||||
<DeploymentHistory on:restore appPath={$appPath} />
|
||||
<DeploymentHistory on:restore={(e) => onRestore?.(e.detail)} appPath={$appPath} />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
|
||||
@@ -176,6 +176,10 @@ export interface AppEditorProps {
|
||||
loadedFromDraft?: boolean
|
||||
othersDraftsCount?: number
|
||||
onOpenOthersDrafts?: () => void
|
||||
// Restoring an older deployment from the history drawer. Threaded through
|
||||
// AppEditorHeader as a callback prop rather than `on:restore` forwarding,
|
||||
// which does not propagate through these runes-mode components.
|
||||
onRestore?: (restoredApp: any) => void
|
||||
}
|
||||
|
||||
export type App = {
|
||||
|
||||
@@ -121,6 +121,14 @@
|
||||
onOpenOthersDrafts?: () => void
|
||||
onRuntimeLogRequester?: (requester: RawAppRuntimeLogRequester | undefined) => void
|
||||
onRunsProvider?: (provider: RawAppRunsProvider | undefined) => void
|
||||
// Restoring an older deployment from the history drawer. A callback prop
|
||||
// (not `on:restore` forwarding): forwarding a `createEventDispatcher`
|
||||
// event up through these runes-mode components silently drops it.
|
||||
onRestore?: (restoredApp: any) => void
|
||||
// Deploy created the app at a new path; the page navigates to it. Callback
|
||||
// prop for the same reason as `onRestore` — `on:savedNewAppPath` forwarding
|
||||
// through these runes-mode components is dropped.
|
||||
onSavedNewAppPath?: (path: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -148,7 +156,9 @@
|
||||
othersDraftsCount = 0,
|
||||
onOpenOthersDrafts,
|
||||
onRuntimeLogRequester = undefined,
|
||||
onRunsProvider = undefined
|
||||
onRunsProvider = undefined,
|
||||
onRestore,
|
||||
onSavedNewAppPath
|
||||
}: Props = $props()
|
||||
export const version: number | undefined = undefined
|
||||
|
||||
@@ -1596,8 +1606,8 @@
|
||||
bind:savedApp
|
||||
bind:summary
|
||||
bind:pendingDraftPath
|
||||
on:restore
|
||||
on:savedNewAppPath
|
||||
{onRestore}
|
||||
{onSavedNewAppPath}
|
||||
{policy}
|
||||
{diffDrawer}
|
||||
{newApp}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
Undo,
|
||||
WandSparkles
|
||||
} from 'lucide-svelte'
|
||||
import { createEventDispatcher, untrack } from 'svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { orderedJsonStringify, type Value, replaceFalseWithUndefined } from '../../utils'
|
||||
import { random_adj } from '$lib/components/random_positive_adjetive'
|
||||
|
||||
@@ -151,6 +151,14 @@
|
||||
loadedFromDraft?: boolean
|
||||
othersDraftsCount?: number
|
||||
onOpenOthersDrafts?: () => void
|
||||
// Restoring an older deployment from the history drawer. A callback prop
|
||||
// (not `on:restore` forwarding): forwarding a `createEventDispatcher`
|
||||
// event up through these runes-mode components silently drops it.
|
||||
onRestore?: (restoredApp: any) => void
|
||||
// Deploy created the app at a new path; the page navigates to it. Callback
|
||||
// prop for the same reason as `onRestore` — `on:savedNewAppPath` forwarding
|
||||
// through these runes-mode components is dropped.
|
||||
onSavedNewAppPath?: (path: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -184,7 +192,9 @@
|
||||
onResetToDeployed,
|
||||
loadedFromDraft = false,
|
||||
othersDraftsCount = 0,
|
||||
onOpenOthersDrafts
|
||||
onOpenOthersDrafts,
|
||||
onRestore,
|
||||
onSavedNewAppPath
|
||||
}: Props = $props()
|
||||
|
||||
// Set by the on-behalf-of selector when the publisher picks a user other than
|
||||
@@ -352,7 +362,7 @@
|
||||
path: appPath
|
||||
})
|
||||
}
|
||||
dispatch('savedNewAppPath', path)
|
||||
onSavedNewAppPath?.(path)
|
||||
onDeploy?.({ path })
|
||||
} catch (e) {
|
||||
sendUserToast(`Error creating app: ${e.body ?? e.message}`, true)
|
||||
@@ -475,7 +485,7 @@
|
||||
})
|
||||
}
|
||||
if (appPath !== npath) {
|
||||
dispatch('savedNewAppPath', npath)
|
||||
onSavedNewAppPath?.(npath)
|
||||
}
|
||||
onDeploy?.({ path: npath })
|
||||
}
|
||||
@@ -575,8 +585,6 @@
|
||||
}
|
||||
])
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let customPath = $state(savedApp?.custom_path)
|
||||
let customPathError = $state('')
|
||||
|
||||
@@ -688,7 +696,7 @@
|
||||
|
||||
<Drawer bind:open={historyBrowserDrawerOpen} size="1200px">
|
||||
<DrawerContent title="Deployment History" on:close={() => (historyBrowserDrawerOpen = false)}>
|
||||
<DeploymentHistory on:restore {appPath} />
|
||||
<DeploymentHistory on:restore={(e) => onRestore?.(e.detail)} {appPath} />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
|
||||
@@ -388,9 +388,14 @@
|
||||
|
||||
let diffDrawer: DiffDrawer | undefined = $state()
|
||||
|
||||
function onRestore(ev: any) {
|
||||
function onRestore(restoredApp: any) {
|
||||
sendUserToast('App restored from previous deployment')
|
||||
app = ev.detail
|
||||
// Drop the stale pre-restore autosave. The remounted AppEditor seeds its
|
||||
// state from `appDraftHandle.draft ?? app`, so without this it keeps showing
|
||||
// the old draft instead of the restored version. Same reason `reloadDeployed`
|
||||
// removes the draft before remounting.
|
||||
UserDraft.remove('app', path)
|
||||
app = restoredApp
|
||||
// Re-pin the stale-draft fork base to the current head. A restored value
|
||||
// carries the `parent_version` baked in when that older version was deployed,
|
||||
// which would make the deploy guard (`compareVersions`) falsely report "not on
|
||||
@@ -470,7 +475,7 @@
|
||||
app.path = url
|
||||
}
|
||||
}}
|
||||
on:restore={onRestore}
|
||||
{onRestore}
|
||||
summary={app.summary}
|
||||
app={app.value}
|
||||
{deployedBaseline}
|
||||
|
||||
@@ -449,9 +449,9 @@
|
||||
|
||||
let diffDrawer: DiffDrawer | undefined = $state(undefined)
|
||||
|
||||
function onRestore(ev: any) {
|
||||
function onRestore(restoredApp: any) {
|
||||
sendUserToast('App restored from previous deployment')
|
||||
let prev = ev.detail
|
||||
let prev = restoredApp
|
||||
extractRawApp(prev)
|
||||
savedApp = {
|
||||
summary: prev.summary,
|
||||
@@ -536,12 +536,12 @@
|
||||
{#key redraw}
|
||||
<div class="h-screen">
|
||||
<RawAppEditor
|
||||
on:savedNewAppPath={(event) => {
|
||||
onSavedNewAppPath={(savedPath) => {
|
||||
draftSync.remove()
|
||||
goto(`/apps_raw/edit/${event.detail}`)
|
||||
newPath = event.detail
|
||||
goto(`/apps_raw/edit/${savedPath}`)
|
||||
newPath = savedPath
|
||||
}}
|
||||
on:restore={onRestore}
|
||||
{onRestore}
|
||||
bind:files
|
||||
bind:runnables
|
||||
bind:data
|
||||
|
||||
Reference in New Issue
Block a user