Skip to content

Conversation

@atharvadeosthale
Copy link
Member

@atharvadeosthale atharvadeosthale commented Nov 26, 2025

Ditches the release tag based deployments on one-click deployments, and rather uses branches instead. This means target repository doesn't need to have releases anymore.

Summary by CodeRabbit

  • New Features

    • Git branch selection for function and site deployments with default-branch preselection, loading states, validation, and UI for choosing branches.
    • Quick deploy via URL parameters (quick and branch) to create sites/functions/deployments directly.
    • Environment variable parsing now supports KEY and KEY=VALUE formats, exposing key/value pairs for prefilled values.
  • Bug Fixes / Improvements

    • Deployment flows and URLs respect project region for cloud deployments and include richer error/notification handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@appwrite
Copy link

appwrite bot commented Nov 26, 2025

Console (appwrite/console)

Project ID: 688b7bf400350cbd60e9

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

Tip

Teams feature lets you group users with membership management and role permissions

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

Walkthrough

Added three Git helpers (getBranchFromUrl, getBranches, validateBranch) and replaced tag-based deployment flows with branch-based flows across function and site create pages in the console. Create pages now load branches and default branch, manage selectedBranch state, validate branch selection, and create deployments using Type.Branch with reference: selectedBranch. Multiple public deploy pages and URL builders now extract branch from query or repository URL, forward a quick flag, and format environment variables as KEY or KEY=VALUE pairs (envVars introduced while envKeys is preserved). Several load signatures, return payloads, and quick-deploy automation paths were added or adjusted; project.region is used for cloud region resolution.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • New helpers in src/lib/helpers/github.ts: correctness of URL parsing/decoding, HTTP error handling, and null-return semantics.
  • Branch-loading, default-branch resolution, and validation logic in console create pages (functions and sites), including parallel async flows, onMount changes, and UI state handling.
  • Quick-deploy flows added to several load handlers: branch selection/validation, env parsing (envVars vs envKeys), runtime/spec selection, resource creation sequences, redirects, and error vs redirect handling.
  • Changes to load signatures and returned payloads (envKeys → envVars) across multiple routes and their consumers; ensure callers remain compatible.
  • UI wiring: select inputs, loading states, and form-disable logic related to branch loading and billing/project limits.
  • URL construction and propagation: project.region fallback behavior, branch and quick query params, and env serialization format.

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 title clearly and concisely summarizes the main change: switching from tag-based to branch-based deployments in one-click deployments, which is the core objective of this PR.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch one-click-updates

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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.

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: 0

Caution

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

⚠️ Outside diff range comments (4)
src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte (2)

379-387: Deploy button should be disabled when branches aren't ready.

The button's disabled condition checks domainIsValid and domain, but does not account for branch loading state or selection. This allows users to click Deploy before branches are loaded.

Apply this diff to add branch validation to the disabled condition:

         <Button
             size="s"
             fullWidthMobile
             submissionLoader
             forceShowLoader={$isSubmitting}
             on:click={() => formComponent.triggerSubmit()}
-            disabled={$isSubmitting || !domainIsValid || !domain}>
+            disabled={$isSubmitting || !domainIsValid || !domain || !selectedBranch || loadingBranches}>
             Deploy
         </Button>

251-253: Potential null access in page title.

data.repository.name is accessed without optional chaining. If repository information is missing (as handled in onMount lines 158-164), this could cause a runtime error.

 <svelte:head>
-    <title>Deploy {data.repository.name} - Appwrite</title>
+    <title>Deploy {data.repository?.name ?? 'Site'} - Appwrite</title>
 </svelte:head>
src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte (2)

35-35: Potential null access when initializing name.

data.repository.name is accessed directly without optional chaining. If data.repository is undefined, this will throw at component initialization.

-    let name = $state(data.repository.name);
+    let name = $state(data.repository?.name ?? '');

206-208: Potential null access in page title.

Same issue as the sites page - data.repository.name accessed without optional chaining.

 <svelte:head>
-    <title>Deploy {data.repository.name} - Appwrite</title>
+    <title>Deploy {data.repository?.name ?? 'Function'} - Appwrite</title>
 </svelte:head>
🧹 Nitpick comments (1)
src/lib/helpers/github.ts (1)

58-72: Consider pagination for repositories with many branches.

The GitHub /repos/{owner}/{name}/branches endpoint returns a maximum of 30 branches per page by default (100 max with per_page parameter). Repositories with more branches will have their list truncated, potentially hiding important branches from the user.

For one-click deployments this is likely acceptable since most template repositories have few branches, but worth noting.

If needed, you could add pagination support or increase per_page:

     const branchesResponse = await fetch(
-        `https://api.github.com/repos/${owner}/${name}/branches`
+        `https://api.github.com/repos/${owner}/${name}/branches?per_page=100`
     );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f76fbc and 0f3aaba.

