Files
windmill/frontend/src/lib/components/WorkspaceDeployLayout.svelte
T
Ruben Fiszel c57c769dea feat: add CI test scripts with auto-trigger on deploy (#8736)
* feat: add CI test scripts with auto-trigger on deploy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: fix annotation parser early return and handle renames correctly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move CI test results to top of script/flow detail pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: improve CI test results spacing, icon, and remove pass label

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: support one-line annotation and use script/path format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: move CI test trigger logic to EE

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: move CI badge next to New badge and add deduplicated CI summary

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add CI test e2e tests and fix nullable column annotations

Add integration tests for CI test annotation parsing (creates/removes
ci_test_reference rows) and the CI test results API (single + batch
endpoints). Add backend test for auto-trigger on deploy (private+python).

Fix sqlx LEFT JOIN LATERAL nullable column annotations in
get_ci_test_results and get_ci_test_results_batch queries — sqlx
cannot infer nullability from LATERAL subqueries, causing runtime
decode errors when no matching job exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix build/sqlx

* fix

* feat: CI test improvements and templates

- Fix windmill-dep-map/private feature propagation in worker, api-scripts,
  and api-flows Cargo.toml so CI test triggers actually fire in EE mode
- Clone ci_test_reference rows during workspace fork
- Add polling to CiTestResults component (refetch every 3s while running)
- Add running state and auto-refresh to ForkWorkspaceBanner CI summary
- Add yellow "CI test" badge on script list rows and detail page
- Fix Library badge border color (remove indigo border override)
- Add CI Test TypeScript and CI Test Python templates in ScriptBuilder
- Update sqlx offline cache
- Add debug tracing for CI test trigger in worker_lockfiles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add missing children prop to WorkspaceDeployLayout

Fixes svelte-fast-check type error when passing named snippets as
children content inside the component tag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review feedback

- Remove empty wrapper divs around CiTestResults, move mb-4 into component
- Add batch endpoint size cap (max 200 items)
- Add ON DELETE CASCADE to ci_test_reference workspace FK (new migration)
- Downgrade CI test trigger logs from info to debug
- Fix false-positive polling: only treat status='running' as running,
  not null status (CiTestResults, CompareWorkspaces, ForkWorkspaceBanner)
- Fix test numbering in integration tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to latest EE commit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref to d9d68c2406df0b59f413ea0b2cb24780a9817d04

This commit updates the EE repository reference after PR #516 was merged in windmill-ee-private.

Previous ee-repo-ref: d7ccd9b86da99ec056a0e8708e3637d64290387a

New ee-repo-ref: d9d68c2406df0b59f413ea0b2cb24780a9817d04

Automated by sync-ee-ref workflow.

* fix: treat queued jobs (job_id set, null status) as running

Jobs that have been pushed but not yet picked up by a worker have a
job_id but null status. Treat these as 'running' to avoid showing
misleading 'pass' badges or '0 passing'. Tests that were never
triggered (no job_id, null status) remain neutral/hidden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: hugocasa <hugo@casademont.ch>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-04-09 17:21:36 +00:00

158 lines
3.9 KiB
Svelte

<script lang="ts">
import { Loader2 } from 'lucide-svelte'
import { Badge } from './common'
import Row from './common/table/Row.svelte'
import Tooltip from './Tooltip.svelte'
import type { Snippet } from 'svelte'
import type { Kind } from '$lib/utils_deployable'
interface DeployableItem {
key: string
path: string
kind: Kind
triggerKind?: string
[key: string]: unknown
}
interface Props {
items: DeployableItem[]
selectedItems: string[]
selectablePredicate?: (item: DeployableItem) => boolean
deploymentStatus: Record<string, { status: 'loading' | 'deployed' | 'failed'; error?: string }>
allSelected?: boolean
emptyMessage?: string
children?: Snippet
// Snippets for customization
header?: Snippet
alerts?: Snippet
itemSummary?: Snippet<[DeployableItem]>
itemActions?: Snippet<[DeployableItem]>
footer?: Snippet
// Callbacks
onToggleItem?: (item: DeployableItem) => void
onSelectAll?: () => void
onDeselectAll?: () => void
}
let {
items,
selectedItems,
selectablePredicate = () => true,
deploymentStatus,
allSelected = false,
emptyMessage = 'No items to deploy',
header,
alerts,
itemSummary,
itemActions,
footer,
onToggleItem,
onSelectAll,
onDeselectAll
}: Props = $props()
let selectableItems = $derived(items.filter(selectablePredicate))
let hasSelectableItems = $derived(selectableItems.length > 0)
</script>
<div class="flex flex-col h-full">
<!-- Header section -->
{#if header}
<div class="bg-surface">
{@render header()}
</div>
{/if}
<!-- Alerts section -->
{#if alerts}
{@render alerts()}
{/if}
{#if items.length > 0}
<!-- Select all row -->
<div class="px-4 py-2 flex items-center justify-between">
<div
class="flex items-center gap-2 text-secondary text-xs"
class:opacity-50={!hasSelectableItems}
>
<input
type="checkbox"
disabled={!hasSelectableItems}
checked={allSelected}
onchange={allSelected ? onDeselectAll : onSelectAll}
class="rounded max-w-4 w-full"
/> Select all
</div>
</div>
<!-- Items list -->
<div class="overflow-y-auto">
<div class="border rounded-md bg-surface-tertiary">
{#each items as item (item.key)}
{@const isSelectable = selectablePredicate(item)}
{@const isSelected = selectedItems.includes(item.key)}
{@const status = deploymentStatus[item.key]}
{@const isDeployed = status?.status === 'deployed'}
<Row
isSelectable={isSelectable && !isDeployed}
alignWithSelectable={true}
disabled={!isSelectable}
selected={isSelected && !isDeployed}
onSelect={() => onToggleItem?.(item)}
path={item.kind !== 'resource' &&
item.kind !== 'variable' &&
item.kind !== 'resource_type'
? item.path
: ''}
marked={undefined}
kind={item.kind}
triggerKind={item.triggerKind}
canFavorite={false}
workspaceId=""
>
{#snippet customSummary()}
{#if itemSummary}
{@render itemSummary(item)}
{:else}
{item.path}
{/if}
{/snippet}
{#snippet actions()}
{#if itemActions}
{@render itemActions(item)}
{/if}
<!-- Deployment status always shown -->
{#if status}
{#if status.status === 'loading'}
<Loader2 class="animate-spin" />
{:else if status.status === 'deployed'}
<Badge color="green">Deployed</Badge>
{:else if status.status === 'failed'}
<div class="inline-flex gap-1">
<Badge color="red">Failed</Badge>
<Tooltip>{status.error}</Tooltip>
</div>
{/if}
{/if}
{/snippet}
</Row>
{/each}
</div>
</div>
{:else}
<div class="flex items-center justify-center h-full">
<div class="text-hint text-xs">{emptyMessage}</div>
</div>
{/if}
<!-- Footer -->
{#if footer}
<div class="pt-4">
{@render footer()}
</div>
{/if}
</div>