Production Deployment: From Code to Users

Building a cool app on localhost is fun. Delivering it to millions of users is engineering.

This chapter covers the complete journey from commit to customerβ€”CI/CD pipelines, code signing, auto-updates, and production monitoring.

flowchart LR
    A["πŸ‘¨β€πŸ’» Commit"] --> B["πŸ”„ CI/CD"]
    B --> C["πŸ§ͺ Tests"]
    C --> D["πŸ“¦ Build"]
    D --> E["πŸ” Sign"]
    E --> F["πŸš€ Release"]
    F --> G["πŸ“± Auto-Update"]
    
    style F fill:#10b981


GitHub Actions: The Automation Engine

We use GitHub Actions to build our React site and Tauri binaries automatically on every push.

Web App Deployment

# .github/workflows/deploy-web.yml
name: Deploy Web App

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'
      
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      
      - name: Build
        run: pnpm build
        env:
          VITE_API_URL: ${{ vars.PRODUCTION_API_URL }}
      
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'

Desktop App Release

# .github/workflows/release-desktop.yml
name: Release Desktop App

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write

jobs:
  release:
    strategy:
      fail-fast: false
      matrix:
        include:
          - platform: macos-latest
            args: '--target universal-apple-darwin'
          - platform: ubuntu-22.04
            args: ''
          - platform: windows-latest
            args: ''
    
    runs-on: ${{ matrix.platform }}
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'
      
      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
      
      - name: Install Linux dependencies
        if: matrix.platform == 'ubuntu-22.04'
        run: |
          sudo apt-get update
          sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
      
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      
      - name: Build and Release
        uses: tauri-apps/tauri-action@v0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        with:
          tagName: ${{ github.ref_name }}
          releaseName: 'v__VERSION__'
          releaseBody: 'See assets below to download for your platform.'
          releaseDraft: false
          prerelease: false
          args: ${{ matrix.args }}

Code Signing

Without code signing, users see scary warnings:

Platform Warning
macOS β€œApp is from an unidentified developer”
Windows β€œWindows protected your PC”

macOS Code Signing

  1. Get an Apple Developer ID ($99/year at developer.apple.com)

  2. Export your certificate as a .p12 file

  3. Add to GitHub Secrets:

    • APPLE_CERTIFICATE β€” Base64 encoded .p12
    • APPLE_CERTIFICATE_PASSWORD β€” Password for the .p12
    • APPLE_SIGNING_IDENTITY β€” e.g., β€œDeveloper ID Application: Your Name”
    • APPLE_ID β€” Your Apple ID email
    • APPLE_PASSWORD β€” App-specific password
    • APPLE_TEAM_ID β€” Your team ID
  4. Notarization β€” Apple also requires notarization. Tauri handles this automatically with the secrets above.

Windows Code Signing

  1. Purchase a code signing certificate (DigiCert, Sectigo, etc.)

  2. Add to GitHub Secrets:

    • WINDOWS_CERTIFICATE β€” Base64 encoded .pfx
    • WINDOWS_CERTIFICATE_PASSWORD β€” Password

Auto-Updater

Tauri has built-in auto-update support. Users get prompted when new versions are available.

Configuration

// src-tauri/tauri.conf.json
{
  "plugins": {
    "updater": {
      "active": true,
      "dialog": true,
      "endpoints": [
        "https://github.com/YOUR_ORG/YOUR_REPO/releases/latest/download/latest.json"
      ],
      "pubkey": "YOUR_PUBLIC_KEY"
    }
  }
}

Generate Keys

# Generate signing keys for updates
npx @tauri-apps/cli signer generate -w ~/.tauri/myapp.key

Add TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PRIVATE_KEY_PASSWORD to your GitHub Secrets.

User Experience

When you publish a new release, users see:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  A new version is available!       β”‚
β”‚                                    β”‚
β”‚  Current: v1.0.0                   β”‚
β”‚  New: v1.1.0                       β”‚
β”‚                                    β”‚
β”‚  [Install Now]  [Later]            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Environment Management

Environment Variables

// vite.config.ts
export default defineConfig({
  define: {
    __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
  },
});
// Use in React
function Footer() {
  return <span>v{__APP_VERSION__}</span>;
}

Build-time Configuration

# GitHub Actions
- name: Build
  run: pnpm build
  env:
    VITE_API_URL: ${{ vars.PRODUCTION_API_URL }}
    VITE_SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
    VITE_ANALYTICS_ID: ${{ vars.ANALYTICS_ID }}

Error Tracking with Sentry

Catch production errors before users report them:

npm install @sentry/react
// src/main.tsx
import * as Sentry from '@sentry/react';

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN,
  environment: import.meta.env.MODE,
  release: __APP_VERSION__,
  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.replayIntegration(),
  ],
  tracesSampleRate: 0.1,
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
});

Error Boundary Integration

import * as Sentry from '@sentry/react';

function App() {
  return (
    <Sentry.ErrorBoundary fallback={<ErrorPage />}>
      <Routes />
    </Sentry.ErrorBoundary>
  );
}

Performance Monitoring

Lighthouse CI

# .github/workflows/lighthouse.yml
- name: Lighthouse CI
  uses: treosh/lighthouse-ci-action@v10
  with:
    urls: |
      https://your-app.com/
      https://your-app.com/dashboard
    configPath: ./lighthouserc.json
    uploadArtifacts: true
// lighthouserc.json
{
  "ci": {
    "assert": {
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.9 }],
        "categories:accessibility": ["error", { "minScore": 0.9 }],
        "first-contentful-paint": ["warn", { "maxNumericValue": 2000 }]
      }
    }
  }
}

Deployment Checklist

Before every production release:


Rollback Strategy

Things will break. Have a plan:

Vercel/Netlify

# Instant rollback to previous deployment
vercel rollback

GitHub Releases

Point users to the previous release while you fix:

# Quickly create a patch release
git tag v1.0.1
git push origin v1.0.1

TipDeployment Philosophy
  1. Automate everything β€” Manual deployments cause mistakes
  2. Ship small, ship often β€” Smaller changes = easier debugging
  3. Monitor from day one β€” You can’t fix what you can’t see
  4. Plan for failure β€” Rollbacks should take seconds, not hours

You started this book learning useEffect. You’re leaving it knowing how to architect a cross-platform, AI-powered desktop application with automated CI/CD.

Go build something amazing.