UNPKG

@capgo/capacitor-updater

Version:
319 lines (245 loc) 155 kB
# Capacitor updater <a href="https://capgo.app/"><img src='https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png' alt='Capgo - Instant updates for capacitor'/></a> [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.com/invite/VnYRvBfgA6) <a href="https://discord.com/invite/VnYRvBfgA6"><img src="https://img.shields.io/discord/912707985829163099?color=%237289DA&label=Discord" alt="Discord"> [![npm](https://img.shields.io/npm/dm/@capgo/capacitor-updater)](https://www.npmjs.com/package/@capgo/capacitor-updater) [![GitHub latest commit](https://badgen.net/github/last-commit/Cap-go/capacitor-updater/main)](https://GitHub.com/Cap-go/capacitor-updater/commit/) [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Cap-go_capacitor-updater&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=Cap-go_capacitor-updater) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=Cap-go_capacitor-updater&metric=bugs)](https://sonarcloud.io/summary/new_code?id=Cap-go_capacitor-updater) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Cap-go_capacitor-updater&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=Cap-go_capacitor-updater) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=Cap-go_capacitor-updater&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=Cap-go_capacitor-updater) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=Cap-go_capacitor-updater&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=Cap-go_capacitor-updater) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Cap-go_capacitor-updater&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Cap-go_capacitor-updater) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=Cap-go_capacitor-updater&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=Cap-go_capacitor-updater) [![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2FCapgo%2Fbounties%3Fstatus%3Dopen)](https://console.algora.io/org/Capgo/bounties?status=open) [![Rewarded Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2FCapgo%2Fbounties%3Fstatus%3Dcompleted)](https://console.algora.io/org/Capgo/bounties?status=completed) <div align="center"> <h2><a href="https://capgo.app/?ref=plugin_updater_v7"> ➡️ Get Instant updates for your App with Capgo</a></h2> <h2><a href="https://capgo.app/consulting/?ref=plugin_updater_v7"> Missing a feature? We’ll build the plugin for you 💪</a></h2> </div> Capacitor plugin to update your app remotely in real-time. Open-source Alternative to Appflow, Codepush or Capawesome ## Why Capacitor Updater? App store review processes can take days or weeks, blocking critical bug fixes and updates from reaching your users. Capacitor Updater solves this by: - **Instant updates** - Push JavaScript/HTML/CSS updates directly to users without app store review - **Delta updates** - Only download changed files, making updates ultra-fast - **Rollback protection** - Automatically revert broken updates to keep your app stable - **Open source** - Self-host or use Capgo Cloud, with full control over your update infrastructure - **Battle-tested** - Used by 3000+ production apps with proven reliability - **Most stared** - Capacitor updater is the most stared Capacitor plugin on GitHub Perfect for fixing bugs immediately, A/B testing features, and maintaining control over your release schedule. ## Features - ☁️ Cloud / Self hosted Support: Use our [Cloud](https://capgo.app/) to manage your app updates or yours. - 📦 Bundle Management: Download, assign to channel, rollback. - 📺 Channel Support: Use channels to manage different environments. - 🎯 Set Channel to specific device to do QA or debug one user. - 🔄 Auto Update: Automatically download and set the latest bundle for the app. - 🛟 Rollback: Reset the app to last working bundle if an incompatible bundle has been set. - 🔁 **Delta Updates**: Make instant updates by only downloading changed files. - 🔒 **Security**: Encrypt and sign each updates with best in class security standards. - ⚔️ **Battle-Tested**: Used in more than 3000 projects. - 📊 View your deployment statistics - 🔋 Supports Android and iOS - ⚡️ Capacitor 4/5/6/7/8 support - 🌐 **Open Source**: Licensed under the Mozilla Public License 2.0 - 🌐 **Open Source Backend**: Self install [our backend](https://github.com/Cap-go/capgo) in your infra You have 3 ways possible : - Use [capgo.app](https://capgo.app) a full featured auto-update system in 5 min Setup, to manage version, update, revert and see stats. - Use your own server update with auto-update system - Use manual methods to zip, upload, download, from JS to do it when you want. ## Documentation The most complete [documentation here](https://capgo.app/docs/). ## Community Join the [discord](https://discord.gg/VnYRvBfgA6) to get help. ## Migration to v8 This major version is here to follow Capacitor major version 8 First follow the migration guide of Capacitor: [https://capacitorjs.com/docs/updating/8-0](https://capacitorjs.com/docs/updating/8-0/) ## Migration to v7.34 - **Channel storage change**: `setChannel()` now stores channel assignments locally on the device instead of in the cloud. This provides better offline support and reduces backend load. - Channel assignments persist between app restarts - Use `unsetChannel()` to clear the local assignment and revert to `defaultChannel` - Old devices (< v7.34.0) will continue using cloud-based storage - **New event**: Listen to the `channelPrivate` event to handle cases where a user tries to assign themselves to a private channel (one that doesn't allow self-assignment). See example in the `setChannel()` documentation above. ## Migration to v7 The minimum iOS version is now **15.0** to match Capacitor 7/8 requirements. Starting from v8, the plugin uses [ZIPFoundation](https://github.com/weichsel/ZIPFoundation) instead of SSZipArchive/ZipArchive for ZIP extraction. ZIPFoundation uses Apple's native `libcompression` framework, which removes the previous zlib dependency and its associated security constraints. ## Compatibility | Plugin version | Capacitor compatibility | Maintained | | -------------- | ----------------------- | ----------------- | | v8.\*.\* | v8.\*.\* | ✅ | | v7.\*.\* | v7.\*.\* | ✅ | | v6.\*.\* | v6.\*.\* | ✅ | | v5.\*.\* | v5.\*.\* | ✅ | | v4.\*.\* | v4.\*.\* | ✅ | | v3.\*.\* | v3.\*.\* | ⚠️ Deprecated | | > 7 | v4.\*.\* | ⚠️ Deprecated, our CI got crazy and bumped too much version | > **Note:** Versions 4, 5, 6, 7, and 8 all share the same features. The major version simply follows your Capacitor version. You can safely use any of these versions that matches your Capacitor installation. ### iOS #### Privacy manifest Add the `NSPrivacyAccessedAPICategoryUserDefaults` dictionary key to your [Privacy Manifest](https://capacitorjs.com/docs/ios/privacy-manifest) (usually `ios/App/PrivacyInfo.xcprivacy`): ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSPrivacyAccessedAPITypes</key> <array> <!-- Add this dict entry to the array if the file already exists. --> <dict> <key>NSPrivacyAccessedAPIType</key> <string>NSPrivacyAccessedAPICategoryUserDefaults</string> <key>NSPrivacyAccessedAPITypeReasons</key> <array> <string>CA92.1</string> </array> </dict> </array> </dict> </plist> ``` We recommend to declare [`CA92.1`](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278401) as the reason for accessing the [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults) API. ## Installation Step by step here: [Getting started](https://capgo.app/docs/getting-started/add-an-app/) Or ```bash npm install @capgo/capacitor-updater npx cap sync ``` ### Install a specific version Use npm tags to install the version matching your Capacitor version: ```bash # For Capacitor 8 (latest) npm install @capgo/capacitor-updater@latest # For Capacitor 7 npm install @capgo/capacitor-updater@lts-v7 # For Capacitor 6 npm install @capgo/capacitor-updater@lts-v6 # For Capacitor 5 npm install @capgo/capacitor-updater@lts-v5 # For Capacitor 4 npm install @capgo/capacitor-updater@lts-v4 ``` ## Auto-update setup Create your account in [capgo.app](https://capgo.app) and get your [API key](https://console.capgo.app/dashboard/apikeys) - Login to CLI `npx @capgo/cli@latest init API_KEY` And follow the steps by step to setup your app. For detailed instructions on the auto-update setup, refer to the [Auto update documentation](https://capgo.app/docs/plugin/cloud-mode/getting-started/). ## No Cloud setup Download update distribution zipfiles from a custom URL. Manually control the entire update process. - Edit your `capacitor.config.json` like below, set `autoUpdate` to false. ```json // capacitor.config.json { "appId": "**.***.**", "appName": "Name", "plugins": { "CapacitorUpdater": { "autoUpdate": false, } } } ``` - Add to your main code ```javascript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` This informs Capacitor Updater that the current update bundle has loaded successfully. Failing to call this method will cause your application to be rolled back to the previously successful version (or built-in bundle). - Add this to your application. ```javascript const version = await CapacitorUpdater.download({ version: '0.0.4', url: 'https://github.com/Cap-go/demo-app/releases/download/0.0.4/dist.zip', }) await CapacitorUpdater.set(version); // sets the new version, and reloads the app ``` - Failed updates will automatically roll back to the last successful version. - Example: Using App-state to control updates, with SplashScreen: You might also consider performing auto-update when application state changes, and using the Splash Screen to improve user experience. ```javascript import { CapacitorUpdater, VersionInfo } from '@capgo/capacitor-updater' import { SplashScreen } from '@capacitor/splash-screen' import { App } from '@capacitor/app' let version: VersionInfo; App.addListener('appStateChange', async (state) => { if (state.isActive) { // Ensure download occurs while the app is active, or download may fail version = await CapacitorUpdater.download({ version: '0.0.4', url: 'https://github.com/Cap-go/demo-app/releases/download/0.0.4/dist.zip', }) } if (!state.isActive && version) { // Activate the update when the application is sent to background SplashScreen.show() try { await CapacitorUpdater.set(version); // At this point, the new version should be active, and will need to hide the splash screen } catch () { SplashScreen.hide() // Hide the splash screen again if something went wrong } } }) ``` TIP: If you prefer a secure and automated way to update your app, you can use [capgo.app](https://capgo.app) - a full-featured, auto-update system. ### Store Guideline Compliance Android Google Play and iOS App Store have corresponding guidelines that have rules you should be aware of before integrating the Capacitor-updater solution within your application. #### Google play Third paragraph of [Device and Network Abuse](https://support.google.com/googleplay/android-developer/answer/9888379?hl=en) topic describe that updating source code by any method besides Google Play's update mechanism is restricted. But this restriction does not apply to updating JavaScript bundles. > This restriction does not apply to code that runs in a virtual machine and has limited access to Android APIs (such as JavaScript in a web view or browser). That fully allow Capacitor-updater as it updates just JS bundles and can't update native code part. #### App Store Paragraph **3.3.2**, since back in 2015's [Apple Developer Program License Agreement](https://developer.apple.com/programs/ios/information/) fully allowed performing over-the-air updates of JavaScript and assets. And in its latest version (20170605) [downloadable here](https://developer.apple.com/terms/) this ruling is even broader: > Interpreted code may be downloaded to an Application, but only so long as such code: - (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store - (b) does not create a store or storefront for other code or applications - (c) does not bypass signing, sandbox, or other security features of the OS. Capacitor-updater allows you to respect these rules in full compliance, so long as the update you push does not significantly deviate your product from its original App Store approved intent. To further remain in compliance with Apple's guidelines, we suggest that App Store-distributed apps don't enable the `Force update` scenario, since in the [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) it is written that: > Apps must not force users to rate the app, review the app, download other apps, or other similar actions to access functionality, content, or use of the app. This is not a problem for the default behavior of background update, since it won't force the user to apply the new version until the next app close, but at least you should be aware of that ruling if you decide to show it. ### Packaging `dist.zip` update bundles Capacitor Updater works by unzipping a compiled app bundle to the native device filesystem. Whatever you choose to name the file you upload/download from your release/update server URL (via either manual or automatic updating), this `.zip` bundle must meet the following requirements: - The zip file should contain the full contents of your production Capacitor build output folder, usually `{project directory}/dist/` or `{project directory}/www/`. This is where `index.html` will be located, and it should also contain all bundled JavaScript, CSS, and web resources necessary for your app to run. - Do not password encrypt the bundle zip file, or it will fail to unpack. - Make sure the bundle does not contain any extra hidden files or folders, or it may fail to unpack. ### Downgrading to a previous version of the updater plugin Downgrading to a previous version of the updater plugin is not supported. ## Updater Plugin Config <docgen-config> <!--Update the source file JSDoc comments and rerun docgen to update the docs below--> CapacitorUpdater can be configured with these options: | Prop | Type | Description | Default | Since | | ------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------- | | **`appReadyTimeout`** | <code>number</code> | Configure the number of milliseconds the native plugin should wait before considering an update 'failed'. Only available for Android and iOS. | <code>10000 // (10 seconds)</code> | | | **`responseTimeout`** | <code>number</code> | Configure the number of seconds the native plugin should wait before considering API timeout. Only available for Android and iOS. | <code>20 // (20 second)</code> | | | **`autoDeleteFailed`** | <code>boolean</code> | Configure whether the plugin should use automatically delete failed bundles. Only available for Android and iOS. | <code>true</code> | | | **`autoDeletePrevious`** | <code>boolean</code> | Configure whether the plugin should use automatically delete previous bundles after a successful update. Only available for Android and iOS. | <code>true</code> | | | **`autoUpdate`** | <code>boolean</code> | Configure whether the plugin should use Auto Update via an update server. Only available for Android and iOS. | <code>true</code> | | | **`resetWhenUpdate`** | <code>boolean</code> | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Setting this to false can broke the auto update flow if the user download from the store a native app bundle that is older than the current downloaded bundle. Upload will be prevented by channel setting downgrade_under_native. Only available for Android and iOS. | <code>true</code> | | | **`updateUrl`** | <code>string</code> | Configure the URL / endpoint to which update checks are sent. Only available for Android and iOS. | <code>https://plugin.capgo.app/updates</code> | | | **`channelUrl`** | <code>string</code> | Configure the URL / endpoint for channel operations. Only available for Android and iOS. | <code>https://plugin.capgo.app/channel_self</code> | | | **`statsUrl`** | <code>string</code> | Configure the URL / endpoint to which update statistics are sent. Only available for Android and iOS. Set to "" to disable stats reporting. | <code>https://plugin.capgo.app/stats</code> | | | **`publicKey`** | <code>string</code> | Configure the public key for end to end live update encryption Version 2 Only available for Android and iOS. | <code>undefined</code> | 6.2.0 | | **`version`** | <code>string</code> | Configure the current version of the app. This will be used for the first update request. If not set, the plugin will get the version from the native code. Only available for Android and iOS. | <code>undefined</code> | 4.17.48 | | **`directUpdate`** | <code>boolean \| 'always' \| 'atInstall' \| 'onLaunch'</code> | Configure when the plugin should direct install updates. Only for autoUpdate mode. Works well for apps less than 10MB and with uploads done using --delta flag. Zip or apps more than 10MB will be relatively slow for users to update. - false: Never do direct updates (use default behavior: download at start, set when backgrounded) - atInstall: Direct update only when app is installed, updated from store, otherwise act as directUpdate = false - onLaunch: Direct update only on app installed, updated from store or after app kill, otherwise act as directUpdate = false - always: Direct update in all previous cases (app installed, updated from store, after app kill or app resume), never act as directUpdate = false - true: (deprecated) Same as "always" for backward compatibility Activate this flag will automatically make the CLI upload delta in CICD envs and will ask for confirmation in local uploads. Only available for Android and iOS. | <code>false</code> | 5.1.0 | | **`autoSplashscreen`** | <code>boolean</code> | Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed. This removes the need to manually listen for appReady events and call SplashScreen.hide(). Only works when directUpdate is set to "atInstall", "always", "onLaunch", or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires autoUpdate and directUpdate to be enabled. Only available for Android and iOS. | <code>false</code> | 7.6.0 | | **`autoSplashscreenLoader`** | <code>boolean</code> | Display a native loading indicator on top of the splashscreen while automatic direct updates are running. Only takes effect when {@link autoSplashscreen} is enabled. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Only available for Android and iOS. | <code>false</code> | 7.19.0 | | **`autoSplashscreenTimeout`** | <code>number</code> | Automatically hide the splashscreen after the specified number of milliseconds when using automatic direct updates. If the timeout elapses, the update continues to download in the background while the splashscreen is dismissed. Set to `0` (zero) to disable the timeout. When the timeout fires, the direct update flow is skipped and the downloaded bundle is installed on the next background/launch. Requires {@link autoSplashscreen} to be enabled. Only available for Android and iOS. | <code>10000 // (10 seconds)</code> | 7.19.0 | | **`periodCheckDelay`** | <code>number</code> | Configure the delay period for period update check. the unit is in seconds. Only available for Android and iOS. Cannot be less than 600 seconds (10 minutes). | <code>0 (disabled)</code> | | | **`localS3`** | <code>boolean</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 | | **`localHost`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 | | **`localWebHost`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 | | **`localSupa`** | <code>string</code> | Configure the CLI to use a local server for testing or self-hosted update server. | <code>undefined</code> | 4.17.48 | | **`localSupaAnon`** | <code>string</code> | Configure the CLI to use a local server for testing. | <code>undefined</code> | 4.17.48 | | **`localApi`** | <code>string</code> | Configure the CLI to use a local api for testing. | <code>undefined</code> | 6.3.3 | | **`localApiFiles`** | <code>string</code> | Configure the CLI to use a local file api for testing. | <code>undefined</code> | 6.3.3 | | **`allowModifyUrl`** | <code>boolean</code> | Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side. | <code>false</code> | 5.4.0 | | **`allowModifyAppId`** | <code>boolean</code> | Allow the plugin to modify the appId dynamically from the JavaScript side. | <code>false</code> | 7.14.0 | | **`allowManualBundleError`** | <code>boolean</code> | Allow marking bundles as errored from JavaScript while using manual update flows. When enabled, {@link CapacitorUpdaterPlugin.setBundleError} can change a bundle status to `error`. | <code>false</code> | 7.20.0 | | **`persistCustomId`** | <code>boolean</code> | Persist the customId set through {@link CapacitorUpdaterPlugin.setCustomId} across app restarts. Only available for Android and iOS. | <code>false (will be true by default in a future major release v8.x.x)</code> | 7.17.3 | | **`persistModifyUrl`** | <code>boolean</code> | Persist the updateUrl, statsUrl and channelUrl set through {@link CapacitorUpdaterPlugin.setUpdateUrl}, {@link CapacitorUpdaterPlugin.setStatsUrl} and {@link CapacitorUpdaterPlugin.setChannelUrl} across app restarts. Only available for Android and iOS. | <code>false</code> | 7.20.0 | | **`allowSetDefaultChannel`** | <code>boolean</code> | Allow or disallow the {@link CapacitorUpdaterPlugin.setChannel} method to modify the defaultChannel. When set to `false`, calling `setChannel()` will return an error with code `disabled_by_config`.