Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { Button } from '$lib/elements/forms';
import { Click, trackEvent } from '$lib/actions/analytics';
import UpdateRepository from './updateRepository.svelte';
import UpdateBuildTriggers from './updateBuildTriggers.svelte';
import UpdateBuildCommand from './updateBuildCommand.svelte';
import UpdateResourceLimits from './updateResourceLimits.svelte';
import UpdateDeploymentRetention from './updateDeploymentRetention.svelte';
Expand Down Expand Up @@ -105,6 +106,7 @@
<UpdateRuntime runtimesList={data.runtimesList} />
{#key data.function.providerRepositoryId}
<UpdateRepository func={data.function} installations={data.installations} />
<UpdateBuildTriggers func={data.function} />
{/key}

<UpdateVariables
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/state';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputTags } from '$lib/elements/forms';
import { symmetricDifference } from '$lib/helpers/array';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { type Models, Runtime, type Scopes } from '@appwrite.io/console';
import { Input, Layout } from '@appwrite.io/pink-svelte';

export let func: Models.Function;

type FuncWithTriggers = Models.Function & {
providerBranches?: string[];
providerPaths?: string[];
};

function normalizeTriggerPatterns(patterns: string[] = []) {
return [...new Set(patterns.map((pattern) => pattern.trim()).filter(Boolean))];
}

let providerBranches: string[] = normalizeTriggerPatterns(
(func as FuncWithTriggers).providerBranches
);
let providerPaths: string[] = normalizeTriggerPatterns(
(func as FuncWithTriggers).providerPaths
);
let savedBranches = normalizeTriggerPatterns((func as FuncWithTriggers).providerBranches);
let savedPaths = normalizeTriggerPatterns((func as FuncWithTriggers).providerPaths);
Comment on lines +15 to +32
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Existing trigger configuration never loaded

providerBranches and providerPaths are not present on Models.Function (nor Models.Site in the sites component), so the as FuncWithTriggers cast silently resolves to undefined at runtime. normalizeTriggerPatterns then defaults to [], meaning the form always opens empty even when the server already has branch/path filters stored. A user who saves without noticing will silently clear all existing trigger config. If the SDK response does carry these fields at runtime (without them being reflected in TypeScript types), the type augmentation workaround should be accompanied by a comment explaining that, and the PR should ensure the SDK pin is bumped to a version that properly types these fields.


async function update() {
providerBranches = normalizeTriggerPatterns(providerBranches);
providerPaths = normalizeTriggerPatterns(providerPaths);

try {
await sdk.forProject(page.params.region, page.params.project).functions.update({
functionId: func.$id,
name: func.name,
runtime: func.runtime as Runtime,
execute: func.execute?.length ? (func.execute as string[]) : undefined,
events: func.events?.length ? (func.events as string[]) : undefined,
schedule: func.schedule ?? undefined,
timeout: func.timeout ?? undefined,
enabled: func.enabled ?? undefined,
logging: func.logging ?? undefined,
entrypoint: func.entrypoint ?? undefined,
commands: func.commands ?? undefined,
scopes: func.scopes?.length ? (func.scopes as Scopes[]) : undefined,
installationId: func.installationId ?? undefined,
providerRepositoryId: func.providerRepositoryId ?? undefined,
providerBranch: func.providerBranch ?? undefined,
providerSilentMode: func.providerSilentMode ?? undefined,
providerRootDirectory: func.providerRootDirectory ?? undefined,
buildSpecification: func.buildSpecification ?? undefined,
providerBranches,
providerPaths
});
savedBranches = [...providerBranches];
savedPaths = [...providerPaths];
await invalidate(Dependencies.FUNCTION);
addNotification({
type: 'success',
message: 'Build triggers have been updated successfully'
});
trackEvent(Submit.FunctionUpdateConfiguration);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.FunctionUpdateConfiguration);
}
}

$: isDisabled =
Comment on lines +14 to +78
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Legacy Svelte 4 syntax in new component

Both new updateBuildTriggers.svelte files use export let props and $: reactive declarations, while AGENTS.md explicitly requires Svelte 5 runes ($props(), $state(), $derived()) for all new and modified code. The sibling updateBuildSettings.svelte in the sites settings already uses runes. The isDisabled reactive declaration and the func prop should be migrated to $derived() and $props() respectively. The same pattern is repeated in the sites counterpart file.

!symmetricDifference(providerBranches, savedBranches).length &&
!symmetricDifference(providerPaths, savedPaths).length;
</script>

