Skip to content

Conversation

@hmacr
Copy link
Contributor

@hmacr hmacr commented Oct 28, 2025

Task: https://linear.app/appwrite/issue/SER-455/improve-ux-for-sitesfunction-deployment-from-uploaded-file

Summary by CodeRabbit

  • New Features

    • Upload function and site deployments with real-time progress and status updates.
    • Site deployments support custom build/install commands and output directory.
    • Immediate “upload started” notifications and automatic navigation once uploads succeed.
    • Unified uploader used across creation flows for more reliable deployments.
  • Style

    • Progress bars now render above other content, improving visibility during uploads.

@appwrite
Copy link

appwrite bot commented Oct 28, 2025

Console

Project ID: 688b7bf400350cbd60e9

Sites (1)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Ready Ready View Logs Preview URL QR Code

Tip

Cursor pagination performs better than offset pagination when loading further pages.

@railway-app
Copy link

railway-app bot commented Oct 28, 2025

🚅 Deployed to the console-pr-2523 environment in Imagine

Service Status Web Updated (UTC)
console-container 😴 Sleeping (View Logs) Web Oct 28, 2025 at 7:45 pm

@railway-app railway-app bot temporarily deployed to console-container (Imagine / console-pr-2523) October 28, 2025 16:38 Destroyed
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

The pull request centralizes deployment handling by enhancing the uploader store and routing site/function deployments through it instead of calling the SDK directly. uploader.ts gains an admin-mode Functions client helper, an expanded uploadSiteDeployment signature (now accepts buildCommand, installCommand, outputDirectory) and a new uploadFunctionDeployment method. Both deployment paths forward options and use onProgress callbacks to update deployment progress/status in the uploader queue. Several UI pages were updated to call the uploader, defer navigation until upload completion, and perform cleanup on errors. A CSS z-index for layout-level progress bars was increased and analytics were added to deployment modals.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Areas requiring extra attention:

  • src/lib/stores/uploader.ts — review temporaryFunctions helper, uploadFunctionDeployment, and onProgress handling that updates UploaderFile entries and persists status changes.
  • Call-site updates — confirm all callers use the new uploadSiteDeployment signature and correctly await/check uploader queue status (sites/create-site/manual, createManualDeploymentModal.svelte, function creation pages).
  • Control flow and navigation — verify navigation now occurs after upload completion and handles success/failure paths consistently.
  • Error cleanup — ensure failed uploads are removed from the uploader queue and UI state is reset where appropriate.
  • Analytics and CSS change — validate added tracking calls and the z-index increase do not introduce regressions.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Improve UX for manual sites & functions deployment" directly aligns with the core changes in the changeset. The modifications refactor deployment flows across multiple components to use an uploader service abstraction, introduce real-time progress tracking via onProgress handlers, add user notifications when deployments start, defer navigation until uploads complete, and include error cleanup logic. These changes collectively work toward the stated goal of improving user experience for manual deployment workflows. The title is concise, specific about the scope (manual sites & functions deployment), and clearly communicates the primary improvement focus.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ser-455

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@railway-app railway-app bot temporarily deployed to console-container (Imagine / console-pr-2523) October 28, 2025 16:41 Destroyed
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte (1)

24-27: Fix file size limit units and error handling

  • maxSize: plan path converts MB→bytes, fallback path does not. Normalize to bytes for both.
  • Show the dynamic limit in the error instead of a hardcoded “10MB”.
  • Handle unknown in catch.
 $: maxSize =
-        isCloud && $currentPlan
-            ? $currentPlan.deploymentSize * 1000000
-            : $consoleVariables._APP_COMPUTE_SIZE_LIMIT; // already in MB
+        isCloud && $currentPlan
+            ? $currentPlan.deploymentSize * 1_000_000
+            : ($consoleVariables._APP_COMPUTE_SIZE_LIMIT ?? 0) * 1_000_000; // MB → bytes
-        } catch (e) {
-            error = e.message;
-            trackError(e, Submit.DeploymentCreate);
+        } catch (e) {
+            const msg = e instanceof Error ? e.message : String(e);
+            error = msg;
+            trackError(e, Submit.DeploymentCreate);
         }