📒 Files selected for processing (3)
  • src/lib/helpers/github.ts (1 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte (7 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte (7 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/lib/helpers/github.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
src/lib/helpers/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Implement pure functions in src/lib/helpers/ directory and use camelCase for helper function names

Files:

  • src/lib/helpers/github.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.ts: Define types inline or in .d.ts files, avoid creating separate .types.ts files
Use TypeScript in non-strict mode; any type is tolerated in this project

Files:

  • src/lib/helpers/github.ts
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/lib/helpers/github.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
src/routes/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Files:

  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Configure dynamic routes using SvelteKit convention with [param] syntax in route directory names

Files:

  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
🧠 Learnings (4)
📓 Common learnings
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2316
File: src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte:95-103
Timestamp: 2025-09-08T13:21:53.793Z
Learning: In Appwrite's one-click deployment flows, Git tag validation should be lenient with fallbacks (like '1.0.0') rather than strict validation that blocks deployments, since the source code retrieval from the repository is more important than having proper version tags for the deployment to succeed.
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
📚 Learning: 2025-10-05T09:41:40.439Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2398
File: src/routes/(console)/verify-email/+page.svelte:48-51
Timestamp: 2025-10-05T09:41:40.439Z
Learning: In SvelteKit 5, `page` imported from `$app/state` is a reactive state object (using runes), not a store. It should be accessed as `page.data` without the `$` prefix, unlike the store-based `$page` from `$app/stores` in earlier versions.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
📚 Learning: 2025-10-13T05:16:07.656Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/header.svelte:54-58
Timestamp: 2025-10-13T05:16:07.656Z
Learning: In SvelteKit apps, shared layout components (like headers) that use `$derived(page.data.*)` should use optional chaining when accessing properties that may not be present on all routes. During page transitions, reactive statements can briefly evaluate with different page.data structures, so optional chaining prevents runtime errors when navigating between routes with different data shapes (e.g., between `/databases` and `/databases/database-[database]`).

Applied to files:

  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
⏰ 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). (2)
  • GitHub Check: e2e
  • GitHub Check: build
🔇 Additional comments (6)
src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte (3)

124-164: LGTM on branch loading logic.

Good use of Promise.all for parallel fetching, proper error handling with user notifications, and sensible default branch pre-selection fallback.


217-228: Verify data.repository is defined before accessing properties.

While selectedBranch is validated at line 176, data.repository.name and data.repository.owner on lines 222-223 are accessed without null checks. If repository info is missing, this will throw.

Consider adding a guard or using optional chaining with a fallback:

     // Create deployment from GitHub repository using the selected branch
+    if (!data.repository?.owner || !data.repository?.name) {
+        addNotification({
+            type: 'error',
+            message: 'Repository information is missing.'
+        });
+        return;
+    }
+
     const deployment = await sdk

305-315: LGTM on branch selection UI.

The select input properly shows loading state, disables during fetch, and uses a clear placeholder. Good UX.

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

172-183: Add guard for repository info before deployment.

Similar to the sites page, data.repository.name and data.repository.owner are accessed without null checks. The guard at line 128 only validates branch selection.

Consider adding validation:

     // Create deployment from GitHub repository using the selected branch
+    if (!data.repository?.owner || !data.repository?.name) {
+        addNotification({
+            type: 'error',
+            message: 'Repository information is missing.'
+        });
+        return;
+    }
+
     await sdk

333-345: LGTM on Deploy button validation.

The disabled condition properly includes branch readiness checks (!selectedBranch || branches.length === 0), preventing submission before branches are loaded.


266-280: LGTM on Git configuration section.

Clean implementation with proper loading state handling and disabled state during branch fetch.

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 (1)
src/routes/(public)/functions/deploy/+page.svelte (1)

125-129: rootDirectory fallback never used due to outer condition.

The fallback data.deploymentData.repository.rootDirectory on line 128 will never be used because the outer if (rootDir) condition only executes when rootDir from the URL is truthy. If rootDir is null/undefined, the entire block is skipped.

If the intent is to also forward the root directory extracted from the repository URL when no explicit rootDir param is provided, consider:

-        if (rootDir)
-            url.searchParams.set(
-                'rootDir',
-                rootDir || data.deploymentData.repository.rootDirectory
-            );
+        const effectiveRootDir = rootDir || data.deploymentData.repository.rootDirectory;
+        if (effectiveRootDir) url.searchParams.set('rootDir', effectiveRootDir);
♻️ Duplicate comments (1)
src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts (1)

26-40: Same duplicate envVars parsing as noted in functions loader.

This parsing logic is identical to the functions deploy loader. As mentioned previously, consider extracting to a shared utility.

🧹 Nitpick comments (4)
src/routes/(public)/functions/deploy/+page.ts (1)

64-70: Type mismatch: rootDirectory initialized as null but typed as string | undefined.

The type declaration on line 57 defines rootDirectory?: string, but the initialization assigns null. While this works in non-strict TypeScript, it's inconsistent.

     deploymentData.repository = {
         url: repository!,
-        rootDirectory: null
+        rootDirectory: undefined
     },
src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte (1)

395-403: Consider disabling the Deploy button while branches are loading.

The button is disabled when $isSubmitting || !domainIsValid || !domain, but not when loadingBranches is true. Users could click Deploy before branches finish loading, which would trigger the error notification at line 192-198.

             disabled={$isSubmitting || !domainIsValid || !domain}>
+            disabled={$isSubmitting || !domainIsValid || !domain || loadingBranches || !selectedBranch}>
src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts (1)

33-47: Environment variable parsing duplicated across loaders.

The same KEY or KEY=value parsing logic is repeated in both the functions and sites deploy loaders. Consider extracting this to a shared helper in $lib/helpers/github.ts or a new utility file.

// In $lib/helpers/env.ts or similar
export function parseEnvParams(envParam: string | null): Array<{ key: string; value: string }> {
    if (!envParam) return [];
    return envParam.split(',').map((entry) => {
        const trimmed = entry.trim();
        const eqIndex = trimmed.indexOf('=');
        if (eqIndex === -1) {
            return { key: trimmed, value: '' };
        }
        return {
            key: trimmed.substring(0, eqIndex),
            value: trimmed.substring(eqIndex + 1)
        };
    });
}
src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts (1)

55-58: Frameworks fetched twice if quick mode fails.

In quick mode, listFrameworks() is called at line 55-57. If quick mode fails (catch block at line 141-148), the code falls through to line 151-157 where listFrameworks() is called again. Consider caching the result or restructuring to avoid the duplicate API call.

+    let frameworks = null;
+
     if (quickMode) {
         try {
             const { regionalConsoleVariables } = await parent();

             const preset = url.searchParams.get('preset') || 'nextjs';
             const framework = (Object.values(Framework) as string[]).includes(preset.toLowerCase())
                 ? (preset.toLowerCase() as Framework)
                 : Framework.Nextjs;

             // Get framework defaults
-            const frameworks = await sdk
+            frameworks = await sdk
                 .forProject(params.region, params.project)
                 .sites.listFrameworks();

Then at line 151:

-    const [frameworks, installations] = await Promise.all([
-        sdk.forProject(params.region, params.project).sites.listFrameworks(),
+    const [fetchedFrameworks, installations] = await Promise.all([
+        frameworks ?? sdk.forProject(params.region, params.project).sites.listFrameworks(),
         sdk
             .forProject(params.region, params.project)
             .vcs.listInstallations()
             .catch(() => null)
     ]);
+    frameworks = fetchedFrameworks;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5436e1 and 20ca407.

📒 Files selected for processing (9)
  • src/lib/helpers/github.ts (2 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte (8 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts (3 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte (7 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts (3 hunks)
  • src/routes/(public)/functions/deploy/+page.svelte (3 hunks)
  • src/routes/(public)/functions/deploy/+page.ts (5 hunks)
  • src/routes/(public)/sites/deploy/+page.svelte (2 hunks)
  • src/routes/(public)/sites/deploy/+page.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/routes/(public)/sites/deploy/+page.svelte
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/routes/(public)/sites/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts
  • src/lib/helpers/github.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.ts: Define types inline or in .d.ts files, avoid creating separate .types.ts files
Use TypeScript in non-strict mode; any type is tolerated in this project

Files:

  • src/routes/(public)/sites/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts
  • src/lib/helpers/github.ts
  • src/routes/(public)/functions/deploy/+page.ts
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/routes/(public)/sites/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts
  • src/lib/helpers/github.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.ts
src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Configure dynamic routes using SvelteKit convention with [param] syntax in route directory names

Files:

  • src/routes/(public)/sites/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.ts
src/lib/helpers/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Implement pure functions in src/lib/helpers/ directory and use camelCase for helper function names

Files:

  • src/lib/helpers/github.ts
src/routes/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Files:

  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
🧠 Learnings (7)
📓 Common learnings
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2316
File: src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte:95-103
Timestamp: 2025-09-08T13:21:53.793Z
Learning: In Appwrite's one-click deployment flows, Git tag validation should be lenient with fallbacks (like '1.0.0') rather than strict validation that blocks deployments, since the source code retrieval from the repository is more important than having proper version tags for the deployment to succeed.
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to src/routes/**/*.svelte : Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Applied to files:

  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts
  • src/routes/(public)/functions/deploy/+page.svelte
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts
  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
📚 Learning: 2025-11-19T11:22:42.553Z
Learnt from: atharvadeosthale
Repo: appwrite/console PR: 2512
File: src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte:51-83
Timestamp: 2025-11-19T11:22:42.553Z
Learning: In src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte, the Lovable integration URL format `https://lovable.dev/` with `autosubmit` and `prompt` as query parameters (set via searchParams) is correct and functional.

Applied to files:

  • src/routes/(public)/functions/deploy/+page.svelte
📚 Learning: 2025-11-19T11:22:42.553Z
Learnt from: atharvadeosthale
Repo: appwrite/console PR: 2512
File: src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte:51-83
Timestamp: 2025-11-19T11:22:42.553Z
Learning: In src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte, the Cursor integration URL format `https://cursor.com/link/prompt` with the `text` query parameter is correct and functional.

Applied to files:

  • src/routes/(public)/functions/deploy/+page.svelte
📚 Learning: 2025-10-05T09:41:40.439Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2398
File: src/routes/(console)/verify-email/+page.svelte:48-51
Timestamp: 2025-10-05T09:41:40.439Z
Learning: In SvelteKit 5, `page` imported from `$app/state` is a reactive state object (using runes), not a store. It should be accessed as `page.data` without the `$` prefix, unlike the store-based `$page` from `$app/stores` in earlier versions.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
📚 Learning: 2025-10-13T05:16:07.656Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/header.svelte:54-58
Timestamp: 2025-10-13T05:16:07.656Z
Learning: In SvelteKit apps, shared layout components (like headers) that use `$derived(page.data.*)` should use optional chaining when accessing properties that may not be present on all routes. During page transitions, reactive statements can briefly evaluate with different page.data structures, so optional chaining prevents runtime errors when navigating between routes with different data shapes (e.g., between `/databases` and `/databases/database-[database]`).

Applied to files:

  • src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.svelte
🧬 Code graph analysis (4)
src/routes/(public)/sites/deploy/+page.ts (1)
src/lib/helpers/github.ts (1)
  • getBranchFromUrl (6-9)
src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts (2)
src/lib/helpers/github.ts (3)
  • validateBranch (80-94)
  • getDefaultBranch (47-61)
  • getNestedRootDirectory (1-4)
src/routes/(console)/project-[region]-[project]/store.ts (1)
  • regionalConsoleVariables (34-39)
src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts (3)
src/lib/helpers/github.ts (3)
  • getNestedRootDirectory (1-4)
  • validateBranch (80-94)
  • getDefaultBranch (47-61)
src/lib/stores/sdk.ts (1)
  • sdk (173-196)
src/routes/(console)/project-[region]-[project]/store.ts (1)
  • regionalConsoleVariables (34-39)
src/routes/(public)/functions/deploy/+page.ts (1)
src/lib/helpers/github.ts (1)
  • getBranchFromUrl (6-9)
⏰ 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). (2)
  • GitHub Check: e2e
  • GitHub Check: build
🔇 Additional comments (20)
src/lib/helpers/github.ts (2)

6-9: LGTM - Branch extraction helper is well-implemented.

The regex correctly captures the branch segment from GitHub tree URLs and properly decodes URI components to handle special characters in branch names.


80-94: LGTM - Branch validation is correctly implemented.

The function properly encodes the branch name in the URL path to handle special characters, and the error handling is consistent with other functions in this file.

src/routes/(public)/functions/deploy/+page.ts (2)

32-49: LGTM - Robust environment variable parsing.

The parsing logic correctly handles both KEY and KEY=value formats, and using indexOf('=') ensures values containing = characters are preserved correctly.


80-86: LGTM - Branch extraction integrated correctly.

Branch is properly extracted from the URL and assigned to the deployment data, enabling branch-based deployments.

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

67-142: Well-structured branch loading with appropriate fallbacks.

The implementation correctly:

  • Validates URL-provided branch before using it
  • Falls back to default branch, then first available branch
  • Shows appropriate error notifications on failures
  • Uses Promise.all for efficient parallel requests

This aligns with the project's philosophy of lenient validation with fallbacks rather than strict blocking. Based on learnings, this approach ensures deployments succeed even when branch information isn't ideal.


144-151: LGTM - Appropriate guard clause for branch validation.

The check ensures a branch is selected and branches are loaded before proceeding, with a clear user notification.


189-200: LGTM - Correct switch from tag-based to branch-based deployment.

The deployment template now correctly uses Type.Branch with reference: selectedBranch, completing the migration from tag-based deployments.


283-297: LGTM - Clean branch selection UI.

The fieldset properly handles loading state with placeholder text and disabled state, providing good UX during async branch loading.

src/routes/(public)/sites/deploy/+page.ts (2)

38-55: Consistent env parsing implementation.

The env parsing logic is identical to src/routes/(public)/functions/deploy/+page.ts. While this could be extracted to a shared helper, the duplication is acceptable given the limited scope.


87-101: LGTM - Branch extraction correctly integrated for repo-based deployments.

Branch is properly extracted and assigned only for repository-based deployments (not templates), which is the correct behavior.

src/routes/(public)/functions/deploy/+page.svelte (2)

116-131: LGTM - Branch and quick parameter forwarding is correctly implemented.

The branch priority (explicit URL param → extracted from repo URL) and conditional quick parameter forwarding are well-designed.


133-139: LGTM - Correct envVars serialization preserving values.

The serialization correctly formats entries as KEY=value when a value exists, or just KEY otherwise, maintaining compatibility with the parsing logic.

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

92-180: Branch loading and fallback logic looks good.

The implementation properly handles:

  • Parallel fetching of branch list, default branch, and validation
  • Fallback to default branch or first branch when URL param is invalid
  • User notifications on failure

Based on learnings, the lenient validation with fallbacks aligns with the preferred approach for one-click deployments.


241-242: Branch-based deployment correctly replaces tag-based approach.

The deployment now uses Type.Branch with reference: selectedBranch, which aligns with the PR objective to switch from release-tag–based to branch-based deployments.


119-126: Type alignment verified—no issues found.

The loader at +page.ts (line 28) explicitly types envVars as Array<{ key: string; value: string }> and returns it (line 160). The component correctly receives this shape and enriches it by adding the secret: false property during mapping. The type contract between loader and component is properly aligned.

src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts (3)

68-77: Branch resolution logic is correct with proper fallbacks.

The code validates the provided branch, falls back to the default branch, and ultimately to 'main' as a last resort. This aligns with the learnings indicating Git validation should be lenient with fallbacks.


96-99: Auto-generated domain uses ID.unique() for subdomain.

Using ID.unique() generates a random identifier for the domain prefix. This is consistent with the sites implementation and provides unique domains automatically.


129-136: Quick mode error handling correctly re-throws redirects.

Using isRedirect(e) to distinguish between SvelteKit redirects and actual errors prevents swallowing the redirect and correctly falls through to the wizard on real failures.

src/routes/(console)/project-[region]-[project]/sites/create-site/deploy/+page.ts (2)

76-85: Branch resolution consistent with functions loader.

The branch validation and fallback logic mirrors the functions loader implementation, ensuring consistent behavior across both deployment types.


141-148: Error handling preserves redirect behavior.

The isRedirect(e) check properly re-throws SvelteKit redirects while allowing actual errors to fall through to the wizard UI. The console.error provides visibility into quick deploy failures.

Comment on lines +63 to +78
export async function getBranches(owner: string, name: string): Promise<string[] | null> {
try {
const branchesResponse = await fetch(
`https://api.github.com/repos/${owner}/${name}/branches`
);
if (!branchesResponse.ok) {
return null;
}

const branches = await branchesResponse.json();
return branches.map((branch) => branch.name);
} catch (error) {
console.error('Failed to fetch branches from GitHub:', error);
return null;
}
}
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

🧩 Analysis chain

🌐 Web query:

GitHub API branches endpoint pagination default limit

💡 Result:

The List Branches REST endpoint returns 30 results per page by default (per_page default = 30, max = 100); page defaults to 1. [1][2]

Sources:
[1] GitHub REST API — Branches (List branches) documentation.
[2] GitHub Docs — REST API endpoints for branches.


🏁 Script executed:

#!/bin/bash
# First, let's examine the complete getBranches function and its usage context
cat -n src/lib/helpers/github.ts | head -100

Repository: appwrite/console

Length of output: 3534


🏁 Script executed:

#!/bin/bash
# Search for all usages of getBranches function to understand the context
rg -n "getBranches" --type ts --type tsx

Repository: appwrite/console

Length of output: 87


🏁 Script executed:

#!/bin/bash
# Search for usages of getBranches
rg -n "getBranches" --type ts --type js

Repository: appwrite/console

Length of output: 179


🏁 Script executed:

#!/bin/bash
# Search for pagination patterns in the codebase
rg -n "per_page|pagination|page=" --type ts --type js -C 2

Repository: appwrite/console

Length of output: 1074


🏁 Script executed:

#!/bin/bash
# Check if there are any Link headers or pagination handling elsewhere
rg -n "Link|headers" --type ts --type js src/lib/helpers/ -C 2

Repository: appwrite/console

Length of output: 42


Based on my verification, I can now provide the rewritten review comment.

Add GitHub API pagination to handle repositories with more than 30 branches.

The GitHub Branches API defaults to 30 results per page, so repositories with many branches will have incomplete results. The suggested fix of adding ?per_page=100 improves this (GitHub's max), but note that repositories with more than 100 branches would still require full pagination support via the Link header or looping through pages.

For most repositories, increasing to per_page=100 is sufficient:

 export async function getBranches(owner: string, name: string): Promise<string[] | null> {
     try {
         const branchesResponse = await fetch(
-            `https://api.github.com/repos/${owner}/${name}/branches`
+            `https://api.github.com/repos/${owner}/${name}/branches?per_page=100`
         );
         if (!branchesResponse.ok) {
             return null;
         }

         const branches = await branchesResponse.json();
         return branches.map((branch) => branch.name);
     } catch (error) {
         console.error('Failed to fetch branches from GitHub:', error);
         return null;
     }
 }
🤖 Prompt for AI Agents
In src/lib/helpers/github.ts around lines 63 to 78, the current function fetches
only the first page of branches (GitHub defaults to 30 per page) causing missing
branches for large repos; update the function to request up to per_page=100 and
implement full pagination by looping requests and following the Link response
header (or incrementing page until an empty array) to accumulate all branch
names, deduplicate if needed, and return the combined list; ensure error
handling remains and stop when there is no next page.

Comment on lines +79 to +81
// Get first available specification
const specification =
specificationsList?.specifications?.[0]?.slug || 's-0.5vcpu-512mb';
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 27, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if this specification slug is used elsewhere or defined as a constant
rg -n 's-0.5vcpu-512mb' --type ts

Repository: appwrite/console

Length of output: 236


🏁 Script executed:

#!/bin/bash
# Read the file to see the context around lines 79-81
cat -n src/routes/\(console\)/project-\[region\]-\[project\]/functions/create-function/deploy/+page.ts | sed -n '60,100p'

Repository: appwrite/console

Length of output: 2099


🏁 Script executed:

#!/bin/bash
# Read the top of the file to see imports and variable definitions
cat -n src/routes/\(console\)/project-\[region\]-\[project\]/functions/create-function/deploy/+page.ts | head -80

Repository: appwrite/console

Length of output: 3522


🏁 Script executed:

#!/bin/bash
# Find the parent layout file - should be in src/routes/(console)
find src/routes -name '+layout.ts' -o -name '+layout.svelte' | head -20

Repository: appwrite/console

Length of output: 1394


🏁 Script executed:

#!/bin/bash
# Read the project-level layout file
cat -n src/routes/\(console\)/project-\[region\]-\[project\]/+layout.ts | head -150

Repository: appwrite/console

Length of output: 3874


🏁 Script executed:

#!/bin/bash
# Read the console-level layout file and search for specificationsList
rg -n 'specificationsList' src/routes/\(console\)/+layout.ts -A 5 -B 5

Repository: appwrite/console

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for specificationsList definition across the codebase
rg -n 'specificationsList' --type ts -B 3 -A 3

Repository: appwrite/console

Length of output: 13283


🏁 Script executed:

#!/bin/bash
# Read the functions layout file to see the full context
cat -n src/routes/\(console\)/project-\[region\]-\[project\]/functions/+layout.ts | head -35

Repository: appwrite/console

Length of output: 1165


🏁 Script executed:

#!/bin/bash
# Search for specification constants or defaults
rg -n 'specification' --type ts -i | grep -i 'const\|default\|fallback' | head -20

Repository: appwrite/console

Length of output: 1481


🏁 Script executed:

#!/bin/bash
# Search for constants files and any specification-related constants
find src -name '*const*' -type f | head -20

Repository: appwrite/console

Length of output: 80


🏁 Script executed:

#!/bin/bash
# Read the constants file and search for specification-related content
rg -n 'specification' src/lib/constants.ts -i -A 2 -B 2

Repository: appwrite/console

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for other hardcoded values in the deploy page
rg -n 'node-18\|runtime\|slug' src/routes/\(console\)/project-\[region\]-\[project\]/functions/create-function/deploy/+page.ts -A 1 -B 1

Repository: appwrite/console

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look at the runtime fallback on line 56
sed -n '54,58p' src/routes/\(console\)/project-\[region\]-\[project\]/functions/create-function/deploy/+page.ts

Repository: appwrite/console

Length of output: 213


🏁 Script executed:

#!/bin/bash
# Search for types or documentation about specifications
rg -n 'SpecificationList|specification.*slug' --type ts -A 2 -B 2 | head -40

Repository: appwrite/console

Length of output: 1653


Based on my verification, I've found:

  1. The hardcoded slug is used only once in the codebase at line 81 of the target file
  2. It's a valid fallback pattern - the code tries to get the first available specification and falls back to 's-0.5vcpu-512mb' when none are available
  3. No constant defined - the slug is not extracted as a named constant anywhere in the codebase
  4. Consistent with the codebase pattern - similar fallbacks exist (e.g., 'node-18.0' for runtimes)
  5. The slug format appears valid - it follows Appwrite's specification naming convention and represents a 0.5 vCPU / 512MB specification

Minor Fixes Required

The hardcoded specification slug 's-0.5vcpu-512mb' should be extracted as a named constant for maintainability and easier future updates. While the slug appears valid and the fallback behavior is sound, this aligns with best practices for configuration values that might need centralized management.

🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.ts
around lines 79 to 81, the fallback spec slug 's-0.5vcpu-512mb' is hardcoded;
extract it to a named constant (e.g., DEFAULT_SPEC_SLUG) placed either at the
top of this file or in a shared constants module, export it if placed in a
shared module, then replace the inline string with that constant; ensure any
imports are updated and run linters to confirm no unused-vars or export issues.

Copy link
Member

Choose a reason for hiding this comment

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

@atharvadeosthale agreee, see if we have proper constant for this. I think there's a RuntimeSpecification type 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

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: 0

🧹 Nitpick comments (3)
src/routes/(public)/functions/deploy/+page.svelte (1)

66-74: Consider tracking billing fetch errors.

The silent error handling ensures the deployment flow isn't blocked by billing API issues. However, tracking these errors could help identify systematic problems.

Consider adding error tracking:

         } catch {
+            trackError(error, 'billing.getOrganizationPlan');
             currentPlan = null;
         }
src/routes/(public)/sites/deploy/+page.svelte (2)

49-63: Consider adding explanatory comments for billing logic.

The billing state logic is sound but complex. The conditions distinguish between addon territory (pricing info) and hard limits (block creation).

One edge case to verify: Line 54 checks currentPlan?.projects > 0, which means if projects is explicitly 0, the limit check is bypassed (unlimited projects). Confirm this behavior is intentional.

Consider adding inline comments explaining when each derived state applies:

 let currentPlan = $state<Plan>(null);

+// Block project creation when hard limit is reached
 let projectsLimited = $derived(
     isCloud &&
         currentPlan?.projects > 0 &&
         projects?.total !== undefined &&
         projects.total >= currentPlan.projects
 );
+// Show addon pricing when using slots beyond plan-included count
 let isAddonProject = $derived(
     isCloud &&
         currentPlan?.addons?.projects?.supported &&
         projects?.total !== undefined &&
         projects.total >= (currentPlan?.addons?.projects?.planIncluded ?? 0)
 );

429-431: LGTM! Billing-aware UI correctly gates project creation.

The disabled states and alerts work together to:

  1. Show addon pricing when using extra project slots (lines 464-473)
  2. Block creation when at the hard limit (lines 474-481)
  3. Disable inputs appropriately (lines 429, 454)

The mutually exclusive alert conditions are correct.

For improved clarity, consider adding a comment above the alerts explaining the three UI states (under limit, in addon range, at limit).

Also applies to: 454-454, 464-481

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20ca407 and 2d3699c.

📒 Files selected for processing (2)
  • src/routes/(public)/functions/deploy/+page.svelte (10 hunks)
  • src/routes/(public)/sites/deploy/+page.svelte (8 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/routes/(public)/sites/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
src/routes/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Files:

  • src/routes/(public)/sites/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/routes/(public)/sites/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/routes/(public)/sites/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Configure dynamic routes using SvelteKit convention with [param] syntax in route directory names

Files:

  • src/routes/(public)/sites/deploy/+page.svelte
  • src/routes/(public)/functions/deploy/+page.svelte
🧠 Learnings (2)
📓 Common learnings
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2316
File: src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte:95-103
Timestamp: 2025-09-08T13:21:53.793Z
Learning: In Appwrite's one-click deployment flows, Git tag validation should be lenient with fallbacks (like '1.0.0') rather than strict validation that blocks deployments, since the source code retrieval from the repository is more important than having proper version tags for the deployment to succeed.
📚 Learning: 2025-11-19T11:22:42.553Z
Learnt from: atharvadeosthale
Repo: appwrite/console PR: 2512
File: src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte:51-83
Timestamp: 2025-11-19T11:22:42.553Z
Learning: In src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte, the Lovable integration URL format `https://lovable.dev/` with `autosubmit` and `prompt` as query parameters (set via searchParams) is correct and functional.

Applied to files:

  • src/routes/(public)/functions/deploy/+page.svelte
⏰ 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). (2)
  • GitHub Check: build
  • GitHub Check: e2e
🔇 Additional comments (15)
src/routes/(public)/functions/deploy/+page.svelte (9)

8-8: LGTM! Billing imports added appropriately.

The new imports for Plan, Alert, and formatCurrency align with the billing-aware features introduced in this PR.

Also applies to: 15-15, 28-28


43-57: LGTM! Billing state logic is sound.

The derived values correctly implement project limit checks and addon eligibility. Optional chaining is appropriately used for nested addon properties.


122-122: LGTM! Region handling is correct.

The logic properly handles cloud vs non-cloud modes with an appropriate fallback to 'default'.


273-273: LGTM! Consistent UX for billing limits.

Disabling inputs when projectsLimited prevents users from completing forms they cannot submit, providing clear feedback aligned with the billing constraints.

Also applies to: 275-275, 298-298


308-326: LGTM! Clear billing communication to users.

The alerts provide appropriate feedback about addon costs and project limits. The mutually exclusive logic ensures users see the most relevant message.


332-336: LGTM! Button disable logic correctly handles billing limits.

The updated condition appropriately prevents submission when project creation is blocked by billing constraints, maintaining consistency with the disabled inputs and alert messages.


146-147: LGTM! Strict boolean flag handling.

The strict check quick === 'true' ensures only the explicit 'true' value is forwarded, which is appropriate for a boolean flag parameter.

Also applies to: 157-157


159-165: The dual-tracking is intentional and correct—envKeys is properly derived from envVars.

The data loader (line 49 of +page.ts) explicitly derives envKeys from envVars via .map((v) => v.key), with a comment stating "Keep envKeys for backward compatibility." Since envKeys is computed directly from envVars on every load, both arrays remain synchronized automatically. The code is working as designed.


143-145: Branch extraction logic is safe and properly handles null values.

The data loader populates repository.branch via getBranchFromUrl(), which returns string | null. When the repository URL doesn't contain a branch pattern (e.g., /tree/branch), it returns null. The fallback pattern in lines 143-145 is sound: it prefers the explicit URL parameter but safely falls back to the data field. Line 156 verifies the branch exists before setting it with the conditional check if (branch), which prevents null values from propagating. No issues identified.

src/routes/(public)/sites/deploy/+page.svelte (6)

9-9: LGTM! Imports support billing features.

The new imports for Plan, Alert, and formatCurrency are appropriate for the billing-aware UI enhancements.

Also applies to: 17-17, 32-32


71-78: LGTM! Plan fetching with graceful degradation.

The plan fetch is properly guarded for cloud deployments and includes error handling. Silent failure (setting null) is appropriate here since billing checks are enhancements that degrade gracefully if the plan info is unavailable.


126-127: LGTM! Proper region handling for cloud and self-hosted.

The region resolution correctly uses the project's actual region for cloud deployments and falls back to 'default' appropriately. The inline comment clearly explains the logic.


489-489: LGTM! Submit button correctly prevents creation when at project limit.

The additional condition properly disables the button when attempting to create a new project while at the organization's project limit, consistent with the disabled inputs and warning alert.


163-169: Environment variable format handling is correctly implemented and fully verified.

The page loader (+page.ts) at lines 40-52 properly parses the env search parameter and handles both KEY and KEY=value formats:

  • Splits entries by comma
  • Detects = delimiter to extract key and value
  • Returns {key, value: ''} for key-only entries
  • Returns {key, value} for key=value entries

The data.envVars is correctly populated at line 148 and passed to the component, supporting the pre-filling behavior shown in the snippet at lines 163-169 of +page.svelte.


149-160: Branch handling verified - implementation is correct.

The loader properly populates data.deploymentData.repository.branch using the getBranchFromUrl helper (line 88), extracting the branch from the repository URL structure and assigning it at line 96. The +page.svelte snippet correctly uses this value with URL parameter override, maintaining proper precedence.

}

export function getBranchFromUrl(repository: string): string | null {
const match = repository.match(/\/tree\/([^/]+)/);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const match = repository.match(/\/tree\/([^/]+)/);
const match = repository.match(/\/tree\/([^/?#]+)/);

});
}
} catch (error) {
console.error('Failed to load branches:', error);
Copy link
Member

Choose a reason for hiding this comment

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

leftover.


if (quickMode) {
try {
const runtimeParam = runtime || 'node-18.0';
Copy link
Member

Choose a reason for hiding this comment

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

mm, not sure about the default value of node-18. IIRC self-hosted has 22 enabled by default? whatever the value on SH side can be used here.

Comment on lines +79 to +81
// Get first available specification
const specification =
specificationsList?.specifications?.[0]?.slug || 's-0.5vcpu-512mb';
Copy link
Member

Choose a reason for hiding this comment

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

@atharvadeosthale agreee, see if we have proper constant for this. I think there's a RuntimeSpecification type 🤔

Comment on lines +103 to +111
envVars.map((variable) =>
sdk.forProject(params.region, params.project).functions.createVariable({
functionId: func.$id,
key: variable.key,
value: variable.value,
secret: false
})
)
);
Copy link
Member

Choose a reason for hiding this comment

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

unrelated but this has a very bad performance, we need a batch method for this.

// Redirect to function page
redirect(
302,
`${base}/project-${params.region}-${params.project}/functions/function-${func.$id}`
Copy link
Member

Choose a reason for hiding this comment

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

lets use resolve for all url building. type safety!

const preset = url.searchParams.get('preset') || 'nextjs';
const framework = (Object.values(Framework) as string[]).includes(preset.toLowerCase())
? (preset.toLowerCase() as Framework)
: Framework.Nextjs;
Copy link
Member

Choose a reason for hiding this comment

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

is this default intended? don't we have Other?

throw e;
}
// On error, fall through to show the wizard
console.error('Quick deploy failed:', e);
Copy link
Member

Choose a reason for hiding this comment

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

same here.

// Redirect to deploying page
redirect(
302,
`${base}/project-${params.region}-${params.project}/sites/create-site/deploying?site=${site.$id}&deployment=${deployment.$id}`
Copy link
Member

Choose a reason for hiding this comment

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

resolve

// Fetch plan info for billing checks
if (isCloud && selectedOrg) {
try {
currentPlan = await sdk.forConsole.billing.getOrganizationPlan(selectedOrg);
Copy link
Member

Choose a reason for hiding this comment

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

there's a plansInfo derived store that has cached plan details, we could use that to avoid a request. If a plan isn't found in there, we should fallback to making the API call.

const envKeys = envParam ? envParam.split(',').map((key: string) => key.trim()) : [];

// Parse env vars - supports KEY or KEY=value format
const envVars: Array<{ key: string; value: string }> = envParam
Copy link
Member

Choose a reason for hiding this comment

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

lets add an helper of sorts, duplicated in quite a few places.

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.

3 participants