{#if func.providerRepositoryId}
<Form onSubmit={update}>
<CardGrid>
<svelte:fragment slot="title">Build triggers</svelte:fragment>
Control which branch pushes and file changes trigger automatic deployments. Use glob patterns
to include or exclude specific branches and paths.
<svelte:fragment slot="aside">
<Layout.Stack gap="xl">
<Layout.Stack gap="s">
<InputTags
id="providerBranches"
label="Branch filters"
placeholder={providerBranches.length
? ''
: 'e.g. main, feat/*, !hotfix/*'}
bind:tags={providerBranches} />
<Input.Helper state="default"
>Leave empty to deploy on all branches. Prefix with <code>!</code> to exclude.</Input.Helper>
</Layout.Stack>
<Layout.Stack gap="s">
<InputTags
id="providerPaths"
label="Path filters"
placeholder={providerPaths.length ? '' : 'e.g. src/**, !docs/**'}
bind:tags={providerPaths} />
<Input.Helper state="default"
>Leave empty to deploy on all file changes. Prefix with <code>!</code>
to exclude.</Input.Helper>
</Layout.Stack>
</Layout.Stack>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={isDisabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import UpdateTimeout from './updateTimeout.svelte';
import UpdateRuntimeSettings from './updateRuntimeSettings.svelte';
import UpdateRepository from './updateRepository.svelte';
import UpdateBuildTriggers from './updateBuildTriggers.svelte';
import { onMount } from 'svelte';
import { showConnectRepo } from './store';
import { isCloud } from '$lib/system';
Expand Down Expand Up @@ -71,6 +72,7 @@
<UpdateName site={data.site} />
{#key data.site.providerRepositoryId}
<UpdateRepository site={data.site} installations={data.installations} />
<UpdateBuildTriggers site={data.site} />
{/key}
<UpdateBuildSettings
site={data.site}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/state';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputTags } from '$lib/elements/forms';
import { symmetricDifference } from '$lib/helpers/array';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Adapter, BuildRuntime, Framework, type Models } from '@appwrite.io/console';
import { Input, Layout } from '@appwrite.io/pink-svelte';

export let site: Models.Site;

type SiteWithTriggers = Models.Site & { providerBranches?: string[]; providerPaths?: string[] };

function normalizeTriggerPatterns(patterns: string[] = []) {
return [...new Set(patterns.map((pattern) => pattern.trim()).filter(Boolean))];
}

let providerBranches: string[] = normalizeTriggerPatterns(
(site as SiteWithTriggers).providerBranches
);
let providerPaths: string[] = normalizeTriggerPatterns(
(site as SiteWithTriggers).providerPaths
);
let savedBranches = normalizeTriggerPatterns((site as SiteWithTriggers).providerBranches);
let savedPaths = normalizeTriggerPatterns((site as SiteWithTriggers).providerPaths);

async function update() {
providerBranches = normalizeTriggerPatterns(providerBranches);
providerPaths = normalizeTriggerPatterns(providerPaths);

try {
await sdk.forProject(page.params.region, page.params.project).sites.update({
siteId: site.$id,
name: site.name,
framework: site.framework as Framework,
enabled: site.enabled ?? undefined,
logging: site.logging ?? undefined,
timeout: site.timeout ?? undefined,
installCommand: site.installCommand ?? undefined,
buildCommand: site.buildCommand ?? undefined,
startCommand: site.startCommand ?? undefined,
outputDirectory: site.outputDirectory ?? undefined,
buildRuntime: (site.buildRuntime as BuildRuntime) ?? undefined,
adapter: (site.adapter as Adapter) ?? undefined,
fallbackFile: site.fallbackFile ?? undefined,
installationId: site.installationId ?? undefined,
providerRepositoryId: site.providerRepositoryId ?? undefined,
providerBranch: site.providerBranch ?? undefined,
providerSilentMode: site.providerSilentMode ?? undefined,
providerRootDirectory: site.providerRootDirectory ?? undefined,
buildSpecification: site.buildSpecification ?? undefined,
providerBranches,
providerPaths
});
savedBranches = [...providerBranches];
savedPaths = [...providerPaths];
await invalidate(Dependencies.SITE);
addNotification({
type: 'success',
message: 'Build triggers have been updated successfully'
});
trackEvent(Submit.SiteUpdateConfiguration);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.SiteUpdateConfiguration);
}
}

$: isDisabled =
!symmetricDifference(providerBranches, savedBranches).length &&
!symmetricDifference(providerPaths, savedPaths).length;
</script>

{#if site.providerRepositoryId}
<Form onSubmit={update}>
<CardGrid>
<svelte:fragment slot="title">Build triggers</svelte:fragment>
Control which branch pushes and file changes trigger automatic deployments. Use glob patterns
to include or exclude specific branches and paths.
<svelte:fragment slot="aside">
<Layout.Stack gap="xl">
<Layout.Stack gap="s">
<InputTags
id="providerBranches"
label="Branch filters"
placeholder={providerBranches.length
? ''
: 'e.g. main, feat/*, !hotfix/*'}
bind:tags={providerBranches} />
<Input.Helper state="default"
>Leave empty to deploy on all branches. Prefix with <code>!</code> to exclude.</Input.Helper>
</Layout.Stack>
<Layout.Stack gap="s">
<InputTags
id="providerPaths"
label="Path filters"
placeholder={providerPaths.length ? '' : 'e.g. src/**, !docs/**'}
bind:tags={providerPaths} />
<Input.Helper state="default"
>Leave empty to deploy on all file changes. Prefix with <code>!</code>
to exclude.</Input.Helper>
</Layout.Stack>
</Layout.Stack>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={isDisabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
{/if}
Loading