-        } else if (reason === InvalidFileType.SIZE) {
-            error = 'File size exceeds 10MB';
+        } else if (reason === InvalidFileType.SIZE) {
+            error = `File size exceeds ${readableMaxSize.value}${readableMaxSize.unit}`;

Also applies to: 55-56, 45-47

src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte (1)

23-27: Normalize file size units, dynamic message, and error typing

Same MB→bytes mismatch and static “10MB” string; also guard unknown in catch.

 $: maxSize =
-        isCloud && $currentPlan?.deploymentSize
-            ? $currentPlan.deploymentSize * 1000000
-            : $consoleVariables._APP_COMPUTE_SIZE_LIMIT; // already in MB
+        isCloud && $currentPlan?.deploymentSize
+            ? $currentPlan.deploymentSize * 1_000_000
+            : ($consoleVariables._APP_COMPUTE_SIZE_LIMIT ?? 0) * 1_000_000; // MB → bytes
-        } else if (reason === InvalidFileType.SIZE) {
-            error = 'File size exceeds 10MB';
+        } else if (reason === InvalidFileType.SIZE) {
+            error = `File size exceeds ${readableMaxSize.value}${readableMaxSize.unit}`;
-        } catch (e) {
-            error = e.message;
+        } catch (e) {
+            error = e instanceof Error ? e.message : String(e);
             trackError(e, Submit.DeploymentCreate);
         }

Also applies to: 55-56, 45-47

src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte (1)

46-50: Normalize file size limit units (MB→bytes)

Ensure fallback path is bytes like the plan path.

 $: maxSize =
         isCloud && $currentPlan
-            ? $currentPlan.deploymentSize * 1000000
-            : $regionalConsoleVariables._APP_COMPUTE_SIZE_LIMIT; // already in MB
+            ? $currentPlan.deploymentSize * 1_000_000
+            : ($regionalConsoleVariables._APP_COMPUTE_SIZE_LIMIT ?? 0) * 1_000_000; // MB → bytes
src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (2)

169-173: Normalize file size limit units and message

Same MB→bytes normalization and dynamic error text.

 $: maxSize =
-        isCloud && $currentPlan
-            ? $currentPlan.deploymentSize * 1000000
-            : $regionalConsoleVariables._APP_COMPUTE_SIZE_LIMIT; // already in MB
+        isCloud && $currentPlan
+            ? $currentPlan.deploymentSize * 1_000_000
+            : ($regionalConsoleVariables._APP_COMPUTE_SIZE_LIMIT ?? 0) * 1_000_000; // MB → bytes
-        } else if (reason === InvalidFileType.SIZE) {
-            addNotification({
-                type: 'error',
-                message: 'File size exceeds 10MB'
-            });
+        } else if (reason === InvalidFileType.SIZE) {
+            addNotification({
+                type: 'error',
+                message: `File size exceeds ${readableMaxSize.value}${readableMaxSize.unit}`
+            });

Also applies to: 145-148


1-1: Verify and fix repo-wide inconsistencies in file size limit logic

The verification confirms multiple issues that need addressing:

  1. Unit mismatch in maxSize computation: Files calculate maxSize = $currentPlan.deploymentSize * 1000000 (bytes) but fall back to $regionalConsoleVariables._APP_COMPUTE_SIZE_LIMIT (already in MB). This creates inconsistent units depending on the code path.

  2. Hardcoded error messages misaligned with dynamic limits: Multiple files hardcode "File size exceeds 10MB" or "File size exceeds 5MB" in error messages, but maxSize is dynamically computed from plan data. Users see static messages that don't reflect the actual limit.

  3. Inconsistent error strings across codebase: Error messages vary ("exceeds 10MB", "exceeds 5MB", "exceeds the limit") across different pages, preventing centralized updates.

Affected files:

  • src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (line 144, 166-169)
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte (line 23-26, 55)
  • src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte (line 46-49, 145)
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte (line 24-27, 55)
  • src/routes/(console)/organization-[organization]/domains/domain-[domain]/importRecordModal.svelte (line 47)

Consolidate size limit constants, ensure byte conversion is applied uniformly, and use dynamic values in user-facing error messages.

src/lib/stores/uploader.ts (2)

120-168: Add error handling to update deployment status on failure.

The method lacks error handling. If createDeployment throws an error, the deployment's status will remain 'pending' in the uploader UI instead of being marked as 'failed'. This leaves users without visual feedback about the failure.

Wrap the deployment call in a try-catch block:

         update((n) => {
             n.isOpen = true;
             n.isCollapsed = false;
             n.files.unshift(newDeployment);
             return n;
         });
-        const uploadedFile = await temporarySites(
-            page.params.region,
-            page.params.project
-        ).createDeployment({
-            siteId,
-            code,
-            activate: true,
-            buildCommand,
-            installCommand,
-            outputDirectory,
-            onProgress: (progress) => {
-                newDeployment.$id = progress.$id;
-                newDeployment.progress = progress.progress;
-                newDeployment.status = progress.progress === 100 ? 'success' : 'pending';
-                updateFile(progress.$id, newDeployment);
-            }
-        });
-        newDeployment.$id = uploadedFile.$id;
-        newDeployment.progress = 100;
-        newDeployment.status = 'success';
-        updateFile(newDeployment.$id, newDeployment);
+        try {
+            const uploadedFile = await temporarySites(
+                page.params.region,
+                page.params.project
+            ).createDeployment({
+                siteId,
+                code,
+                activate: true,
+                buildCommand,
+                installCommand,
+                outputDirectory,
+                onProgress: (progress) => {
+                    newDeployment.$id = progress.$id;
+                    newDeployment.progress = progress.progress;
+                    newDeployment.status = progress.progress === 100 ? 'success' : 'pending';
+                    updateFile(progress.$id, newDeployment);
+                }
+            });
+            newDeployment.$id = uploadedFile.$id;
+            newDeployment.progress = 100;
+            newDeployment.status = 'success';
+            updateFile(newDeployment.$id, newDeployment);
+        } catch (error) {
+            newDeployment.status = 'failed';
+            newDeployment.error = error.message || 'Deployment failed';
+            updateFile(newDeployment.$id, newDeployment);
+            throw error;
+        }
     },

