Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/skills/usage/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ TanStack Intent skills should stay focused and under the validator line limit, s

### Project setup and diagnostics

- `init [apikey] [appId]`: guided first-time setup for Capgo in a Capacitor app. The interactive flow now runs as a real Ink-based fullscreen onboarding so it uses the same UI stack as `build init` (alias: `build onboarding`), with a persistent dashboard, phase roadmap, progress cards, shared log area, and resume support. When dependency auto-detection fails on macOS, the flow opens a native file picker for `package.json` before falling back to manual path entry. If the local bundle ID already exists in the selected Capgo account, onboarding offers to reuse that app, then offers to delete and recreate it, then falls back to alternate bundle ID suggestions. If the user reuses a pending app that was already created in the web onboarding flow, the CLI syncs that selected dashboard app ID back into `capacitor.config.*` before the remaining steps continue. Outside that reused pending-app path, the CLI keeps using the local Capacitor app ID. It can also offer a final `npx skills add https://github.com/Cap-go/capgo-skills -g -y` install step before the GitHub support prompt; if accepted, the support menu includes `Cap-go/capgo-skills` alongside the updater-only and all-Capgo choices. If native platforms are missing, the onboarding can offer to run `cap add` for you. The updater step now verifies that `@capgo/capacitor-updater` is both declared in the selected `package.json` and resolvable from `node_modules`; if automatic install or later build/sync fails, onboarding prints the manual command, waits for the user to type `ready`, re-checks, and only then continues. During the iOS run-on-device step, onboarding asks whether to use a physical iPhone/iPad or a simulator; for physical devices, it asks the user to connect and unlock the device, then offers a check-again loop before launching with the detected target. If iOS sync validation fails during onboarding, the CLI can offer to run a one-line native reset command, wait for you to type `ready` after a manual fix, surface `doctor`, and save a support bundle before you leave the flow.
- `init [apikey] [appId]`: guided first-time setup for Capgo in a Capacitor app. The interactive flow now runs as a real Ink-based fullscreen onboarding so it uses the same UI stack as `build init` (alias: `build onboarding`), with a persistent dashboard, phase roadmap, progress cards, shared log area, and resume support. When dependency auto-detection fails on macOS, the flow opens a native file picker for `package.json` before falling back to manual path entry. If the local bundle ID already exists in the selected Capgo account, onboarding offers to reuse that app, then offers to delete and recreate it, then falls back to alternate bundle ID suggestions. If the user reuses a pending app that was already created in the web onboarding flow, the CLI syncs that selected dashboard app ID back into `capacitor.config.*` before the remaining steps continue. Outside that reused pending-app path, the CLI keeps using the local Capacitor app ID. It writes the new `autoUpdate` policy modes into config: `"atBackground"` for the default flow and `"always"` for instant updates. It can also offer a final `npx skills add https://github.com/Cap-go/capgo-skills -g -y` install step before the GitHub support prompt; if accepted, the support menu includes `Cap-go/capgo-skills` alongside the updater-only and all-Capgo choices. If native platforms are missing, the onboarding can offer to run `cap add` for you. The updater step now verifies that `@capgo/capacitor-updater` is both declared in the selected `package.json` and resolvable from `node_modules`; if automatic install or later build/sync fails, onboarding prints the manual command, waits for the user to type `ready`, re-checks, and only then continues. During the iOS run-on-device step, onboarding asks whether to use a physical iPhone/iPad or a simulator; for physical devices, it asks the user to connect and unlock the device, then offers a check-again loop before launching with the detected target. If iOS sync validation fails during onboarding, the CLI can offer to run a one-line native reset command, wait for you to type `ready` after a manual fix, surface `doctor`, and save a support bundle before you leave the flow.
- `run device [platform]`: run a Capacitor app on a connected device or simulator. In an interactive terminal, omitting `[platform]` asks whether to start on iOS or Android. The command lists available devices and simulators, includes a reload option, and resolves the `cap run` command. Use `npx @capgo/cli@latest run device ios --no-launch` to exercise iOS physical/simulator target selection and print the resolved command without launching the app.
- `login [apikey]`: store an API key locally.
- `doctor`: inspect installation health and gather troubleshooting details.
Expand Down
15 changes: 8 additions & 7 deletions cli/src/bundle/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getChecksum } from '../checksum'
import { getRepoStarStatus, isRepoStarredInSession, starRepository } from '../github'
import { confirmWithRememberedChoice } from '../promptPreferences'
import { showReplicationProgress } from '../replicationProgress'
import { usesAlwaysDirectUpdate } from '../updaterConfig'
import { baseKeyV2, BROTLI_MIN_UPDATER_VERSION_V5, BROTLI_MIN_UPDATER_VERSION_V6, BROTLI_MIN_UPDATER_VERSION_V7, canPromptInteractively, checkChecksum, checkCompatibilityCloud, checkPlanValidUpload, checkRemoteCliMessages, createSupabaseClient, deletedFailedVersion, findRoot, findSavedKey, formatError, getAppId, getBundleVersion, getCompatibilityDetails, getConfig, getInstalledVersion, getLocalConfig, getLocalDependencies, getOrganizationId, getPMAndCommand, getRemoteFileConfig, hasCliPermission, hasOrganizationPerm, isCompatible, isDeprecatedPluginVersion, OrganizationPerm, regexSemver, resolveUserIdFromApiKey, sendEvent, updateConfigUpdater, updateOrCreateChannel, updateOrCreateVersion, UPLOAD_TIMEOUT, uploadTUS, uploadUrl, zipFile } from '../utils'
import { getVersionSuggestions, interactiveVersionBump } from '../versionHelpers'
import { checkIndexPosition, searchInDirectory } from './check'
Expand Down Expand Up @@ -749,29 +750,29 @@ export async function uploadBundleInternal(preAppid: string, options: OptionsUpl
if (options.verbose)
log.info(`[Verbose] Capacitor config loaded successfully`)

// Check if directUpdate is enabled and auto-enable delta updates
const directUpdateEnabled = extConfig?.config?.plugins?.CapacitorUpdater?.directUpdate === 'always'
// Check if instant updates are enabled and auto-enable delta updates.
const instantUpdateEnabled = usesAlwaysDirectUpdate(extConfig?.config?.plugins?.CapacitorUpdater)
const interactive = canPromptInteractively({ silent })
if (directUpdateEnabled && options.delta === undefined) {
if (instantUpdateEnabled && options.delta === undefined) {
if (interactive) {
log.info('💡 Direct Update (instant updates) is enabled in your config')
log.info('💡 Instant updates are enabled in your config')
log.info(' Delta updates send only changed files instead of the full bundle')
const enableDelta = await pConfirm({
message: 'Enable delta updates for this upload? (Recommended with Direct Update)',
message: 'Enable delta updates for this upload? (Recommended with instant updates)',
initialValue: true,
})
if (!pIsCancel(enableDelta) && enableDelta) {
options.delta = true
if (options.verbose)
log.info(`[Verbose] Delta updates auto-enabled due to Direct Update configuration`)
log.info(`[Verbose] Delta updates auto-enabled due to instant update configuration`)
}
}
else if (!silent) {
// Non-interactive mode (CI/CD): auto-enable unless explicitly disabled
if (options.delta !== false) {
options.delta = true
if (options.verbose)
log.info(`[Verbose] Delta updates auto-enabled in CI/CD mode due to Direct Update configuration`)
log.info(`[Verbose] Delta updates auto-enabled in CI/CD mode due to instant update configuration`)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ Example: npx @capgo/cli@latest bundle upload com.example.app --path ./dist --cha
.option('--partial-only', `[DEPRECATED] Use --delta-only instead. Upload only incremental updates, skip full bundle`)
.option('--delta', `Upload delta updates (only changed files) for instant, super-fast updates instead of big zip downloads`)
.option('--delta-only', `Upload only delta updates without full bundle for maximum speed (useful for large apps)`)
.option('--no-delta', `Disable delta updates even if Direct Update is enabled`)
.option('--no-delta', `Disable delta updates even if instant updates are enabled`)
.option('--encrypted-checksum <encryptedChecksum>', `An encrypted checksum (signature). Used only when uploading an external bundle.`)
.option('--auto-set-bundle', `Set the bundle in capacitor.config.json`)
.option('--dry-upload', `Dry upload the bundle process: add the row in database without uploading files or updating channels (Used by Capgo for internal testing)`)
Expand Down
3 changes: 1 addition & 2 deletions cli/src/init/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,9 @@ export function getInitUpdaterPluginConfig(appId: string, directInstall: boolean
return {
version: initNativeBundleVersion,
appId,
autoUpdate: true,
autoUpdate: directInstall ? 'always' : 'atBackground',
...(directInstall
? {
directUpdate: 'always',
autoSplashscreen: true,
}
: {}),
Expand Down
20 changes: 20 additions & 0 deletions cli/src/updaterConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type AutoUpdatePolicy = boolean | 'off' | 'atBackground' | 'atInstall' | 'onLaunch' | 'always' | 'onlyDownload'
type DirectUpdatePolicy = boolean | 'atInstall' | 'always' | 'onLaunch'

export interface CapacitorUpdaterPluginConfig {
autoUpdate?: AutoUpdatePolicy
directUpdate?: DirectUpdatePolicy
}

export function usesAlwaysDirectUpdate(config: CapacitorUpdaterPluginConfig | undefined): boolean {
const autoUpdate = config?.autoUpdate

if (autoUpdate === 'always')
return true

if (typeof autoUpdate === 'string' || autoUpdate === false)
return false

const directUpdate = config?.directUpdate
return directUpdate === true || directUpdate === 'always'
}
16 changes: 13 additions & 3 deletions cli/test/test-init-guardrails.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
isOnlyAllowedInitAutoTestChange,
revertInitAutoTestChangeContent,
} from '../src/init/command.ts'
import { usesAlwaysDirectUpdate } from '../src/updaterConfig.ts'

let failures = 0

Expand Down Expand Up @@ -83,18 +84,27 @@ t('init updater config always starts from native version 0.0.0', () => {
assert.deepEqual(getInitUpdaterPluginConfig('com.example.app', false), {
version: '0.0.0',
appId: 'com.example.app',
autoUpdate: true,
autoUpdate: 'atBackground',
})

assert.deepEqual(getInitUpdaterPluginConfig('com.example.app', true), {
version: '0.0.0',
appId: 'com.example.app',
autoUpdate: true,
directUpdate: 'always',
autoUpdate: 'always',
autoSplashscreen: true,
})
})

t('instant update detection supports new autoUpdate modes and legacy directUpdate', () => {
assert.equal(usesAlwaysDirectUpdate({ autoUpdate: 'always' }), true)
assert.equal(usesAlwaysDirectUpdate({ autoUpdate: 'atBackground', directUpdate: 'always' }), false)
assert.equal(usesAlwaysDirectUpdate({ autoUpdate: 'onlyDownload', directUpdate: true }), false)
assert.equal(usesAlwaysDirectUpdate({ autoUpdate: false, directUpdate: true }), false)
assert.equal(usesAlwaysDirectUpdate({ autoUpdate: true, directUpdate: true }), true)
assert.equal(usesAlwaysDirectUpdate({ directUpdate: 'always' }), true)
assert.equal(usesAlwaysDirectUpdate({ directUpdate: 'onLaunch' }), false)
})

t('guided ota version suggestions stay on major zero when native baseline is pinned', () => {
assert.equal(getInitOtaVersionBase('1.0.0'), '0.0.0')
assert.equal(getInitSuggestedOtaVersion('1.0.0'), '0.0.1')
Expand Down
3 changes: 1 addition & 2 deletions cli/webdocs/bundle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ npx @capgo/cli@latest bundle upload com.example.app --path ./dist --channel prod
| **--partial-only** | <code>boolean</code> | [DEPRECATED] Use --delta-only instead. Upload only incremental updates, skip full bundle |
| **--delta** | <code>boolean</code> | Upload delta updates (only changed files) for instant, super-fast updates instead of big zip downloads |
| **--delta-only** | <code>boolean</code> | Upload only delta updates without full bundle for maximum speed (useful for large apps) |
| **--no-delta** | <code>boolean</code> | Disable delta updates even if Direct Update is enabled |
| **--no-delta** | <code>boolean</code> | Disable delta updates even if instant updates are enabled |
| **--encrypted-checksum** | <code>string</code> | An encrypted checksum (signature). Used only when uploading an external bundle. |
| **--auto-set-bundle** | <code>boolean</code> | Set the bundle in capacitor.config.json |
| **--dry-upload** | <code>boolean</code> | Dry upload the bundle process: add the row in database without uploading files or updating channels (Used by Capgo for internal testing) |
Expand Down Expand Up @@ -285,4 +285,3 @@ npx @capgo/cli@latest bundle zip com.example.app --path ./dist
| **--no-code-check** | <code>boolean</code> | Ignore checking if notifyAppReady() is called in source code and index present in root folder |
| **--key-v2** | <code>boolean</code> | Use encryption v2 |
| **--package-json** | <code>string</code> | Paths to package.json files for monorepos (comma-separated) |

Loading