diff --git a/webapp/src/lib/api/generated-client.ts b/webapp/src/lib/api/generated-client.ts index 218f58681..89c3a9265 100644 --- a/webapp/src/lib/api/generated-client.ts +++ b/webapp/src/lib/api/generated-client.ts @@ -423,6 +423,20 @@ export class GeneratedGarmApiClient { const response = await this.hooksApi.getOrgWebhookInfo(orgId); return response.data; } + + async installForgeInstanceWebhook(forgeInstanceId: string, params: any = {}): Promise { + await this.forgeInstancesApi.installForgeInstanceWebhook(forgeInstanceId, params); + } + + async uninstallForgeInstanceWebhook(forgeInstanceId: string): Promise { + await this.forgeInstancesApi.uninstallForgeInstanceWebhook(forgeInstanceId); + } + + async getForgeInstanceWebhookInfo(forgeInstanceId: string): Promise { + const response = await this.forgeInstancesApi.getForgeInstanceWebhookInfo(forgeInstanceId); + return response.data; + } + async listOrganizations(): Promise { const response = await this.organizationsApi.listOrgs(); return response.data || []; @@ -503,8 +517,8 @@ export class GeneratedGarmApiClient { } // Forge Instances - async listForgeInstances(): Promise { - const response = await this.forgeInstancesApi.listForgeInstances(); + async listForgeInstances(endpoint?: string): Promise { + const response = await this.forgeInstancesApi.listForgeInstances(endpoint); return response.data || []; } diff --git a/webapp/src/lib/components/CreateForgeInstanceModal.svelte b/webapp/src/lib/components/CreateForgeInstanceModal.svelte index 387804b55..2bac52b00 100644 --- a/webapp/src/lib/components/CreateForgeInstanceModal.svelte +++ b/webapp/src/lib/components/CreateForgeInstanceModal.svelte @@ -49,18 +49,21 @@ } } - // Only show Gitea credentials - $: filteredCredentials = credentials.filter(cred => cred.forge_type === 'gitea'); - // Only show Gitea endpoints $: giteaEndpoints = endpoints.filter(ep => ep.endpoint_type === 'gitea'); - // Auto-select endpoint from selected credentials + // Filter credentials by selected endpoint + $: filteredCredentials = credentials.filter(cred => + cred.forge_type === 'gitea' && + (!endpointName || cred.endpoint?.name === endpointName) + ); + + // Clear credentials when endpoint changes (if current selection doesn't match) $: { - if (credentialsName) { - const cred = filteredCredentials.find(c => c.name === credentialsName); - if (cred?.endpoint?.name) { - endpointName = cred.endpoint.name; + if (endpointName && credentialsName) { + const stillValid = filteredCredentials.find(c => c.name === credentialsName); + if (!stillValid) { + credentialsName = ''; } } } diff --git a/webapp/src/lib/components/CreatePoolModal.svelte b/webapp/src/lib/components/CreatePoolModal.svelte index c9654819e..5fbda716d 100644 --- a/webapp/src/lib/components/CreatePoolModal.svelte +++ b/webapp/src/lib/components/CreatePoolModal.svelte @@ -1,9 +1,11 @@ - dispatch('close')}> -
-
-

Create New Pool

-
- -
- {#if error} -
-

{error}

-
+ dispatch('close')} + on:submit={handleSubmit} +> + +
+ {#if !initialEntityType} + + {#if poolType !== 'scaleset'} + selectForgeType(e.detail)} + /> {/if} - -
- - Entity Level * - -
- - - - -
-
- - {#if entityLevel} - -
-

- Entity & Provider Configuration -

-
-
- - {#if loadingEntities} -
- {:else} - - {/if} -
-
- - {#if loadingProviders} -
- {:else} - - {/if} -
-
-
- - -
-

- Image & OS Configuration -

-
-
- - -
-
- - -
-
- - -
-
- - -
+ + + + Forge Instance + + {/if}
- - + + {/if} + {/if} + + {#if entityLevel} +
+

+ Entity & Provider Configuration +

+
-
-
- - -
-

- Runner Limits & Timing -

-
-
- - -
-
- - -
-
- - + + {#if loadingProviders} +
+ {:else} + + {/if}
- -
-

- Advanced Settings -

-
-
- - -
-
- - -
-
- - -
-
- - -
-
+ {/if} +
- -
-
- - -
- - - - -
-
- {#if !entityAgentMode} -
- - - - - Shell access requires agent mode to be enabled on the {entityLevel}. - {#if selectedEntityId} - - {/if} - -
- {/if} -
-
- {/if} + +
+ +
+ +
+
- -
- - -
- + +
+ { if (selectedEntityId) showEntityUpdateModal = true; }} + />
- + - + {#if showEntityUpdateModal && selectedEntityId} {@const selectedEntity = getSelectedEntity()} {#if selectedEntity && (entityLevel === 'repository' || entityLevel === 'organization' || entityLevel === 'enterprise')} @@ -790,4 +594,4 @@ on:submit={(e) => handleEntityUpdate(e.detail)} /> {/if} -{/if} \ No newline at end of file +{/if} diff --git a/webapp/src/lib/components/CreateScaleSetModal.svelte b/webapp/src/lib/components/CreateScaleSetModal.svelte index 5e766ea16..4393e465d 100644 --- a/webapp/src/lib/components/CreateScaleSetModal.svelte +++ b/webapp/src/lib/components/CreateScaleSetModal.svelte @@ -1,775 +1,20 @@ - dispatch('close')}> -
-
-

Create New Scale Set

-

Scale sets are only available for GitHub endpoints

-
- -
- {#if error} -
-

{error}

-
- {/if} - - -
- - -
- - -
-
- - Entity Level * - -
- - - -
-
-
- - {#if entityLevel} - -
-

- Entity & Provider Configuration -

-
-
- - {#if loadingEntities} -
- {:else} - - {/if} -
-
- - {#if loadingProviders} -
- {:else} - - {/if} -
-
-
- - -
-

- Image & OS Configuration -

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- - {#if loadingTemplates} -
-
- Loading templates... -
- {:else if templates.length > 0} - -

- Templates define how the runner software is installed and configured. - {#if selectedEntityId} - Showing templates for {getSelectedEntityForgeType()} {osType}. - {/if} -

- {:else if selectedEntityId} -
-

No templates found for {getSelectedEntityForgeType()} {osType}.

-

- - Create a template first - or proceed without a template to use default behavior. -

-
- {:else} -
-

Select an entity first to see available templates

-
- {/if} -
-
- - -
-

- Runner Limits & Timing -

-
-
- - -
-
- - -
-
- - -
-
-
- - -
-

- Advanced Settings -

-
-
- - -
-
- - -
-
- - -
- -
-
- - -
- {#if labels.length > 0} -
- {#each labels as label, index} - - {label} - - - {/each} -
- {/if} -
-

- Labels are used to target workflows to this scale set. If no labels are specified, the scale set name is used as the default label. -

-
- - -
-
- Extra Specs (JSON) -
- -
- - -
- - -
- - -
-
- - -
- - - - -
-
- {#if !entityAgentMode} -
- - - - - Shell access requires agent mode to be enabled on the {entityLevel}. - {#if selectedEntityId} - - {/if} - -
- {/if} -
-
- {/if} - - -
- - -
-
-
-
- - -{#if showEntityUpdateModal && selectedEntityId} - {@const selectedEntity = getSelectedEntity()} - {#if selectedEntity && (entityLevel === 'repository' || entityLevel === 'organization' || entityLevel === 'enterprise')} - showEntityUpdateModal = false} - on:submit={(e) => handleEntityUpdate(e.detail)} - /> - {/if} -{/if} \ No newline at end of file + dispatch('close')} + on:submit={(e) => dispatch('submit', e.detail)} +/> diff --git a/webapp/src/lib/components/Navigation.svelte b/webapp/src/lib/components/Navigation.svelte index 3dc4286dc..4f4180f0d 100644 --- a/webapp/src/lib/components/Navigation.svelte +++ b/webapp/src/lib/components/Navigation.svelte @@ -63,7 +63,7 @@ { href: resolve('/forge-instances'), label: 'Forge Instances', - icon: 'M5 12h14M12 5l7 7-7 7' // Server/circuit icon + icon: 'M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01' // Server rack icon }, { href: resolve('/pools'), diff --git a/webapp/src/lib/components/UpdatePoolModal.svelte b/webapp/src/lib/components/UpdatePoolModal.svelte index f66a85bd7..23a8e1810 100644 --- a/webapp/src/lib/components/UpdatePoolModal.svelte +++ b/webapp/src/lib/components/UpdatePoolModal.svelte @@ -1,19 +1,21 @@ dispatch('close')}>

- Update Pool {pool.id} + {modalTitle}

@@ -309,16 +346,16 @@

{error}

{/if} - + {#if validationError}

{validationError}

{/if} - +
-

Pool Information (Read-only)

+

{infoTitle}

Provider: @@ -333,312 +370,57 @@
- -
-

- Image & OS Configuration -

-
-
- - -
-
- - -
-
- - -
-
- - -
- - -
- - {#if loadingTemplates} -
-
- Loading templates... -
- {:else if templates.length > 0} - -

- Templates define how the runner software is installed and configured. - Showing templates for {getEntityForgeType()} {osType}. -

- {:else} -
-

No templates found for {getEntityForgeType()} {osType}.

-

- - Create a template first - or proceed without a template to use default behavior. -

-
- {/if} -
-
-
- - -
-

- Runner Limits & Timing -

-
-
- - -
-
- - -
-
- - -
-
-
- - -
-

- Advanced Settings -

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - Tags - -
-
- - -
- {#if tags.length > 0} -
- {#each tags as tag, index} - - {tag} - - - {/each} -
- {/if} -
-
-
- - + + {#if poolType === 'scaleset'}
-
- - Extra Specs (JSON) - - -
-
- - -
+ -
+ {/if} - -
-
- - -
- - - - -
-
- {#if !entityAgentMode} -
- - - - - Shell access requires agent mode to be enabled on the {getEntityType(pool).toLowerCase()}. - - -
- {/if} -
-
+ + + + + + + + showEntityUpdateModal = true} + />
@@ -660,7 +442,7 @@ Updating...
{:else} - Update Pool + {submitLabel} {/if}
@@ -673,10 +455,10 @@ {@const entity = getEntity()} {#if entity} showEntityUpdateModal = false} on:submit={(e) => handleEntityUpdate(e.detail)} /> {/if} -{/if} \ No newline at end of file +{/if} diff --git a/webapp/src/lib/components/UpdateScaleSetModal.svelte b/webapp/src/lib/components/UpdateScaleSetModal.svelte index 2fa49a359..16177ce65 100644 --- a/webapp/src/lib/components/UpdateScaleSetModal.svelte +++ b/webapp/src/lib/components/UpdateScaleSetModal.svelte @@ -1,591 +1,19 @@ - dispatch('close')}> -
-
-

- Update Scale Set {scaleSet.name} -

-
- -
- {#if error} -
-

{error}

-
- {/if} - - {#if validationError} -
-

{validationError}

-
- {/if} - - -
-

Scale Set Information

-
-
- Provider: - {scaleSet.provider_name} -
-
- Entity: - - {#if scaleSet.repo_name}Repository: {scaleSet.repo_name} - {:else if scaleSet.org_name}Organization: {scaleSet.org_name} - {:else if scaleSet.enterprise_name}Enterprise: {scaleSet.enterprise_name} - {:else}Unknown Entity{/if} - -
-
-
- - -
- - -
- - -
-

- Image & OS Configuration -

-
-
- - -
-
- - -
-
- - -
-
- - -
- - -
- - {#if loadingTemplates} -
-
- Loading templates... -
- {:else if templates.length > 0} - -

- Templates define how the runner software is installed and configured. - Showing templates for {getEntityForgeType()} {osType}. -

- {:else} -
-

No templates found for {getEntityForgeType()} {osType}.

-

- - Create a template first - or proceed without a template to use default behavior. -

-
- {/if} -
-
-
- - -
-

- Runner Limits & Timing -

-
-
- - -
-
- - -
-
- - -
-
-
- - -
-

- Advanced Settings -

-
-
- - -
-
- - -
-
- - -
-
- - Extra Specs (JSON) - - -
-
- - -
- - -
- - -
-
- - -
- - - - -
-
- {#if !entityAgentMode} -
- - - - - Shell access requires agent mode to be enabled on the {getEntityType()}. - - -
- {/if} -
-
- - -
- - -
-
-
-
- - -{#if showEntityUpdateModal} - {@const entity = getEntity()} - {#if entity} - showEntityUpdateModal = false} - on:submit={(e) => handleEntityUpdate(e.detail)} - /> - {/if} -{/if} \ No newline at end of file + dispatch('close')} + on:submit={(e) => dispatch('submit', e.detail)} +/> diff --git a/webapp/src/lib/components/WebhookSection.svelte b/webapp/src/lib/components/WebhookSection.svelte index b4085f795..5ff2a8333 100644 --- a/webapp/src/lib/components/WebhookSection.svelte +++ b/webapp/src/lib/components/WebhookSection.svelte @@ -6,7 +6,7 @@ import { createEventDispatcher } from 'svelte'; import { extractAPIError } from '$lib/utils/apiError'; - export let entityType: 'repository' | 'organization'; + export let entityType: 'repository' | 'organization' | 'forge_instance'; export let entityId: string; export let entityName: string; @@ -25,8 +25,10 @@ checking = true; if (entityType === 'repository') { webhookInfo = await garmApi.getRepositoryWebhookInfo(entityId); - } else { + } else if (entityType === 'organization') { webhookInfo = await garmApi.getOrganizationWebhookInfo(entityId); + } else if (entityType === 'forge_instance') { + webhookInfo = await garmApi.getForgeInstanceWebhookInfo(entityId); } } catch (err) { // If we get a 404, it means no webhook is installed @@ -49,8 +51,10 @@ if (entityType === 'repository') { await garmApi.installRepositoryWebhook(entityId); - } else { + } else if (entityType === 'organization') { await garmApi.installOrganizationWebhook(entityId); + } else if (entityType === 'forge_instance') { + await garmApi.installForgeInstanceWebhook(entityId); } toastStore.success( @@ -79,8 +83,10 @@ if (entityType === 'repository') { await garmApi.uninstallRepositoryWebhook(entityId); - } else { + } else if (entityType === 'organization') { await garmApi.uninstallOrganizationWebhook(entityId); + } else if (entityType === 'forge_instance') { + await garmApi.uninstallForgeInstanceWebhook(entityId); } toastStore.success( diff --git a/webapp/src/lib/components/WizardModal.svelte b/webapp/src/lib/components/WizardModal.svelte new file mode 100644 index 000000000..119294ae1 --- /dev/null +++ b/webapp/src/lib/components/WizardModal.svelte @@ -0,0 +1,179 @@ + + + dispatch('close')}> +
+ +
+

{title}

+
+ + +
+ +
+ + + {#if error} +
+

{error}

+
+ {/if} + + +
+ {#if currentStep === 0} +
+ +
+ {/if} + {#if currentStep === 1} +
+ +
+ {/if} + {#if currentStep === 2} +
+ +
+ {/if} + {#if currentStep === 3} +
+ +
+ {/if} + {#if currentStep === 4} +
+ +
+ {/if} +
+ + +
+ +
+ {#if currentStep > 0} + + {/if} + {#if !isLastStep} + + {:else} + + {/if} +
+
+
+
diff --git a/webapp/src/lib/components/fields/RunnerAdvancedFields.svelte b/webapp/src/lib/components/fields/RunnerAdvancedFields.svelte new file mode 100644 index 000000000..f2ff21393 --- /dev/null +++ b/webapp/src/lib/components/fields/RunnerAdvancedFields.svelte @@ -0,0 +1,149 @@ + + +
+

+ Advanced Settings +

+
+
+ + +
+ {#if showPriority} +
+ + +
+ {/if} +
+ + +
+
+ + + {#if showTags} + + {/if} + + +
+ + Extra Specs (JSON) + + +
+ + +
+ + +
+ + +
+
+ + +
+ + + + +
+
+ {#if !entityAgentMode} +
+ + + + + Shell access requires agent mode to be enabled on the entity. + + +
+ {/if} +
+
diff --git a/webapp/src/lib/components/fields/RunnerConfigFields.svelte b/webapp/src/lib/components/fields/RunnerConfigFields.svelte new file mode 100644 index 000000000..1bb6b288c --- /dev/null +++ b/webapp/src/lib/components/fields/RunnerConfigFields.svelte @@ -0,0 +1,109 @@ + + +
+

+ Image & OS Configuration +

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + {#if loadingTemplates} +
+
+ Loading templates... +
+ {:else if templates.length > 0} + +

+ Templates define how the runner software is installed and configured. +

+ {:else} +
+

No templates available

+
+ {/if} +
+
diff --git a/webapp/src/lib/components/fields/RunnerLimitsFields.svelte b/webapp/src/lib/components/fields/RunnerLimitsFields.svelte new file mode 100644 index 000000000..6f629d360 --- /dev/null +++ b/webapp/src/lib/components/fields/RunnerLimitsFields.svelte @@ -0,0 +1,52 @@ + + +
+

+ Runner Limits & Timing +

+
+
+ + +
+
+ + +
+
+ + +
+
+
diff --git a/webapp/src/lib/components/fields/TagsField.svelte b/webapp/src/lib/components/fields/TagsField.svelte new file mode 100644 index 000000000..991673e7b --- /dev/null +++ b/webapp/src/lib/components/fields/TagsField.svelte @@ -0,0 +1,70 @@ + + +
+ +
+
+ + +
+ {#if tags.length > 0} +
+ {#each tags as tag, index} + + {tag} + + + {/each} +
+ {/if} +
+
diff --git a/webapp/src/lib/components/forms/EntityForm.svelte b/webapp/src/lib/components/forms/EntityForm.svelte index d640c9660..51bbebef5 100644 --- a/webapp/src/lib/components/forms/EntityForm.svelte +++ b/webapp/src/lib/components/forms/EntityForm.svelte @@ -30,6 +30,8 @@ // Entity type selector export let showEntityTypeSelector = false; + // When true, only show the entity type selector buttons (no form fields below) + export let showEntityTypeSelectorOnly = false; export let idPrefix = ''; @@ -101,28 +103,40 @@

Organization

- + {#if forgeType === 'gitea'} + + {:else} + + {/if}
{/if} +{#if !showEntityTypeSelectorOnly} {#if entityType === 'repository'}
@@ -326,3 +340,4 @@
{/if} {/if} +{/if} diff --git a/webapp/src/lib/components/setup/EntityStep.svelte b/webapp/src/lib/components/setup/EntityStep.svelte index 54ca5a089..e8ec75b78 100644 --- a/webapp/src/lib/components/setup/EntityStep.svelte +++ b/webapp/src/lib/components/setup/EntityStep.svelte @@ -1,11 +1,12 @@