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
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.
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
Get an Apple Developer ID ($99/year at developer.apple.com)
Export your certificate as a
.p12fileAdd to GitHub Secrets:
APPLE_CERTIFICATEβ Base64 encoded.p12APPLE_CERTIFICATE_PASSWORDβ Password for the.p12APPLE_SIGNING_IDENTITYβ e.g., βDeveloper ID Application: Your NameβAPPLE_IDβ Your Apple ID emailAPPLE_PASSWORDβ App-specific passwordAPPLE_TEAM_IDβ Your team ID
Notarization β Apple also requires notarization. Tauri handles this automatically with the secrets above.
Windows Code Signing
Purchase a code signing certificate (DigiCert, Sectigo, etc.)
Add to GitHub Secrets:
WINDOWS_CERTIFICATEβ Base64 encoded.pfxWINDOWS_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.keyAdd 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 rollbackGitHub 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- Automate everything β Manual deployments cause mistakes
- Ship small, ship often β Smaller changes = easier debugging
- Monitor from day one β You canβt fix what you canβt see
- 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.