-
-
Notifications
You must be signed in to change notification settings - Fork 130
feat(rn): Capgo React Native updater + CLI with file-level delta #2703
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c032379
8c4465f
59a883a
2b4bd0d
33939ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { describe, expect, test } from 'bun:test' | ||
| import { mkdirSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs' | ||
| import { tmpdir } from 'node:os' | ||
| import { join } from 'node:path' | ||
|
|
||
| // Dynamic import after build; for source we import ts via bun | ||
| const { getCapgoUpdaterPackageVersion } = await import('../src/utils.ts') | ||
|
|
||
| describe('getCapgoUpdaterPackageVersion', () => { | ||
| test('detects react-native updater package', async () => { | ||
| const dir = mkdtempSync(join(tmpdir(), 'capgo-rn-')) | ||
| try { | ||
| mkdirSync(join(dir, 'node_modules', '@capgo', 'react-native-updater'), { recursive: true }) | ||
| writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: 'app', dependencies: { '@capgo/react-native-updater': '0.1.0' } })) | ||
| writeFileSync( | ||
| join(dir, 'node_modules', '@capgo', 'react-native-updater', 'package.json'), | ||
| JSON.stringify({ name: '@capgo/react-native-updater', version: '0.1.0' }), | ||
| ) | ||
| const result = await getCapgoUpdaterPackageVersion(dir) | ||
| expect(result?.kind).toBe('react-native') | ||
| expect(result?.version).toBe('0.1.0') | ||
| } | ||
| finally { | ||
| rmSync(dir, { recursive: true, force: true }) | ||
| } | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| lib/ | ||
| node_modules/ | ||
| android/build/ | ||
| ios/build/ | ||
| *.tsbuildinfo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| require "json" | ||
|
|
||
| package = JSON.parse(File.read(File.join(__dir__, "package.json"))) | ||
|
|
||
| Pod::Spec.new do |s| | ||
| s.name = "CapgoReactNativeUpdater" | ||
| s.version = package["version"] | ||
| s.summary = package["description"] | ||
| s.homepage = package["homepage"] | ||
| s.license = package["license"] | ||
| s.authors = { "Capgo" => "martin@capgo.app" } | ||
| s.platforms = { :ios => "13.0" } | ||
| s.source = { :git => "https://github.com/Cap-go/capgo.git", :tag => "rn-updater-#{s.version}" } | ||
| s.source_files = "ios/**/*.{h,m,mm,swift}" | ||
| s.dependency "React-Core" | ||
| s.swift_version = "5.0" | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # @capgo/react-native-updater | ||
|
|
||
| Capgo live updates for React Native. Uses the same Capgo Cloud backend and **file-level delta** system as `@capgo/capacitor-updater` (per-file SHA-256 manifests + optional Brotli), not binary bspatch. | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| npm install @capgo/react-native-updater | ||
| cd ios && pod install | ||
| ``` | ||
|
|
||
| ## Native wiring | ||
|
|
||
| ### Android | ||
|
|
||
| In `MainApplication`, override `getJSBundleFile()`: | ||
|
|
||
| ```kotlin | ||
| override fun getJSBundleFile(): String { | ||
| return CapgoUpdater.getJSBundleFile(applicationContext) | ||
| } | ||
| ``` | ||
|
|
||
| Add meta-data in `AndroidManifest.xml`: | ||
|
|
||
| ```xml | ||
| <meta-data android:name="CapgoAppId" android:value="com.example.app" /> | ||
| <meta-data android:name="CapgoUpdateUrl" android:value="https://plugin.capgo.app/updates" /> | ||
| <meta-data android:name="CapgoStatsUrl" android:value="https://plugin.capgo.app/stats" /> | ||
| ``` | ||
|
|
||
| ### iOS | ||
|
|
||
| In `AppDelegate`, return Capgo's bundle URL in release: | ||
|
|
||
| ```swift | ||
| #if !DEBUG | ||
| return CapgoUpdater.getJSBundleURL() | ||
| #endif | ||
| ``` | ||
|
|
||
| Add `CapgoAppId`, `CapgoUpdateUrl`, and `CapgoStatsUrl` to `Info.plist`. | ||
|
|
||
| ## JS usage | ||
|
|
||
| ```ts | ||
| import { CapgoUpdater } from '@capgo/react-native-updater' | ||
|
|
||
| await CapgoUpdater.notifyAppReady() | ||
|
|
||
| const latest = await CapgoUpdater.getLatest() | ||
| if (latest.url || latest.manifest?.length) { | ||
| const bundle = await CapgoUpdater.download({ | ||
| url: latest.url ?? 'https://404.capgo.app/no.zip', | ||
| version: latest.version, | ||
| sessionKey: latest.sessionKey, | ||
| checksum: latest.checksum ?? undefined, | ||
| manifest: latest.manifest, | ||
| }) | ||
| await CapgoUpdater.set({ id: bundle.id }) | ||
| } | ||
| ``` | ||
|
|
||
| ## Upload with CLI | ||
|
|
||
| ```bash | ||
| npx @capgo/rn-cli@latest upload appId --channel production | ||
| ``` | ||
|
|
||
| This runs Metro, exports `index.android.bundle` + `main.jsbundle` + assets, then uploads with Capgo `--delta`. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| buildscript { | ||
| ext.safeExtGet = { prop, fallback -> | ||
| rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback | ||
| } | ||
| } | ||
|
|
||
| apply plugin: "com.android.library" | ||
| apply plugin: "org.jetbrains.kotlin.android" | ||
|
|
||
| android { | ||
| namespace "app.capgo.rnupdater" | ||
| compileSdkVersion safeExtGet("compileSdkVersion", 34) | ||
|
|
||
| defaultConfig { | ||
| minSdkVersion safeExtGet("minSdkVersion", 24) | ||
| targetSdkVersion safeExtGet("targetSdkVersion", 34) | ||
| } | ||
|
|
||
| sourceSets { | ||
| main { | ||
| java.srcDirs = ["src/main/java"] | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // NOSONAR text:S8569 - library pins versions; consuming app owns Gradle dependency locking | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: This comment does not suppress the intended Prompt for AI agents |
||
| configurations.configureEach { | ||
| resolutionStrategy { | ||
| force "com.squareup.okhttp3:okhttp:4.12.0" | ||
| force "org.brotli:dec:0.1.2" | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation "com.facebook.react:react-android" | ||
| implementation "org.brotli:dec:0.1.2" | ||
| implementation "com.squareup.okhttp3:okhttp:4.12.0" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| android.useAndroidX=true |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,3 @@ | ||||||||||||||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||||||||||||||
| package="app.capgo.rnupdater"> | ||||||||||||||||
| </manifest> | ||||||||||||||||
|
Comment on lines
+1
to
+3
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Declare the network permission required by the updater. The module performs HTTP downloads, but its merged manifest contributes no Proposed fix <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.capgo.rnupdater">
+ <uses-permission android:name="android.permission.INTERNET" />
</manifest>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Fall back to the packaged bundle URL.
CapgoUpdater.getJSBundleURL()returnsnilfor"builtin"(CapgoUpdater.swift, Lines 27-28). Returning it directly can leave first installs and reset installations without a release bundle.Proposed documentation fix
📝 Committable suggestion
🤖 Prompt for AI Agents