Skip to content

Conversation

spartan-vutrannguyen
Copy link
Contributor

@spartan-vutrannguyen spartan-vutrannguyen commented Aug 8, 2025

Description

  • Replace in-memory artifact upload with provider-signed download URL
  • Build tar.gz in sandbox, get URL via provider.downloadFiles, pass to deployer

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Release
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Before:
image
After:
image

Additional Notes


Important

Update publish function to use provider-signed download URLs for deployments, supporting both file and URL-based methods.

  • Behavior:
    • Replace in-memory artifact upload with provider-signed download URL in publish.ts and deploy.ts.
    • Support both file-based and URL-based deployments in deployFreestyle().
    • Update DeploymentRequest type to handle 'url' and 'files'.
  • Classes/Functions:
    • Modify FreestyleAdapter in freestyle.ts to handle URL deployments.
    • Update PublishManager in manager.ts to build and upload artifacts using URLs.
  • Misc:
    • Add error handling for deployment failures in publish.ts.
    • Update deployFreestyle() to merge environment variables from sandbox and user input.

This description was created by Ellipsis for e8df827. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • Added artifact-based publishing that deploys from a URL, with automatic fallback to file-based deployment to ensure completion.
    • Supports deployments via uploaded artifact links in addition to file uploads.
    • Maintains deployment progress updates throughout the process.
  • Bug Fixes

    • Unified and clearer success/error messages during publish/unpublish.
    • More reliable deployments with safer defaults when no files are provided.
    • Improved handling of environment variables, ensuring user-provided values take precedence.

Copy link

vercel bot commented Aug 8, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Project Deployment Preview Comments Updated (UTC)
docs Ready Preview Comment Aug 13, 2025 5:34pm
web Ready Preview Comment Aug 13, 2025 5:34pm

Copy link

supabase bot commented Aug 8, 2025

This pull request has been ignored for the connected project wowaemfasoptxrdjhilu because there are no changes detected in apps/backend/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.


const artifactLocalPath = `${CUSTOM_OUTPUT_DIR}/standalone.tar.gz`;
const tarCommand = `tar -czf ${artifactLocalPath} -C ${NEXT_BUILD_OUTPUT_PATH} .`;
await this.provider.runCommand({ args: { command: tarCommand } });
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using a safer method (e.g. spawning a process with argument arrays) to execute the tar command to mitigate potential shell injection risks.

Copy link

coderabbitai bot commented Aug 13, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces URL-based deployments via artifacts, with fallback to file-based deployments. Refactors deployment request types to a discriminated union ("url" | "files"). Updates publish flow to build and upload a tar artifact first, then deploy by URL; if that fails, builds files and deploys them. Adapts adapters and helpers accordingly.

Changes

Cohort / File(s) Summary
Hosting model types
packages/models/src/hosting/index.ts
Replaces single DeploymentRequest interface with discriminated union: BaseDeploymentRequest, FileDeploymentRequest, UrlDeploymentRequest; adds type field ("url"
Freestyle hosting adapter
apps/web/client/src/server/api/routers/domain/adapters/freestyle.ts
Adds URL-based deployment path (kind: 'tar'), unifies error handling, defaults files to {}, enriches response message, minor import style change.
Deploy helper API
apps/web/client/src/server/api/routers/publish/helpers/deploy.ts
Changes deployFreestyle to single-arg discriminated union with type "url"
Publish flow and manager
apps/web/client/src/server/api/routers/publish/helpers/publish.ts, apps/web/client/src/server/api/routers/publish/manager.ts
Replaces publish with buildAndUploadArtifact returning artifact URL; adds buildFiles; modularizes pipeline (prepare, build, badge, postprocess), packages tar and retrieves download URL; publish helper tries artifact-first, falls back to files; merges env vars consistently.
Unpublish helper
apps/web/client/src/server/api/routers/publish/helpers/unpublish.ts
Updates deployFreestyle call to include type: 'files'; behavior otherwise unchanged.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PublishHelper
  participant PublishManager
  participant Provider
  participant FreestyleAdapter
  participant HostingSDK

  Client->>PublishHelper: publish(urls, envVars)
  PublishHelper->>PublishManager: buildAndUploadArtifact(...)
  PublishManager->>Provider: prepareProject + build + tar + downloadFiles
  Provider-->>PublishManager: artifact URL
  PublishManager-->>PublishHelper: sourceUrl
  PublishHelper->>FreestyleAdapter: deploy({type:'url', sourceUrl, config})
  FreestyleAdapter->>HostingSDK: deployWeb(kind:'tar', url, config)
  alt Artifact path fails
    PublishHelper->>PublishManager: buildFiles(...)
    PublishManager-->>PublishHelper: files map
    PublishHelper->>FreestyleAdapter: deploy({type:'files', files, config})
    FreestyleAdapter->>HostingSDK: deployWeb(kind:'files', files, config)
  end
  HostingSDK-->>FreestyleAdapter: response
  FreestyleAdapter-->>PublishHelper: DeploymentResponse
  PublishHelper-->>Client: result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Reduce memory consumption in build by avoiding in-memory file serialization for large projects (#2505)

Poem

I packed our code in a tidy tar,
Hitched it to a starlit URL car.
If that road’s closed, I’ll hop with files,
Byte by byte, across the miles.
Ears up, logs neat, memory light—
Ship it swift through the velvet night! 🐇🚀

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/update-publish-function

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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

♻️ Duplicate comments (1)
apps/web/client/src/server/api/routers/domain/adapters/freestyle.ts (1)

25-29: Previous review comment about operator precedence still applies.

Consider adding parentheses around the OR expression for clarity:

-                    freestyleResponse.error.message ?? freestyleResponse.message ?? 'Unknown error',
+                    (freestyleResponse.error.message || freestyleResponse.message) ?? 'Unknown error',
🧹 Nitpick comments (5)
apps/web/client/src/server/api/routers/publish/helpers/deploy.ts (2)

17-26: Complex type definition could be simplified for maintainability.

The inline discriminated union type definition is complex. Consider extracting it to a named type for better readability and reusability.

Add a type definition above the function:

type DeploymentArgs = {
    urls: string[];
    envVars?: Record<string, string>;
} & (
    | { type: 'url'; sourceUrl: string; files?: never }
    | { type: 'files'; files: Record<string, FreestyleFile>; sourceUrl?: never }
);

Then simplify the function signature:

-export const deployFreestyle = async (
-    args: (
-        { files: Record<string, FreestyleFile>; sourceUrl?: undefined } |
-        { files?: undefined; sourceUrl: string }
-    ) & {
-        urls: string[];
-        envVars?: Record<string, string>;
-        type: "url" | "files";
-    }
-): Promise<{
+export const deployFreestyle = async (
+    args: DeploymentArgs
+): Promise<{

35-62: Type guards could be more robust.

The current type checking relies on both the type field and the presence of data fields. This dual checking is redundant given the discriminated union design.

Simplify the conditions to rely solely on the discriminator:

-    if(args.type === 'url' && args.sourceUrl) {
+    if(args.type === 'url') {
         result = await adapter.deploy({
             type: 'url',
             sourceUrl: args.sourceUrl,
-    } else if(args.type === 'files' && args.files) {
+    } else if(args.type === 'files') {
         result = await adapter.deploy({
             type: 'files',
             files: Object.fromEntries(
apps/web/client/src/server/api/routers/publish/helpers/publish.ts (3)

82-83: Consider extracting duplicated environment variable merging logic.

The environment variable extraction and merging logic is duplicated between the artifact and file deployment paths.

Extract to a helper function at the top of the file:

async function getMergedEnvVars(
    provider: Provider,
    userEnvVars?: Record<string, string>
): Promise<Record<string, string>> {
    const sandboxEnvVars = await extractEnvVarsFromSandbox(provider);
    return { ...sandboxEnvVars, ...(userEnvVars ?? {}) };
}

Then use it in both places:

-                const sandboxEnvVars = await extractEnvVarsFromSandbox(provider);
-                const mergedEnvVars = { ...sandboxEnvVars, ...(envVars ?? {}) };
+                const mergedEnvVars = await getMergedEnvVars(provider, envVars);

Also applies to: 116-117


92-94: Consider more structured error handling for the fallback mechanism.

While the warning log is helpful, consider adding metrics or alerts for monitoring the fallback rate in production, as frequent fallbacks might indicate infrastructure issues.

Consider adding:

  1. Structured logging with error categorization
  2. Metrics tracking for fallback occurrences
  3. Alerting thresholds if fallback rate exceeds acceptable limits

69-79: Duplicated deployment update logic could be extracted.

The deployment status update to "Deploying build..." at 80% progress is duplicated between both paths.

Extract to a helper function:

async function updateToDeployingStatus(
    db: DrizzleDb, 
    deploymentId: string
): Promise<void> {
    const result = await updateDeployment(db, deploymentId, {
        status: DeploymentStatus.IN_PROGRESS,
        message: 'Deploying build...',
        progress: 80,
    });
    if (!result) {
        throw new TRPCError({
            code: 'BAD_REQUEST',
            message: 'Update deployment failed',
        });
    }
}

Then use in both places:

-                const updateDeploymentResult3 = await updateDeployment(db, deploymentId, {
-                    status: DeploymentStatus.IN_PROGRESS,
-                    message: 'Deploying build...',
-                    progress: 80,
-                });
-                if (!updateDeploymentResult3) {
-                    throw new TRPCError({
-                        code: 'BAD_REQUEST',
-                        message: 'Update deployment failed',
-                    });
-                }
+                await updateToDeployingStatus(db, deploymentId);

Also applies to: 104-114

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00fba94 and e8df827.

📒 Files selected for processing (6)
  • apps/web/client/src/server/api/routers/domain/adapters/freestyle.ts (1 hunks)
  • apps/web/client/src/server/api/routers/publish/helpers/deploy.ts (2 hunks)
  • apps/web/client/src/server/api/routers/publish/helpers/publish.ts (1 hunks)
  • apps/web/client/src/server/api/routers/publish/helpers/unpublish.ts (1 hunks)
  • apps/web/client/src/server/api/routers/publish/manager.ts (4 hunks)
  • packages/models/src/hosting/index.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
apps/web/client/src/server/api/routers/domain/adapters/freestyle.ts (2)
packages/models/src/hosting/index.ts (3)
  • HostingProviderAdapter (87-89)
  • DeploymentRequest (79-79)
  • DeploymentResponse (81-85)
apps/web/client/src/server/api/routers/domain/freestyle.ts (1)
  • initializeFreestyleSdk (5-15)
apps/web/client/src/server/api/routers/publish/helpers/publish.ts (4)
packages/constants/src/editor.ts (1)
  • DefaultSettings (38-70)
apps/web/client/src/server/api/routers/publish/helpers/helpers.ts (1)
  • updateDeployment (55-72)
apps/web/client/src/server/api/routers/publish/helpers/env.ts (1)
  • extractEnvVarsFromSandbox (41-68)
apps/web/client/src/server/api/routers/publish/helpers/deploy.ts (1)
  • deployFreestyle (17-69)
apps/web/client/src/server/api/routers/publish/helpers/deploy.ts (2)
apps/web/client/src/server/api/routers/domain/hosting-factory.ts (1)
  • HostingProviderFactory (4-13)
packages/models/src/hosting/index.ts (1)
  • DeploymentResponse (81-85)
apps/web/client/src/server/api/routers/publish/manager.ts (7)
packages/utility/src/file.ts (2)
  • FileOperations (17-28)
  • updateGitignore (36-72)
packages/growth/src/inject.ts (2)
  • injectBuiltWithScript (11-148)
  • addBuiltWithScript (155-177)
packages/parser/src/code-edit/next-config.ts (1)
  • addNextBuildConfig (182-247)
packages/constants/src/files.ts (1)
  • SUPPORTED_LOCK_FILES (23-28)
apps/web/client/src/server/api/routers/publish/helpers/helpers.ts (1)
  • updateDeployment (55-72)
apps/web/client/src/utils/git/index.ts (1)
  • escapeShellString (53-66)
packages/db/src/schema/project/deployment.ts (2)
  • deploymentUpdateSchema (48-48)
  • Deployment (50-50)
🔇 Additional comments (24)
packages/models/src/hosting/index.ts (2)

64-67: LGTM! Well-structured discriminated union pattern.

The base interface approach with discriminated union types is clean and follows TypeScript best practices. This pattern enables type-safe handling throughout the deployment pipeline.


69-77: LGTM! Clear type discrimination for deployment modes.

The separation between FileDeploymentRequest and UrlDeploymentRequest with explicit type literals enables proper exhaustiveness checking at compile time.

apps/web/client/src/server/api/routers/domain/adapters/freestyle.ts (3)

3-7: LGTM! Import style adjustment aligns with TypeScript conventions.

The change to import types alongside regular imports is clean and consistent.


13-36: LGTM! URL-based deployment path properly implemented.

The new URL deployment branch correctly handles the discriminated union type and provides consistent error handling with appropriate fallback messages using nullish coalescing.


38-44: Default files object prevents potential runtime errors.

Good defensive programming - defaulting request.files to {} ensures the SDK receives a valid object even when files are undefined.

apps/web/client/src/server/api/routers/publish/helpers/unpublish.ts (1)

24-29: LGTM! Correct usage of the new deployment API.

The unpublish function correctly uses the type: 'files' mode with an empty files object, which is appropriate for removing deployments.

apps/web/client/src/server/api/routers/publish/helpers/publish.ts (1)

60-94: Good fallback strategy for memory optimization.

The try-catch pattern with artifact-first deployment and file-based fallback directly addresses the memory consumption issue from #2505. The artifact approach using provider-signed URLs avoids loading entire repositories into memory.

apps/web/client/src/server/api/routers/publish/manager.ts (17)

12-12: LGTM - Import added for shell command safety.

The import of escapeShellString is appropriate for safely constructing shell commands later in the file.


22-23: LGTM - Type-only imports for better tree-shaking.

Using type-only imports for FreestyleFile and Provider is a good practice that can improve bundle size.


26-26: LGTM - Clean constructor injection.

Constructor injection of the provider follows good dependency injection patterns.


28-65: LGTM - Consistent provider API usage.

The refactoring to use a single args object for all provider calls improves consistency. The file operations abstraction is well-maintained.


67-70: LGTM - Good separation of concerns.

The addBadge method provides a clear abstraction for badge-related operations.


72-81: LGTM - Proper error handling in project preparation.

The method throws on critical failures while logging warnings for non-critical ones (gitignore update). This is appropriate behavior.


83-99: LGTM - Robust build step implementation.

Good handling of build flags with sensible defaults, and proper error propagation.


101-145: LGTM - Comprehensive postprocessing with clear error reporting.

The method properly validates the build output, copies necessary files, and handles lock file requirements with clear error messages.


147-159: LGTM - Well-structured artifact upload method.

The method signature is clear and follows the established pattern from the codebase.


224-229: LGTM - Proper error handling for download URL.

Good validation that the download URL is available before proceeding.


237-240: Good documentation of artifact lifecycle.

The comment clearly explains why the artifact isn't deleted immediately, preventing potential race conditions.


242-301: LGTM - Consistent pattern for file-based deployment.

The buildFiles method follows the same structure as buildAndUploadArtifact but returns serialized files instead of a URL. Good separation of concerns.


308-340: LGTM - Well-architected file serialization.

The method properly handles batching, categorization, and error handling. The parallel processing approach should help with performance.


342-373: LGTM - Efficient directory traversal.

Using a queue-based approach for directory traversal is more memory-efficient than recursion, and properly excludes heavy directories early.


377-387: LGTM - Comprehensive file filtering.

The skip conditions cover all the typical directories and files that should be excluded from deployment.


389-488: LGTM - Robust batch processing implementation.

Both text and binary file processing methods handle errors gracefully and use appropriate encodings. The batch size of 50 is reasonable for balancing memory usage and performance.


203-215: Shell argument escaping is robust and prevents injection.

The escapeShellString function correctly:

  • Returns "" for empty or non-string inputs.
  • Leaves only safe characters (A–Z, a–z, 0–9, ., _, -, /) unquoted.
  • Wraps all other inputs in single-quotes, escaping any embedded ' as '\''.

This ensures that each tar argument cannot break out of its intended value. No further changes are needed here.

@Kitenite
Copy link
Contributor

Publishing fails for me in a brand new project
https://d4a255a0-420f-4647-8448-fca81514f8b2.onlook.live

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.

[bug] Reduce memory consumption in the build process
2 participants