mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-10 16:05:58 +00:00
* fix: scope flow script-edit drawer to session workspace and fix scroll Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: scope flow schema inference to session workspace Thread an optional workspace through loadSchemaFromPath/loadSchemaFlow/ loadSchemaFromModule/loadFlowModuleState/initFlowState/pickScript/pickFlow and pass the op (session) workspace at fork-context call sites, so a flow opened in an AI session resolves path-referenced scripts/subflows against the session workspace instead of the nav workspace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: scope script editor log panel and git-repo pickers to op workspace LogPanel and the ansible git-repo viewer/picker read the nav workspace directly; pass the script editor's op workspace so past-test results/logs and git-repo resource/file lookups target the session workspace in a fork. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: correct session pipeline trigger-editor workspace comment The comment claimed session activation syncs $workspaceStore; SessionPicker intentionally does not, so trigger create/edit/delete from a fork session's pipeline canvas writes to the nav workspace. Document the known limitation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: forward op workspace to git-repo S3 file browser GitRepoViewer scoped its own calls to the op workspace but rendered the nested S3FilePickerInner without workspace={ws}, so the file list/preview/ metadata still queried the nav workspace with a session-workspace prefix. Addresses Codex review on #10025. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
280 lines
8.1 KiB
Svelte
280 lines
8.1 KiB
Svelte
<script lang="ts">
|
|
import { HelpersService, ResourceService } from '$lib/gen'
|
|
import { workspaceStore } from '$lib/stores'
|
|
import { Button, Drawer, DrawerContent } from './common'
|
|
import { GitBranch, Loader2, FolderOpen } from 'lucide-svelte'
|
|
import Select from './select/Select.svelte'
|
|
import { createEventDispatcher } from 'svelte'
|
|
|
|
interface Props {
|
|
open: boolean
|
|
currentResource?: string
|
|
currentCommit?: string
|
|
currentInventories?: string
|
|
currentPlaybook?: string
|
|
gitSshIdentity?: string[]
|
|
/** Acting workspace (fork/session); falls back to the nav workspace. */
|
|
workspace?: string
|
|
}
|
|
|
|
let {
|
|
open = $bindable(),
|
|
currentResource = undefined,
|
|
currentCommit = undefined,
|
|
currentInventories = undefined,
|
|
currentPlaybook = undefined,
|
|
gitSshIdentity = undefined,
|
|
workspace: workspaceProp = undefined
|
|
}: Props = $props()
|
|
|
|
let ws = $derived(workspaceProp ?? $workspaceStore)
|
|
|
|
const dispatch = createEventDispatcher<{
|
|
selected: {
|
|
resourcePath: string
|
|
playbook?: string
|
|
inventoriesLocation?: string
|
|
}
|
|
addInventories: {
|
|
inventoryPaths: string[]
|
|
}
|
|
}>()
|
|
|
|
let drawer: Drawer | undefined = $state(undefined)
|
|
let loading = $state(false)
|
|
let gitRepoResources = $state<{ value: string; label: string }[]>([])
|
|
let selectedResource = $state<string | undefined>(undefined)
|
|
let playbook = $derived(currentPlaybook ?? '')
|
|
let inventoriesLocation = $derived(currentInventories ?? '')
|
|
let loadingInventories = $state(false)
|
|
|
|
async function loadGitRepoResources() {
|
|
if (!ws) return
|
|
|
|
loading = true
|
|
try {
|
|
const resources = await ResourceService.listResource({
|
|
workspace: ws,
|
|
resourceType: 'git_repository'
|
|
})
|
|
|
|
gitRepoResources = resources.map((resource) => ({
|
|
value: resource.path,
|
|
label: resource.path
|
|
}))
|
|
} catch (error) {
|
|
console.error('Failed to load git repository resources:', error)
|
|
gitRepoResources = []
|
|
} finally {
|
|
loading = false
|
|
}
|
|
}
|
|
|
|
$effect(() => {
|
|
if (open && ws) {
|
|
loadGitRepoResources()
|
|
// Set current resource as selected when opening
|
|
selectedResource = currentResource
|
|
drawer?.openDrawer?.()
|
|
} else if (!open) {
|
|
drawer?.closeDrawer?.()
|
|
}
|
|
})
|
|
|
|
function handleSelect() {
|
|
if (selectedResource) {
|
|
dispatch('selected', { resourcePath: selectedResource, playbook, inventoriesLocation })
|
|
selectedResource = undefined
|
|
open = false
|
|
}
|
|
}
|
|
|
|
function handleClose() {
|
|
selectedResource = undefined
|
|
loadingInventories = false
|
|
open = false
|
|
}
|
|
|
|
export async function getInventoryFiles(
|
|
resourcePath: string,
|
|
inventoriesPath: string,
|
|
commitHash: string
|
|
): Promise<string[]> {
|
|
const rootPath = `gitrepos/${ws}/${resourcePath}/${commitHash}/`
|
|
|
|
if (inventoriesPath.startsWith('./')) inventoriesPath = inventoriesPath.slice(2)
|
|
|
|
let files = await HelpersService.listGitRepoFiles({
|
|
workspace: ws!,
|
|
maxKeys: 100,
|
|
marker: undefined,
|
|
prefix: `${rootPath}/${inventoriesPath}`
|
|
})
|
|
|
|
const fileNames = files.windmill_large_files.map((f) => f.s3.slice(rootPath.length))
|
|
|
|
// Return dummy data for testing
|
|
return fileNames
|
|
}
|
|
async function handleAddInventories() {
|
|
if (!selectedResource || !inventoriesLocation.trim()) return
|
|
|
|
loadingInventories = true
|
|
try {
|
|
// Get commit hash if not provided
|
|
let commitHash = currentCommit
|
|
if (!commitHash) {
|
|
try {
|
|
const result = await ResourceService.getGitCommitHash({
|
|
workspace: ws!,
|
|
path: selectedResource,
|
|
gitSshIdentity: gitSshIdentity?.join(',')
|
|
})
|
|
commitHash = result.commit_hash
|
|
} catch (err) {
|
|
console.error('Failed to get commit hash:', err)
|
|
throw new Error('Could not get commit hash for repository')
|
|
}
|
|
}
|
|
|
|
if (!commitHash) {
|
|
throw new Error('No commit hash available')
|
|
}
|
|
|
|
const inventoryFiles = await getInventoryFiles(
|
|
selectedResource,
|
|
inventoriesLocation.trim(),
|
|
commitHash
|
|
)
|
|
|
|
// Dispatch event to update the script with additional_inventories
|
|
dispatch('addInventories', {
|
|
inventoryPaths: inventoryFiles
|
|
})
|
|
|
|
// TODO: Add success feedback
|
|
} catch (error) {
|
|
console.error('Failed to load inventory files:', error)
|
|
// TODO: Add error feedback
|
|
} finally {
|
|
loadingInventories = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<Drawer bind:this={drawer} size="600px">
|
|
<DrawerContent
|
|
title="Select Git Repository Resource"
|
|
on:close={handleClose}
|
|
tooltip="Select a git repository resource to delegate ansible execution to"
|
|
>
|
|
<div class="flex flex-col gap-4 p-4">
|
|
<div class="flex flex-col gap-1">
|
|
<div class="text-xs font-semibold text-emphasis"> Git Repository Resource </div>
|
|
|
|
{#if currentResource}
|
|
<div
|
|
class="p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700 rounded-md"
|
|
>
|
|
<div class="flex items-center gap-2">
|
|
<GitBranch size={16} class="text-blue-600 dark:text-blue-400 flex-shrink-0" />
|
|
<div class="flex-1 min-w-0">
|
|
<div class="text-xs font-semibold text-blue-800 dark:text-blue-200">
|
|
Currently delegating to:
|
|
</div>
|
|
<div class="text-xs text-blue-600 dark:text-blue-300 truncate">
|
|
{currentResource}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if loading}
|
|
<div class="flex items-center gap-2 p-2">
|
|
<Loader2 size={16} class="animate-spin" />
|
|
<span class="text-xs text-primary">Loading git repository resources...</span>
|
|
</div>
|
|
{:else if gitRepoResources.length === 0}
|
|
<div class="p-4 text-center border border-border-light rounded-md">
|
|
<GitBranch size={24} class="mx-auto mb-2 text-primary" />
|
|
<p class="text-xs text-primary mb-2">No git repository resources found</p>
|
|
<p class="text-2xs text-secondary">
|
|
Create a git repository resource first to use this feature
|
|
</p>
|
|
</div>
|
|
{:else}
|
|
<Select
|
|
bind:value={selectedResource}
|
|
items={gitRepoResources}
|
|
placeholder="Select a git repository resource..."
|
|
class="w-full"
|
|
/>
|
|
{/if}
|
|
</div>
|
|
|
|
<!-- Playbook Configuration -->
|
|
<div class="flex flex-col gap-1">
|
|
<div class="text-xs font-semibold text-emphasis">
|
|
Playbook Path
|
|
<span class="text-xs text-primary font-normal ml-1">(optional)</span>
|
|
</div>
|
|
<p class="text-xs text-primary">
|
|
Specify the path to your main playbook file relative to the git repository root
|
|
</p>
|
|
<input
|
|
type="text"
|
|
bind:value={playbook}
|
|
placeholder="e.g., ./playbooks/site.yml"
|
|
class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-surface text-primary placeholder-tertiary focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Inventories Location Configuration -->
|
|
<div class="flex justify-end gap-2 pt-4 border-t border-gray-200 dark:border-gray-700">
|
|
<Button variant="default" on:click={handleClose}>Cancel</Button>
|
|
<Button
|
|
variant="accent"
|
|
disabled={!selectedResource || loading}
|
|
on:click={handleSelect}
|
|
startIcon={{ icon: GitBranch }}
|
|
>
|
|
Apply Configuration
|
|
</Button>
|
|
</div>
|
|
{#if selectedResource}
|
|
<div class="flex flex-col gap-2">
|
|
<div class="text-xs font-semibold text-emphasis"> Inventories Location </div>
|
|
<input
|
|
type="text"
|
|
bind:value={inventoriesLocation}
|
|
placeholder="e.g., hosts"
|
|
class="w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-surface text-primary placeholder-tertiary focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
/>
|
|
<p class="text-xs text-primary">
|
|
Specify the directory containing your inventory files relative to the git repository
|
|
root
|
|
</p>
|
|
|
|
<div class="mt-2">
|
|
<Button
|
|
variant="default"
|
|
unifiedSize="md"
|
|
disabled={loadingInventories || !inventoriesLocation.trim()}
|
|
startIcon={{ icon: loadingInventories ? Loader2 : FolderOpen }}
|
|
onclick={handleAddInventories}
|
|
btnClasses={loadingInventories ? 'animate-pulse' : ''}
|
|
>
|
|
{#if loadingInventories}
|
|
Loading inventories...
|
|
{:else}
|
|
Add available inventories to script as options
|
|
{/if}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</DrawerContent>
|
|
</Drawer>
|