169-208: Add error handling to update deployment status on failure.

Similar to uploadSiteDeployment, this method lacks error handling. If createDeployment throws, the deployment status remains 'pending' instead of showing 'failed', leaving users without proper failure feedback.

Wrap the deployment call in a try-catch block:

         update((n) => {
             n.isOpen = true;
             n.isCollapsed = false;
             n.files.unshift(newDeployment);
             return n;
         });
-        const uploadedFile = await temporaryFunctions(
-            page.params.region,
-            page.params.project
-        ).createDeployment({
-            functionId,
-            code,
-            activate: true,
-            onProgress: (progress) => {
-                newDeployment.$id = progress.$id;
-                newDeployment.progress = progress.progress;
-                newDeployment.status = progress.progress === 100 ? 'success' : 'pending';
-                updateFile(progress.$id, newDeployment);
-            }
-        });
-        newDeployment.$id = uploadedFile.$id;
-        newDeployment.progress = 100;
-        newDeployment.status = 'success';
-        updateFile(newDeployment.$id, newDeployment);
+        try {
+            const uploadedFile = await temporaryFunctions(
+                page.params.region,
+                page.params.project
+            ).createDeployment({
+                functionId,
+                code,
+                activate: true,
+                onProgress: (progress) => {
+                    newDeployment.$id = progress.$id;
+                    newDeployment.progress = progress.progress;
+                    newDeployment.status = progress.progress === 100 ? 'success' : 'pending';
+                    updateFile(progress.$id, newDeployment);
+                }
+            });
+            newDeployment.$id = uploadedFile.$id;
+            newDeployment.progress = 100;
+            newDeployment.status = 'success';
+            updateFile(newDeployment.$id, newDeployment);
+        } catch (error) {
+            newDeployment.status = 'failed';
+            newDeployment.error = error.message || 'Deployment failed';
+            updateFile(newDeployment.$id, newDeployment);
+            throw error;
+        }
     },
🧹 Nitpick comments (5)
src/routes/(console)/project-[region]-[project]/+layout.svelte (1)

126-133: Z-index bump: verify stacking order vs modals/side‑sheets

Raising to 100 may overlay unintended UI. Please confirm it:

  • stays above content but below modal/backdrop layers,
  • and doesn’t conflict with side‑sheet visibility rules.

If the design system exposes z‑index tokens, prefer a tokenized value (e.g., var(--z-overlay-progress)). Otherwise, add a brief comment documenting the chosen layer.

src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte (1)

33-37: Non-blocking upload: guard against unhandled rejections and make intent explicit

try/catch won’t catch async failures because the upload isn’t awaited. Keep it non-blocking but handle rejection and avoid awaiting UI:

