Merge pull request #2988 from appwrite/feat-deployment-retention-ui

feat: add deployment retention settings
This commit is contained in:
Harsh Mahajan
2026-04-22 20:12:46 +05:30
committed by GitHub
5 changed files with 247 additions and 0 deletions
+2
View File
@@ -336,6 +336,7 @@ export enum Submit {
FunctionUpdateScopes = 'submit_function_key_update_scopes',
FunctionUpdateRuntime = 'submit_function_update_runtime',
FunctionUpdateBuildCommand = 'submit_function_update_build_command',
FunctionUpdateDeploymentRetention = 'submit_function_update_deployment_retention',
FunctionConnectRepo = 'submit_function_connect_repo',
FunctionDisconnectRepo = 'submit_function_disconnect_repo',
FunctionRedeploy = 'submit_function_redeploy',
@@ -458,6 +459,7 @@ export enum Submit {
SiteUpdateScopes = 'submit_site_key_update_scopes',
SiteUpdateBuildSettings = 'submit_site_update_build_settings',
SiteUpdateResourceLimits = 'submit_site_update_resource_limits',
SiteUpdateDeploymentRetention = 'submit_site_update_deployment_retention',
SiteUpdateSinglePageApplication = 'submit_site_update_single_page_application',
SiteConnectRepo = 'submit_site_connect_repo',
SiteRedeploy = 'submit_site_redeploy',
@@ -19,6 +19,7 @@
import UpdateRepository from './updateRepository.svelte';
import UpdateBuildCommand from './updateBuildCommand.svelte';
import UpdateResourceLimits from './updateResourceLimits.svelte';
import UpdateDeploymentRetention from './updateDeploymentRetention.svelte';
import { isCloud } from '$lib/system';
import UpdateVariables from '$routes/(console)/project-[region]-[project]/updateVariables.svelte';
import { page } from '$app/state';
@@ -108,6 +109,7 @@
project={data.project}
analyticsSource="function_settings" />
<UpdateBuildCommand func={data.function} />
<UpdateDeploymentRetention func={data.function} />
<UpdatePermissions />
{#if isCloud}
@@ -0,0 +1,123 @@
<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, InputSelect, InputSwitch } from '$lib/elements/forms';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
let { func }: { func: Models.Function } = $props();
const MAX_DEPLOYMENT_RETENTION = 36500;
const DEPLOYMENT_RETENTION_OPTIONS = [
{ value: 7, label: '1 Week' },
{ value: 30, label: '1 Month' },
{ value: 90, label: '3 Months' },
{ value: 180, label: '6 Months' },
{ value: 365, label: '1 Year' },
{ value: 730, label: '2 Years' },
{ value: 1825, label: '5 Years' },
{ value: 3650, label: '10 Years' }
];
const getInitialDeploymentRetention = () => func.deploymentRetention;
const getRetentionOptions = (retention: number) => {
const hasCurrentOption = DEPLOYMENT_RETENTION_OPTIONS.some(
(option) => option.value === retention
);
if (retention < 1 || retention > MAX_DEPLOYMENT_RETENTION || hasCurrentOption) {
return DEPLOYMENT_RETENTION_OPTIONS;
}
return [{ value: retention, label: `${retention} days` }, ...DEPLOYMENT_RETENTION_OPTIONS];
};
let unlimitedRetention = $state(getInitialDeploymentRetention() === 0);
let retentionDays = $state(
getInitialDeploymentRetention() > 0 ? getInitialDeploymentRetention() : 30
);
const retentionOptions = $derived(getRetentionOptions(retentionDays));
const deploymentRetention = $derived(unlimitedRetention ? 0 : retentionDays);
let isUnchanged = $derived(func.deploymentRetention === deploymentRetention);
let isInvalid = $derived(
!unlimitedRetention &&
(retentionDays === null ||
retentionDays < 1 ||
retentionDays > MAX_DEPLOYMENT_RETENTION)
);
async function update() {
try {
if (!isValueOfStringEnum(Runtime, func.runtime)) {
throw new Error(`Invalid runtime: ${func.runtime}`);
}
await sdk.forProject(page.params.region, page.params.project).functions.update({
functionId: func.$id,
name: func.name,
runtime: func.runtime,
execute: func.execute || undefined,
events: func.events || 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 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,
runtimeSpecification: func.runtimeSpecification || undefined,
deploymentRetention
});
await invalidate(Dependencies.FUNCTION);
addNotification({
type: 'success',
message: 'Deployment retention has been updated'
});
trackEvent(Submit.FunctionUpdateDeploymentRetention);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.FunctionUpdateDeploymentRetention);
}
}
</script>
<Form onSubmit={update}>
<CardGrid>
<svelte:fragment slot="title">Deployment retention</svelte:fragment>
Keep active deployments and choose when inactive deployments are deleted.
<svelte:fragment slot="aside">
<InputSwitch
id="deployment-retention-unlimited"
label="Keep deployments forever"
bind:value={unlimitedRetention} />
{#if !unlimitedRetention}
<InputSelect
id="deployment-retention"
label="Keep for"
placeholder="1 Month"
options={retentionOptions}
required
bind:value={retentionDays} />
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={isUnchanged || isInvalid} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -16,6 +16,7 @@
import UpdateResourceLimits from './updateResourceLimits.svelte';
import UpdateVariables from '$routes/(console)/project-[region]-[project]/updateVariables.svelte';
import UpdateLogging from './updateLogging.svelte';
import UpdateDeploymentRetention from './updateDeploymentRetention.svelte';
export let data;
@@ -81,6 +82,7 @@
{#if isCloud}
<UpdateResourceLimits site={data.site} specs={data.specificationsList} />
{/if}
<UpdateDeploymentRetention site={data.site} />
<UpdateTimeout site={data.site} />
<UpdateLogging site={data.site} />
<DangerZone site={data.site} />
@@ -0,0 +1,118 @@
<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, InputSelect, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Adapter, BuildRuntime, Framework, type Models } from '@appwrite.io/console';
let { site }: { site: Models.Site } = $props();
const MAX_DEPLOYMENT_RETENTION = 36500;
const DEPLOYMENT_RETENTION_OPTIONS = [
{ value: 7, label: '1 Week' },
{ value: 30, label: '1 Month' },
{ value: 90, label: '3 Months' },
{ value: 180, label: '6 Months' },
{ value: 365, label: '1 Year' },
{ value: 730, label: '2 Years' },
{ value: 1825, label: '5 Years' },
{ value: 3650, label: '10 Years' }
];
const getInitialDeploymentRetention = () => site.deploymentRetention;
const getRetentionOptions = (retention: number) => {
const hasCurrentOption = DEPLOYMENT_RETENTION_OPTIONS.some(
(option) => option.value === retention
);
if (retention < 1 || retention > MAX_DEPLOYMENT_RETENTION || hasCurrentOption) {
return DEPLOYMENT_RETENTION_OPTIONS;
}
return [{ value: retention, label: `${retention} days` }, ...DEPLOYMENT_RETENTION_OPTIONS];
};
let unlimitedRetention = $state(getInitialDeploymentRetention() === 0);
let retentionDays = $state(
getInitialDeploymentRetention() > 0 ? getInitialDeploymentRetention() : 30
);
const retentionOptions = $derived(getRetentionOptions(retentionDays));
const deploymentRetention = $derived(unlimitedRetention ? 0 : retentionDays);
let isUnchanged = $derived(site.deploymentRetention === deploymentRetention);
let isInvalid = $derived(
!unlimitedRetention &&
(retentionDays === null ||
retentionDays < 1 ||
retentionDays > MAX_DEPLOYMENT_RETENTION)
);
async function update() {
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,
outputDirectory: site.outputDirectory || undefined,
buildRuntime: (site.buildRuntime as BuildRuntime) || undefined,
adapter: site.adapter as Adapter,
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,
runtimeSpecification: site.runtimeSpecification || undefined,
deploymentRetention
});
await invalidate(Dependencies.SITE);
addNotification({
type: 'success',
message: 'Deployment retention has been updated'
});
trackEvent(Submit.SiteUpdateDeploymentRetention);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.SiteUpdateDeploymentRetention);
}
}
</script>
<Form onSubmit={update}>
<CardGrid>
<svelte:fragment slot="title">Deployment retention</svelte:fragment>
Keep active deployments and choose when inactive deployments are deleted.
<svelte:fragment slot="aside">
<InputSwitch
id="deployment-retention-unlimited"
label="Keep deployments forever"
bind:value={unlimitedRetention} />
{#if !unlimitedRetention}
<InputSelect
id="deployment-retention"
label="Keep for"
placeholder="1 Month"
options={retentionOptions}
required
bind:value={retentionDays} />
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={isUnchanged || isInvalid} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>