mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
nit(workspace-dependencies): better deployment warning (#7265)
* nit(workspace-dependencies): better deployment warning Signed-off-by: pyranota <pyra@duck.com> * ci Signed-off-by: pyranota <pyra@duck.com> * fix npm check Signed-off-by: pyranota <pyra@duck.com> --------- Signed-off-by: pyranota <pyra@duck.com>
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { WorkspaceService } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Button } from './common'
|
||||
import Alert from './common/alert/Alert.svelte'
|
||||
import Modal from './common/modal/Modal.svelte'
|
||||
import ConfirmationModal from './common/confirmationModal/ConfirmationModal.svelte'
|
||||
import {
|
||||
ChevronRight,
|
||||
ChevronDown,
|
||||
@@ -28,18 +27,20 @@
|
||||
importedPath,
|
||||
title = 'Dependency Warning',
|
||||
confirmText = 'Proceed Anyway',
|
||||
cancelText = 'Cancel',
|
||||
onConfirm,
|
||||
onCancel,
|
||||
open = true
|
||||
open = true,
|
||||
isUnnamedDefault = false,
|
||||
language = undefined
|
||||
}: {
|
||||
importedPath: string
|
||||
title?: string
|
||||
confirmText?: string
|
||||
cancelText?: string
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
open?: boolean
|
||||
isUnnamedDefault?: boolean
|
||||
language?: string
|
||||
} = $props()
|
||||
|
||||
let dependencies: DependencyNode[] = $state([])
|
||||
@@ -88,7 +89,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Load children for a specific dependency
|
||||
// Load children for a specific dependency
|
||||
async function loadChildren(node: DependencyNode) {
|
||||
if (node.children || node.loading) return
|
||||
|
||||
@@ -159,10 +160,13 @@
|
||||
return dependencies.reduce((sum, dep) => sum + countNode(dep), 0)
|
||||
}
|
||||
|
||||
// Load dependencies when component mounts
|
||||
// Load dependencies when component mounts (skip for unnamed/default dependencies)
|
||||
$effect(() => {
|
||||
if ($workspaceStore && importedPath) {
|
||||
if ($workspaceStore && importedPath && !isUnnamedDefault) {
|
||||
loadInitialDependencies()
|
||||
} else if (isUnnamedDefault) {
|
||||
// For unnamed dependencies, skip loading - just show warning
|
||||
loading = false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -175,62 +179,80 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal {open} {title} {cancelText} kind="button" on:canceled={handleCancel} class="sm:max-w-2xl">
|
||||
<ConfirmationModal
|
||||
{open}
|
||||
{title}
|
||||
confirmationText={confirmText}
|
||||
type={isUnnamedDefault ? 'danger' : undefined}
|
||||
onConfirmed={handleConfirm}
|
||||
onCanceled={handleCancel}
|
||||
>
|
||||
{#if loading}
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-600"></div>
|
||||
<span class="text-sm">Loading dependencies...</span>
|
||||
</div>
|
||||
{:else if isUnnamedDefault}
|
||||
<!-- For unnamed/default dependencies, just show the critical warning -->
|
||||
<div class="space-y-3">
|
||||
<p class="font-bold">This will redeploy ALL existing {language} scripts and flows/apps that have {language} steps without explicit named dependencies!</p>
|
||||
<p>Default (unnamed) dependencies are automatically used by any {language} runnable that doesn't specify a named dependency. Changing this affects your entire workspace.</p>
|
||||
<div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-3 border-l-4 border-gray-400 dark:border-gray-600 mt-3">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<Package size={16} class="text-gray-600 dark:text-gray-400" />
|
||||
<span class="font-medium text-gray-800 dark:text-gray-200">
|
||||
Default Workspace Dependencies
|
||||
</span>
|
||||
</div>
|
||||
<div class="font-mono text-sm text-gray-700 dark:text-gray-300 ml-6">
|
||||
{importedPath}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- For named dependencies, show the dependents tree -->
|
||||
<div class="space-y-4">
|
||||
{#if loading}
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="animate-spin rounded-full h-4 w-4 border-b-2 border-yellow-600"></div>
|
||||
<span class="text-sm">Loading dependencies...</span>
|
||||
<!-- Header with workspace dependencies info -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 rounded-lg p-3 border-l-4 border-yellow-400">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<Package size={16} class="text-yellow-600 dark:text-yellow-400" />
|
||||
<span class="font-medium text-yellow-800 dark:text-yellow-200">Workspace Dependencies</span>
|
||||
</div>
|
||||
<div class="font-mono text-sm text-yellow-700 dark:text-yellow-300 ml-6">
|
||||
{importedPath}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if dependencies.length === 0}
|
||||
<Alert type="info" title="No Dependent Runnables Found">
|
||||
{#snippet children()}
|
||||
<p class="text-sm">No dependent runnables were found for these workspace dependencies, but the action will still proceed.</p>
|
||||
{/snippet}
|
||||
</Alert>
|
||||
{:else}
|
||||
<!-- Header with workspace dependencies info -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 rounded-lg p-3 border-l-4 border-yellow-400">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<Package size={16} class="text-yellow-600 dark:text-yellow-400" />
|
||||
<span class="font-medium text-yellow-800 dark:text-yellow-200">Workspace Dependencies</span>
|
||||
</div>
|
||||
<div class="font-mono text-sm text-yellow-700 dark:text-yellow-300 ml-6">
|
||||
{importedPath}
|
||||
</div>
|
||||
<!-- Arrow pointing down -->
|
||||
<div class="flex justify-center">
|
||||
<ArrowRight size={16} class="text-yellow-600 dark:text-yellow-400 transform rotate-90" />
|
||||
</div>
|
||||
|
||||
{#if dependencies.length === 0}
|
||||
<Alert type="info" title="No Dependent Runnables Found">
|
||||
{#snippet children()}
|
||||
<p class="text-sm">No dependent runnables were found for these workspace dependencies, but the action will still proceed.</p>
|
||||
{/snippet}
|
||||
</Alert>
|
||||
{:else}
|
||||
<!-- Arrow pointing down -->
|
||||
<div class="flex justify-center">
|
||||
<ArrowRight size={16} class="text-yellow-600 dark:text-yellow-400 transform rotate-90" />
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/30 rounded p-3">
|
||||
<p class="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
This action will trigger redeployment of <strong>{getTotalDependentsCount()}</strong>
|
||||
{getTotalDependentsCount() === 1 ? 'dependent runnable' : 'dependent runnables'}:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Dependency tree -->
|
||||
<div class="space-y-1 max-h-64 overflow-y-auto">
|
||||
{#each dependencies as dependency}
|
||||
{@render DependencyNode({ node: dependency, level: 0 })}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Summary -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/30 rounded p-3">
|
||||
<p class="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
This action will trigger redeployment of <strong>{getTotalDependentsCount()}</strong>
|
||||
{getTotalDependentsCount() === 1 ? 'dependent runnable' : 'dependent runnables'}:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Dependency tree -->
|
||||
<div class="space-y-1 max-h-64 overflow-y-auto">
|
||||
{#each dependencies as dependency}
|
||||
{@render DependencyNode({ node: dependency, level: 0 })}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button size="sm" color="warning" on:click={handleConfirm}>
|
||||
{confirmText}
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
{/if}
|
||||
</ConfirmationModal>
|
||||
|
||||
{#snippet DependencyNode({ node, level }: { node: DependencyNode, level: number })}
|
||||
{@const Icon = getIcon(node.kind)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { WorkspaceDependenciesService, WorkspaceService, type ScriptLang, type WorkspaceDependencies } from '$lib/gen'
|
||||
import { WorkspaceDependenciesService, type ScriptLang, type WorkspaceDependencies } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Button } from './common'
|
||||
import Drawer from './common/drawer/Drawer.svelte'
|
||||
@@ -311,39 +311,20 @@ numpy>=1.24.0
|
||||
}
|
||||
|
||||
async function handleDeployClick(): Promise<void> {
|
||||
// For updates, check for dependents and show warning
|
||||
if (edit) {
|
||||
const existingPath = getWorkspaceDependenciesPath(initialName ?? null, initialLanguage)
|
||||
const isUnnamed = workspaceDependenciesType === 'workspace'
|
||||
const path = getWorkspaceDependenciesPath(
|
||||
isUnnamed ? null : workspaceDependenciesName || initialName || null,
|
||||
edit ? initialLanguage : workspaceDependencies.language
|
||||
)
|
||||
|
||||
if (existingPath === null) {
|
||||
sendUserToast('Unsupported language for enforced dependencies path generation', true)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if there are any dependents
|
||||
const dependents = await WorkspaceService.getDependents({
|
||||
workspace: $workspaceStore!,
|
||||
importedPath: existingPath
|
||||
})
|
||||
|
||||
// Only show warning if there are actually dependents
|
||||
if (dependents.length > 0) {
|
||||
currentImportedPath = existingPath
|
||||
showWarning = true
|
||||
} else {
|
||||
// No dependents, proceed directly
|
||||
await updateWorkspaceDependencies()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking dependents:', error)
|
||||
// On error, proceed without warning
|
||||
await updateWorkspaceDependencies()
|
||||
}
|
||||
} else {
|
||||
// New workspace dependencies, no need to check for dependents
|
||||
await updateWorkspaceDependencies()
|
||||
if (path === null) {
|
||||
sendUserToast('Unsupported language for enforced dependencies path generation', true)
|
||||
return
|
||||
}
|
||||
|
||||
// Always show warning - for all types and operations
|
||||
currentImportedPath = path
|
||||
showWarning = true
|
||||
}
|
||||
|
||||
function confirmDeploy(): void {
|
||||
@@ -372,11 +353,12 @@ numpy>=1.24.0
|
||||
{#if showWarning && currentImportedPath}
|
||||
<DependenciesDeploymentWarning
|
||||
importedPath={currentImportedPath}
|
||||
title="Deployment Warning"
|
||||
confirmText="Deploy Anyway"
|
||||
cancelText="Cancel"
|
||||
title={workspaceDependenciesType === 'workspace' ? "Default Dependencies Deployment" : "Deployment Warning"}
|
||||
confirmText={workspaceDependenciesType === 'workspace' ? "I'm Sure - Deploy Default" : "Deploy Anyway"}
|
||||
onConfirm={confirmDeploy}
|
||||
onCancel={cancelDeploy}
|
||||
isUnnamedDefault={workspaceDependenciesType === 'workspace'}
|
||||
language={LANGUAGE_OPTIONS.find(opt => opt.value === workspaceDependencies.language)?.label || workspaceDependencies.language}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -386,7 +386,6 @@
|
||||
importedPath={currentImportedPath}
|
||||
title={warningTitle}
|
||||
confirmText={warningConfirmText}
|
||||
cancelText="Cancel"
|
||||
onConfirm={handleWarningConfirm}
|
||||
onCancel={handleWarningCancel}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user