-            uploader.uploadSiteDeployment({
-                siteId: site.$id,
-                code: files[0],
-            });
-            show = false;
-            invalidate(Dependencies.DEPLOYMENTS);
-            trackEvent(Submit.DeploymentCreate);
+            void uploader
+                .uploadSiteDeployment({
+                    siteId: site.$id,
+                    code: files[0],
+                })
+                .catch((e) => trackError(e, Submit.DeploymentCreate));
+            show = false;
+            void invalidate(Dependencies.DEPLOYMENTS);
+            trackEvent(Submit.DeploymentCreate);

Also, the pipeline flagged Prettier issues; please run Prettier to align formatting.

Also applies to: 39-40, 46-46

src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte (1)

96-103: Uploader flow looks good; consider avoiding store scan for result

Nice switch to uploader.uploadSiteDeployment(...) and success navigation. To reduce races when multiple uploads target the same resource, prefer using a direct return from the uploader (e.g., resolved payload including deploymentId) instead of scanning $uploader.files by resourceId.

If the API can return { $id, resourceId, status }, you can:

-            const promise = uploader.uploadSiteDeployment({
+            const promise = uploader.uploadSiteDeployment({
                 siteId: site.$id,
                 code: files[0],
                 buildCommand: buildCommand || undefined,
                 installCommand: installCommand || undefined,
                 outputDirectory: outputDirectory || undefined
             });
@@
-            await promise;
-            const upload = $uploader.files.find((f) => f.resourceId === site.$id);
+            const upload = await promise; // use resolved return if available

Also applies to: 113-121

src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (1)

98-101: Uploader usage OK; prefer direct result over store scan

As with sites, avoid scanning $uploader.files by resourceId if the uploader promise can resolve to the upload entry (less race‑prone when multiple uploads exist).

-            const promise = uploader.uploadFunctionDeployment({
+            const promise = uploader.uploadFunctionDeployment({
                 functionId: func.$id,
                 code: files[0],
             });
@@
-            await promise;
-            const upload = $uploader.files.find((f) => f.resourceId === func.$id);
+            const upload = await promise; // if API returns the upload entry

Also applies to: 113-121

src/lib/stores/uploader.ts (1)

120-208: Consider extracting common deployment logic to reduce duplication.

Both uploadSiteDeployment and uploadFunctionDeployment share substantial logic (~80% overlap) for creating uploader entries, tracking progress, and updating status. Extracting this into a common helper would improve maintainability and ensure consistent behavior.

Consider creating a generic helper:

const uploadDeployment = async <T>(
    deploymentFn: () => Promise<T>,
    resourceId: string,
    fileName: string,
    fileSize: number
) => {
    const newDeployment: UploaderFile = {
        $id: '',
        resourceId,
        name: fileName,
        size: fileSize,
        progress: 0,
        status: 'pending'
    };
    update((n) => {
        n.isOpen = true;
        n.isCollapsed = false;
        n.files.unshift(newDeployment);
        return n;
    });
    try {
        const result = await deploymentFn();
        newDeployment.progress = 100;
        newDeployment.status = 'success';
        updateFile(newDeployment.$id, newDeployment);
        return result;
    } catch (error) {
        newDeployment.status = 'failed';
        newDeployment.error = error.message || 'Deployment failed';
        updateFile(newDeployment.$id, newDeployment);
        throw error;
    }
};

Then simplify both methods to use this helper with their specific deployment logic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b9feaf and fe39d26.

📒 Files selected for processing (6)
  • src/lib/stores/uploader.ts (4 hunks)
  • src/routes/(console)/project-[region]-[project]/+layout.svelte (1 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (3 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte (2 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte (4 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/stores/uploader.ts (1)
src/lib/stores/sdk.ts (1)
  • getApiEndpoint (47-59)
🪛 GitHub Actions: Tests
src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte

[warning] 1-1: Code style issues found by Prettier in this file. Run 'prettier --write' to fix.

src/lib/stores/uploader.ts

[warning] 1-1: Code style issues found by Prettier in this file. Run 'prettier --write' to fix.

src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte

[warning] 1-1: Code style issues found by Prettier in this file. Run 'prettier --write' to fix.

src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte

[warning] 1-1: Code style issues found by Prettier in this file. Run 'prettier --write' to fix.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e
🔇 Additional comments (6)
src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte (2)

16-16: Analytics import looks good

Importing and using Submit/trackEvent/trackError is consistent with the new flow.


1-1: Prettier formatting applied and verified

The file has been successfully formatted with Prettier and now passes CI formatting validation. No remaining issues.

src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte (1)

123-127: Great: queue cleanup on failure

Removing the in‑flight upload from the queue prevents stale UI.

src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte (1)

1-1: Prettier: fix style issues flagged by CI

Prettier has been run on the file. Use the correct path with literal directory names:

npx prettier --write "src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte"
src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (1)

1-1: Prettier formatting changes confirmed and applied

The Prettier execution successfully fixed style issues in the file. Changes include reformatting a chained method call from multi-line to single-line format, removing trailing commas, and cleaning up extra whitespace. The file is now compliant with Prettier standards and the CI should pass.

Commit these formatting changes to satisfy CI requirements.

src/lib/stores/uploader.ts (1)

35-40: LGTM!

The temporaryFunctions helper follows the same pattern as temporaryStorage and temporarySites, maintaining consistency across the codebase.

Comment on lines 108 to 111
trackEvent(Submit.FunctionCreate, {
source: 'repository',
runtime: runtime
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Analytics source seems wrong for manual upload

This page is “manual” but tracks { source: 'repository' }. Suggest ‘manual’ for consistency.

-            trackEvent(Submit.FunctionCreate, {
-                source: 'repository',
-                runtime: runtime
-            });
+            trackEvent(Submit.FunctionCreate, {
+                source: 'manual',
+                runtime
+            });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
trackEvent(Submit.FunctionCreate, {
source: 'repository',
runtime: runtime
});
trackEvent(Submit.FunctionCreate, {
source: 'manual',
runtime
});
🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte
around lines 108 to 111, the analytics call uses source: 'repository' but this
is the manual upload page; change the tracked source value to 'manual' so the
trackEvent call reads source: 'manual' (preserve the rest of the payload and
surrounding code).

Comment on lines 32 to 39
uploader.uploadFunctionDeployment({
functionId: $func.$id,
code: files[0],
})
show = false;
files = undefined;
await invalidate(Dependencies.DEPLOYMENTS);
trackEvent(Submit.DeploymentCreate);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make upload non‑blocking but safe; fix success message; add semicolon

  • Without await, use .catch to capture failures for analytics.
  • Add missing semicolon to satisfy Prettier/ASI.
  • The success toast says “Deployment created successfully” before the upload completes; change to “Deployment upload started” or await completion.
-            uploader.uploadFunctionDeployment({
-                functionId: $func.$id,
-                code: files[0],
-            })
+            void uploader
+                .uploadFunctionDeployment({
+                    functionId: $func.$id,
+                    code: files[0],
+                })
+                .catch((e) => trackError(e, Submit.DeploymentCreate));
+            ;
             show = false;
             files = undefined;
             await invalidate(Dependencies.DEPLOYMENTS);
-            trackEvent(Submit.DeploymentCreate);
+            trackEvent(Submit.DeploymentCreate);
             addNotification({
                 type: 'success',
-                message: 'Deployment created successfully'
+                message: 'Deployment upload started'
             });

Also applies to: 41-43

🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte
around lines 32-39 and 41-43, make the upload non-blocking but safe: call
uploader.uploadFunctionDeployment(...) without await, attach a .catch(...) to
capture/log/track failures for analytics, and avoid showing a success toast
before completion by changing the message to "Deployment upload started"; also
add the missing semicolons to satisfy Prettier/ASI. Ensure you still clear
show/files and await invalidate(Dependencies.DEPLOYMENTS) (or otherwise refresh
UI), and duplicate the same non-blocking + .catch + toast + semicolon changes
for the other occurrence on lines 41-43.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte (1)

30-48: Critical: Upload is non-blocking but invalidation/notification happen prematurely.

The upload call at line 32 is not awaited, so:

  • Line 38 invalidates deployments before the upload completes
  • Lines 40-43 show a success notification before the upload finishes
  • The try-catch won't catch errors from the upload promise

This creates a race condition and misleads users about deployment status.

Apply this diff to fix the async flow:

 async function create() {
     try {
-        uploader.uploadFunctionDeployment({
+        await uploader.uploadFunctionDeployment({
             functionId: $func.$id,
             code: files[0]
         });
         show = false;
         files = undefined;
         await invalidate(Dependencies.DEPLOYMENTS);
         trackEvent(Submit.DeploymentCreate);
         addNotification({
             type: 'success',
             message: 'Deployment created successfully'
         });
     } catch (e) {
         error = e.message;
         trackError(e, Submit.DeploymentCreate);
     }
 }
src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (1)

105-108: Analytics source is incorrect for manual upload page.

The analytics event tracks source: 'repository' but this page handles manual file uploads, not repository-based deployments.

Apply this diff:

 trackEvent(Submit.FunctionCreate, {
-    source: 'repository',
+    source: 'manual',
     runtime: runtime
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe39d26 and 5540f3d.

📒 Files selected for processing (4)
  • src/lib/stores/uploader.ts (4 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (3 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/(modals)/createManual.svelte (2 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createManualDeploymentModal.svelte
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/stores/uploader.ts (1)
src/lib/stores/sdk.ts (1)
  • getApiEndpoint (47-59)
🔇 Additional comments (4)
src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte (1)

96-118: Good async flow with proper await before navigation.

The implementation correctly:

  • Initiates the upload asynchronously (line 96)
  • Shows an "upload started" notification immediately (lines 101-104)
  • Awaits completion before checking status (line 110)
  • Conditionally navigates based on success status (lines 113-118)

This provides good UX with immediate feedback while ensuring navigation only happens after successful upload.

src/lib/stores/uploader.ts (3)

35-40: LGTM: Consistent admin client helper.

The temporaryFunctions helper follows the same pattern as temporaryStorage and temporarySites, maintaining consistency across the codebase.


169-208: LGTM: Function deployment follows site deployment pattern.

The implementation correctly mirrors the site deployment flow with proper progress tracking and status updates.


120-167: Verification complete: all call sites properly updated to use new signature.

Found 2 call sites using uploadSiteDeployment, both correctly updated:

  • createManualDeploymentModal.svelte:33 passes { siteId, code }
  • create-site/manual/+page.svelte:96-102 passes all parameters including optional buildCommand, installCommand, and outputDirectory with proper || undefined guards

The API change is fully integrated across the codebase.

Comment on lines +157 to +167
onProgress: (progress) => {
newDeployment.$id = progress.$id;
newDeployment.progress = progress.progress;
newDeployment.status = progress.progress === 100 ? 'success' : 'pending';
updateFile(progress.$id, newDeployment);
}
});
newDeployment.$id = uploadedFile.$id;
newDeployment.progress = 100;
newDeployment.status = 'success';
updateFile(newDeployment.$id, newDeployment);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Consider: Potential race between onProgress and final status update.

Lines 158-161 set status: 'success' when progress === 100, then lines 164-167 set it again after the promise resolves. If an error occurs between progress=100 and promise resolution, the status may incorrectly show 'success'.

However, if createDeployment throws on failure, the try-catch in the caller will handle it (though there's no try-catch here). Consider whether progress=100 truly indicates success or just upload completion.

Check if the SDK's createDeployment can fail after progress === 100:


🌐 Web query:

Does Appwrite's Sites.createDeployment or Functions.createDeployment throw errors after the upload progress reaches 100%, or does progress=100 guarantee success?

💡 Result:

Short answer: No — progress = 100 only means the upload finished from the client side. The deployment/build stage runs afterwards and can still fail. Appwrite exposes separate deployment/build status (ready/failed/in‑progress) and error codes for build failures, and you must check the deployment status (or API response) after upload to confirm success. [1][2][3]

Sources:

  • Functions createDeployment (shows upload/deployment flow and build lifecycle). [1]
  • Sites createVCS/createDeployment docs (deployment build can be activated/cancelled and has its own status). [2]
  • Appwrite response/error codes (functions/build-related error codes such as build_in_progress / build_not_ready / failures). [3]

Fix premature success status update when progress reaches 100%.

The concern is valid: setting status: 'success' at line 160 when progress === 100 is incorrect. Appwrite's upload progress only indicates client-side upload completion; the deployment/build runs separately and can still fail afterwards. Remove the premature status: 'success' assignment from the onProgress handler (line 160). Only set status: 'success' after confirming the deployment actually succeeded—either by checking the returned deployment object's status field or by verifying the API response indicates a ready/successful state.

🤖 Prompt for AI Agents
In src/lib/stores/uploader.ts around lines 157 to 167, remove the premature
assignment that sets newDeployment.status = 'success' inside the onProgress
handler; keep updating newDeployment.$id and newDeployment.progress there and
call updateFile(progress.$id, newDeployment) but do not mark success based on
progress === 100. Instead, determine success only after the upload/deploy
response returns a confirmed success state (e.g., inspect the returned
uploadedFile or deployment object status/ready flag) and set
newDeployment.status = 'success' and call updateFile(...) at that point; if the
deployment fails, set an appropriate error/failed status based on the API
response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants