Files
OpenNoodl/dev-docs/tasks/phase-3-editor-ux-overhaul/TASK-005-deployment-automation/DEPLOY-002-preview-deployments.md

19 KiB
Raw Permalink Blame History

DEPLOY-002: Preview Deployments

Overview

Enable automatic preview deployments for each git branch or commit. When users push changes, a preview URL is automatically generated so stakeholders can review before merging to production.

Context

Currently, sharing work-in-progress requires:

  1. Manual deploy to a staging site
  2. Share URL with stakeholders
  3. Remember which deploy corresponds to which version
  4. Manually clean up old deploys

Preview deployments provide:

  • Automatic URL per branch/PR
  • Easy sharing with stakeholders
  • Visual history of changes
  • Automatic cleanup

This is especially valuable for:

  • Design reviews
  • QA testing
  • Client approvals
  • Team collaboration

Integration with GIT Series

From GIT-002:

  • Git status tracking per project
  • Branch awareness
  • Commit detection

This task leverages that infrastructure to trigger preview deploys.

Requirements

Functional Requirements

  1. Automatic Previews

    • Deploy preview on branch push
    • Unique URL per branch
    • Update preview on new commits
    • Delete preview on branch delete
  2. Manual Previews

    • "Deploy Preview" button in editor
    • Generate shareable URL
    • Named previews (optional)
    • Expiration settings
  3. Preview Management

    • List all active previews
    • View preview URL
    • Delete individual previews
    • Set auto-cleanup rules
  4. Sharing

    • Copy preview URL
    • QR code for mobile
    • Optional password protection
    • Expiration timer
  5. Integration with PRs

    • Comment preview URL on PR
    • Update comment on new commits
    • Status check integration

Non-Functional Requirements

  • Preview available within 2 minutes
  • Support 10+ concurrent previews
  • Auto-cleanup after configurable period
  • Works with all deploy providers

Technical Approach

1. Preview Service

// packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts

interface PreviewDeployment {
  id: string;
  projectId: string;
  branch: string;
  commitSha: string;
  url: string;
  platform: DeployPlatform;
  siteId: string;
  status: PreviewStatus;
  createdAt: string;
  expiresAt?: string;
  password?: string;
  name?: string;
}

enum PreviewStatus {
  PENDING = 'pending',
  BUILDING = 'building',
  READY = 'ready',
  FAILED = 'failed',
  EXPIRED = 'expired'
}

interface PreviewConfig {
  enabled: boolean;
  autoDeployBranches: boolean;
  excludeBranches: string[];  // e.g., ['main', 'master']
  expirationDays: number;
  maxPreviews: number;
  passwordProtect: boolean;
  commentOnPR: boolean;
}

class PreviewDeployService {
  private static instance: PreviewDeployService;
  
  // Preview management
  async createPreview(options: CreatePreviewOptions): Promise<PreviewDeployment>;
  async updatePreview(previewId: string): Promise<PreviewDeployment>;
  async deletePreview(previewId: string): Promise<void>;
  async listPreviews(projectId: string): Promise<PreviewDeployment[]>;
  
  // Auto-deployment
  async onBranchPush(projectId: string, branch: string, commitSha: string): Promise<void>;
  async onBranchDelete(projectId: string, branch: string): Promise<void>;
  
  // PR integration
  async commentOnPR(preview: PreviewDeployment): Promise<void>;
  async updatePRComment(preview: PreviewDeployment): Promise<void>;
  
  // Cleanup
  async cleanupExpiredPreviews(): Promise<void>;
  async enforceMaxPreviews(projectId: string): Promise<void>;
  
  // Configuration
  getConfig(projectId: string): PreviewConfig;
  setConfig(projectId: string, config: Partial<PreviewConfig>): void;
}

2. Branch-Based Preview Naming

// Generate preview URLs based on branch
function generatePreviewUrl(platform: DeployPlatform, branch: string, projectName: string): string {
  const sanitizedBranch = sanitizeBranchName(branch);
  
  switch (platform) {
    case DeployPlatform.NETLIFY:
      // Netlify: branch--sitename.netlify.app
      return `https://${sanitizedBranch}--${projectName}.netlify.app`;
      
    case DeployPlatform.VERCEL:
      // Vercel: project-branch-hash.vercel.app
      return `https://${projectName}-${sanitizedBranch}.vercel.app`;
      
    case DeployPlatform.GITHUB_PAGES:
      // GitHub Pages: use subdirectory or separate branch
      return `https://${owner}.github.io/${repo}/preview/${sanitizedBranch}`;
      
    default:
      return '';
  }
}

function sanitizeBranchName(branch: string): string {
  return branch
    .toLowerCase()
    .replace(/[^a-z0-9-]/g, '-')
    .replace(/-+/g, '-')
    .substring(0, 50);
}

3. Git Integration Hook

// packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts

// Hook into git operations
class PreviewDeployService {
  constructor() {
    // Listen for git events
    EventDispatcher.instance.on('git.push.success', this.handlePush.bind(this));
    EventDispatcher.instance.on('git.branch.delete', this.handleBranchDelete.bind(this));
  }
  
  private async handlePush(event: GitPushEvent): Promise<void> {
    const config = this.getConfig(event.projectId);
    
    if (!config.enabled || !config.autoDeployBranches) {
      return;
    }
    
    // Check if branch is excluded
    if (config.excludeBranches.includes(event.branch)) {
      return;
    }
    
    // Check if we already have a preview for this branch
    const existing = await this.findPreviewByBranch(event.projectId, event.branch);
    
    if (existing) {
      // Update existing preview
      await this.updatePreview(existing.id);
    } else {
      // Create new preview
      await this.createPreview({
        projectId: event.projectId,
        branch: event.branch,
        commitSha: event.commitSha
      });
    }
    
    // Comment on PR if enabled
    if (config.commentOnPR) {
      const pr = await this.findPRForBranch(event.projectId, event.branch);
      if (pr) {
        await this.commentOnPR(existing || await this.getLatestPreview(event.projectId, event.branch));
      }
    }
  }
  
  private async handleBranchDelete(event: GitBranchDeleteEvent): Promise<void> {
    const preview = await this.findPreviewByBranch(event.projectId, event.branch);
    if (preview) {
      await this.deletePreview(preview.id);
    }
  }
}

4. PR Comment Integration

// packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts

async commentOnPR(preview: PreviewDeployment): Promise<void> {
  const github = GitHubApiClient.instance;
  const project = ProjectModel.instance;
  const remote = project.getRemoteUrl();
  
  if (!remote || !remote.includes('github.com')) {
    return;  // Only GitHub PRs supported
  }
  
  const { owner, repo } = parseGitHubUrl(remote);
  const pr = await github.findPRByBranch(owner, repo, preview.branch);
  
  if (!pr) {
    return;
  }
  
  const commentBody = this.generatePRComment(preview);
  
  // Check for existing Noodl comment
  const existingComment = await github.findComment(owner, repo, pr.number, '<!-- noodl-preview -->');
  
  if (existingComment) {
    await github.updateComment(owner, repo, existingComment.id, commentBody);
  } else {
    await github.createComment(owner, repo, pr.number, commentBody);
  }
}

private generatePRComment(preview: PreviewDeployment): string {
  return `<!-- noodl-preview -->
## 🚀 Noodl Preview Deployment

| Status | URL |
|--------|-----|
| ${this.getStatusEmoji(preview.status)} ${preview.status} | [${preview.url}](${preview.url}) |

**Branch:** \`${preview.branch}\`  
**Commit:** \`${preview.commitSha.substring(0, 7)}\`  
**Updated:** ${new Date(preview.createdAt).toLocaleString()}

---
<sub>Deployed automatically by Noodl</sub>`;
}

5. UI Components

Preview Manager Panel

┌─────────────────────────────────────────────────────────────────────┐
│ Preview Deployments                                           [×]  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│ ACTIVE PREVIEWS (3)                                                │
│                                                                     │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 🌿 feature/new-dashboard                           [Copy URL]  │ │
│ │    https://feature-new-dashboard--myapp.netlify.app            │ │
│ │    Updated 10 minutes ago • Commit abc1234                     │ │
│ │    [Open ↗] [QR Code] [Delete]                                 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ 🌿 feature/login-redesign                          [Copy URL]  │ │
│ │    https://feature-login-redesign--myapp.netlify.app           │ │
│ │    Updated 2 hours ago • Commit def5678                        │ │
│ │    [Open ↗] [QR Code] [Delete]                                 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ 🌿 bugfix/form-validation                          [Copy URL]  │ │
│ │    https://bugfix-form-validation--myapp.netlify.app           │ │
│ │    Updated yesterday • Commit ghi9012                          │ │
│ │    [Open ↗] [QR Code] [Delete]                                 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│                                                                     │
│ SETTINGS                                                            │
│ ☑ Auto-deploy branches                                             │
│ ☑ Comment preview URL on PRs                                       │
│ Exclude branches: main, master                                     │
│ Auto-delete after: [7 days ▾]                                      │
│                                                                     │
│                                        [+ Create Manual Preview]   │
└─────────────────────────────────────────────────────────────────────┘

QR Code Modal

┌─────────────────────────────────────────────────────────────────────┐
│ Share Preview                                                 [×]  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│                    ┌─────────────────┐                             │
│                    │ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄│                             │
│                    │ █     █ █     █│                             │
│                    │ █ ███ █ █ ███ █│   Scan to open              │
│                    │ █     █ █     █│   on mobile                 │
│                    │ ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀│                             │
│                    └─────────────────┘                             │
│                                                                     │
│ feature/new-dashboard                                              │
│ https://feature-new-dashboard--myapp.netlify.app                   │
│                                                                     │
│                              [Copy URL]  [Download QR]  [Close]    │
└─────────────────────────────────────────────────────────────────────┘

Create Manual Preview

┌─────────────────────────────────────────────────────────────────────┐
│ Create Preview                                                [×]  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│ Preview Name (optional):                                           │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ client-review-dec-15                                           │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│                                                                     │
│ Expires in:                                                         │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 7 days                                                      [▾] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│                                                                     │
│ ☐ Password protect                                                 │
│   Password: [••••••••                                            ] │
│                                                                     │
│ Deploy from:                                                        │
│ ○ Current state (uncommitted changes included)                     │
│ ● Current branch (feature/new-dashboard)                           │
│ ○ Specific commit: [abc1234 ▾]                                     │
│                                                                     │
│                                      [Cancel]  [Create Preview]    │
└─────────────────────────────────────────────────────────────────────┘

Files to Create

  1. packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts
  2. packages/noodl-core-ui/src/components/deploy/PreviewManager/PreviewManager.tsx
  3. packages/noodl-core-ui/src/components/deploy/PreviewCard/PreviewCard.tsx
  4. packages/noodl-core-ui/src/components/deploy/CreatePreviewModal/CreatePreviewModal.tsx
  5. packages/noodl-core-ui/src/components/deploy/QRCodeModal/QRCodeModal.tsx
  6. packages/noodl-core-ui/src/components/deploy/PreviewSettings/PreviewSettings.tsx

Files to Modify

  1. packages/noodl-editor/src/editor/src/services/DeployService.ts

    • Add preview deployment methods
    • Integrate with deploy providers
  2. packages/noodl-editor/src/editor/src/views/DeployPopup/DeployPopup.tsx

    • Add "Previews" tab
    • Integrate PreviewManager
  3. packages/noodl-editor/src/editor/src/models/projectmodel.ts

    • Store preview configuration
    • Track active previews
  4. packages/noodl-editor/src/editor/src/services/GitHubApiClient.ts

    • Add PR comment methods
    • Add PR lookup by branch

Implementation Steps

Phase 1: Preview Service

  1. Create PreviewDeployService
  2. Implement preview creation
  3. Implement preview deletion
  4. Add configuration storage

Phase 2: Git Integration

  1. Hook into push events
  2. Hook into branch delete events
  3. Implement auto-deployment
  4. Test with branches

Phase 3: PR Integration

  1. Implement PR comment creation
  2. Implement comment updating
  3. Add status emoji handling
  4. Test with GitHub PRs

Phase 4: UI - Preview Manager

  1. Create PreviewManager component
  2. Create PreviewCard component
  3. Add copy/share functionality
  4. Implement delete action

Phase 5: UI - Create Preview

  1. Create CreatePreviewModal
  2. Add expiration options
  3. Add password protection
  4. Add source selection

Phase 6: UI - QR & Sharing

  1. Create QRCodeModal
  2. Add QR code generation
  3. Add download option
  4. Polish sharing UX

Testing Checklist

  • Auto-preview on push works
  • Preview URL is correct
  • PR comment created
  • PR comment updated on new commit
  • Manual preview creation works
  • Preview deletion works
  • Auto-cleanup works
  • QR code generates correctly
  • Password protection works
  • Expiration works
  • Multiple previews supported

Dependencies

  • DEPLOY-001 (One-Click Deploy) - for deploy providers
  • GIT-001 (GitHub OAuth) - for PR comments
  • GIT-002 (Git Status) - for branch awareness

Blocked By

  • DEPLOY-001

Blocks

  • None

Estimated Effort

  • Preview service: 5-6 hours
  • Git integration: 4-5 hours
  • PR integration: 3-4 hours
  • UI preview manager: 4-5 hours
  • UI create preview: 3-4 hours
  • UI QR/sharing: 2-3 hours
  • Testing & polish: 3-4 hours
  • Total: 24-31 hours

Success Criteria

  1. Auto-preview deploys on branch push
  2. Preview URL unique per branch
  3. PR comments posted automatically
  4. Manual previews can be created
  5. QR codes work for mobile testing
  6. Expired previews auto-cleaned

Platform-Specific Implementation

Netlify

  • Branch deploys built-in
  • URL pattern: branch--site.netlify.app
  • Easy configuration

Vercel

  • Preview deployments automatic
  • URL pattern: project-branch-hash.vercel.app
  • Good GitHub integration

GitHub Pages

  • Need separate approach (subdirectory or deploy to different branch)
  • Less native support for branch previews

Future Enhancements

  • Visual regression testing
  • Screenshot comparison
  • Performance metrics per preview
  • A/B testing setup
  • Preview environments (staging, QA)
  • Slack/Teams notifications