Hot Updater

repository·main·Indexed 23 days ago

https://github.com/gronxb/hot-updater

A self-hosted, multi-platform Over-The-Air (OTA) update system for iOS and Android mobile apps. It supports React Native and new architectures, featuring bundle diffing for compact updates and an extensible plugin system for storage and databases. The system includes a server implementation compatible with Elysia.js, Express.js, and Hono, as well as a web-based management console for managing update bundles.

Tokens
125.5K
Snippets
339
Records
571
Agent score
79%

What's inside hot-updater

  1. Key Features of the Expo Plugin

    main

    The @hot-updater/expo plugin provides the following capabilities:

    • Uses Expo's expo export:embed command for bundling.
    • Automatically detects Hermes configuration from app.json.
    • Supports both managed and bare Expo workflows.
    • Compatible with expo prebuild for native builds.
    • Automatically configures the bundler based on your existing Expo settings.
  2. Key Features of the Bare (CLI) Plugin

    main

    The @hot-updater/bare plugin provides the following capabilities:

    • Metro Bundler Integration: Uses the standard React Native CLI's Metro bundler.
    • Hermes Support: Provides automatic Hermes bytecode compilation when enableHermes is set to true.
    • Optimized Minification: Minification is automatically handled by Hermes when enabled (standard minification is disabled in this mode).
    • Customization: Supports specifying custom entry files and output directories.
  3. Key features of Hot Updater

    main

    Hot Updater provides several core capabilities for managing mobile app updates:

    • Self-Hosting: Complete control over your update infrastructure.
    • Multi-Platform: Support for both iOS and Android.
    • Web Console: A management interface for overseeing updates.
    • Version Control: Robust versioning including semantic versioning support.
    • Forced Updates: The ability to push critical updates that users must install.
    • Channel Management: Environment separation (e.g., dev, staging, production).
    • Fingerprint Strategy: Automatic checking to ensure updates are compatible with the current native code.
  4. Understand Native Build prerequisites and artifact storage

    main

    Prerequisites

    • A React Native project with android/ and ios/ directories tracked.
    • hot-updater.config.ts must define at least one scheme for nativeBuild.android and/or nativeBuild.ios.
    • Local platform toolchains installed (gradlew, xcodebuild, CocoaPods/Bundler, devicectl/simctl).
    • Run hot-updater channel set <channel> so HOT_UPDATER_CHANNEL is present in Info.plist/strings.xml.
    • An active update strategy (appVersion or fingerprint) configured.

    Artifact Storage

    By default, artifacts are stored in .hot-updater/output/build/<platform>/<scheme>. This directory is recreated on every run. If you need to persist older artifacts for CI/CD, use the -o flag to point to a different directory.

  5. Understand the Project Structure and Database

    main

    The project is organized as follows:

    • src/index.ts: Main server entry point.
    • src/db.ts: Database setup (PGlite + Kysely + Hot Updater schema).
    • src/routes.ts: API route definitions.
    • data/: PGlite database files (stored at ./data/hot-updater.db). This directory is gitignored.

    Database Details

    The server uses PGlite for file-based persistence. The database schema is generated from Hot Updater's versioned schema and is migrated through the Hot Updater CLI. The schema is initialized automatically on the first run.

  6. Understand the Console API and Data Flow

    main

    The console integrates with Hot Updater's plugin system via TanStack Start server functions. These functions provide type-safe access to bundle management operations.

    Available Server Functions

    • getConfig(): Load console configuration.
    • getChannels(): List available channels.
    • getBundles(filters): List bundles with pagination.
    • getBundle(bundleId): Get single bundle details.
    • updateBundle(bundleId, data): Update bundle configuration.
    • createBundle(bundle): Create a new bundle.
    • deleteBundle(bundleId): Delete a bundle.

    Data Flow Model

    1. URL State: The useFilterParams() hook manages filter state via the URL.
    2. Server Functions: TanStack Start server functions call the underlying Hot Updater plugins.
    3. Data Fetching: React Query (e.g., useBundlesQuery()) fetches and caches data.
    4. UI Rendering: Components display data using shadcn/ui.
    5. Mutations: useUpdateBundleMutation() performs updates with optimistic UI updates.
    6. Invalidation: React Query automatically refreshes queries after mutations.
  7. How Supabase Storage profiles work

    main

    The supabaseStorage plugin implements two distinct storage profiles to handle different parts of the Hot Updater lifecycle:

    • node profile: Used during CLI or Console workflows. It handles uploading, deleting, and downloading files to/from the local filesystem.
    • runtime profile: Used by the client application. It creates signed download URLs and reads small metadata files directly through the Supabase Storage API.

    Note for Supabase Edge Functions: If you are running in a Supabase Edge Function environment, ensure you use the runtime-specific storage export. This allows update checks to resolve downloads without requiring local filesystem APIs.

  8. Understand Bundle Diffing and Patching

    main

    Hot Updater automatically includes artifacts for bundle diffing with every deployment. If the app runtime supports manifest-based diffing, the client will only download changed files instead of the entire archive.

    Hermes bsdiff Patches

    If you enable patch.enabled: true in your hot-updater.config.ts, the deployment process will also attempt to prepare compatible bsdiff patches for the Hermes bundle.

    Note: Patch generation is best-effort. If a patch fails to generate, the deployment will still succeed using the standard archive update.

  9. How Automatic Rollback works

    main

    Automatic rollback is a safety mechanism that prevents a broken Over-The-Air (OTA) bundle from trapping users in a crash loop. Hot Updater manages two roles: a Staging bundle (the newly installed bundle waiting for verification) and a Stable bundle (the last bundle known to start successfully).

    The Rollback Lifecycle:

    1. Install: The new bundle becomes the staging bundle; the previous working bundle is kept as a fallback.
    2. Launch: Hot Updater attempts to load the staging bundle first.
    3. Verify: If the app reaches its first successful render, the staging bundle is promoted to the trusted bundle.
    4. Recover: If the app crashes or exits before successful verification, the staging bundle is marked as failed, and Hot Updater automatically restores the last stable bundle (or the embedded bundle if no stable OTA fallback exists).

    To enable this mechanism, you must use HotUpdater.wrap() or call HotUpdater.init() when your runtime is ready.

  10. Control auto-reload behavior with reloadOnForceUpdate

    main

    The reloadOnForceUpdate option determines if the app automatically reloads after a force update bundle is downloaded.

    • If true (default): The app reloads automatically.
    • If false: The app will not reload automatically, but shouldForceUpdate will be returned as true in the onUpdateProcessCompleted callback, allowing you to trigger a reload manually via HotUpdater.reload().
    // Example without auto-reload
    export default HotUpdater.wrap({
      baseURL: "<your-update-server-url>",
      updateStrategy: "appVersion",
      reloadOnForceUpdate: false, // The app won't reload on force updates
      onUpdateProcessCompleted: ({ status, shouldForceUpdate, id, message }) => {
        console.log("Bundle updated:", status, shouldForceUpdate, id, message);
        if (shouldForceUpdate) {
          // Manually reload if needed
          await HotUpdater.reload();
        }
      },
    })(App);