Skip to content

Commit 74350bb

Browse files
riderxCopilot
andcommitted
fix: try to not quit onboarding when issue
Co-authored-by: Copilot <copilot@github.com>
1 parent 8f197c7 commit 74350bb

File tree

3 files changed

+86
-19
lines changed

3 files changed

+86
-19
lines changed

skills/usage/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ TanStack Intent skills should stay focused and under the validator line limit, s
2424

2525
### Project setup and diagnostics
2626

27-
- `init [apikey] [appId]`: guided first-time setup for Capgo in a Capacitor app. In interactive use, onboarding 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.
27+
- `init [apikey] [appId]`: guided first-time setup for Capgo in a Capacitor app. In interactive use, onboarding 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 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, and offer cancellation every third failed retry.
2828
- `login [apikey]`: store an API key locally.
2929
- `doctor`: inspect installation health and gather troubleshooting details.
3030
- `probe`: test whether the update endpoint would deliver an update.

src/init.ts

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -187,17 +187,70 @@ async function saveAppIdToCapacitorConfig(appId: string) {
187187
}
188188
}
189189

190-
function stopForBrokenIosSync(platformRunner: string, details: string[]): never {
190+
async function handleBrokenIosSync(platformRunner: string, details: string[], orgId: string, apikey: string, failureCount: number) {
191191
const resetAdvice = getNativeProjectResetAdvice(platformRunner, 'ios')
192192
pLog.error('Capgo iOS dependency sync verification failed.')
193193
for (const detail of details) {
194194
pLog.error(detail)
195195
}
196-
pLog.error('Stop here to avoid testing on a broken native iOS project.')
196+
pLog.error('The native iOS project is still broken, so this build step cannot continue yet.')
197197
pLog.warn(resetAdvice.summary)
198198
pLog.info(resetAdvice.command)
199-
pOutro('After reset, run the same `capgo init ...` command to resume onboarding from where you left off (no need to redo previous steps).')
200-
exit(1)
199+
200+
if (failureCount % 3 === 0) {
201+
const cancelInit = await pConfirm({
202+
message: `iOS sync has failed ${failureCount} times. Do you want to cancel init?`,
203+
initialValue: false,
204+
})
205+
await cancelCommand(cancelInit, orgId, apikey)
206+
if (cancelInit) {
207+
await markSnag('onboarding-v2', orgId, apikey, 'canceled', '🤷')
208+
pOutro('Bye 👋\n💡 You can resume the onboarding anytime by running the same command again')
209+
exit(1)
210+
}
211+
}
212+
213+
const runResetNow = await pConfirm({
214+
message: 'Would you like me to run this reset command for you now?',
215+
initialValue: true,
216+
})
217+
await cancelCommand(runResetNow, orgId, apikey)
218+
219+
if (runResetNow) {
220+
const resetSpinner = pSpinner()
221+
resetSpinner.start(`Running: ${resetAdvice.command}`)
222+
try {
223+
execSync(resetAdvice.command, execOption as ExecSyncOptions)
224+
resetSpinner.stop('iOS folder recreated and synced ✅')
225+
}
226+
catch (err) {
227+
resetSpinner.stop('iOS folder reset failed ❌')
228+
pLog.error(formatError(err))
229+
}
230+
return
231+
}
232+
233+
pLog.info('We will wait while you fix the iOS folder yourself.')
234+
pLog.info('When you are ready, type "ready" and I will retry this step.')
235+
236+
while (true) {
237+
const ready = await pText({
238+
message: 'Type "ready" when the iOS folder is fixed.',
239+
placeholder: 'ready',
240+
validate: (value) => {
241+
if (!value?.trim())
242+
return 'Type "ready" to retry.'
243+
if (value.trim().toLowerCase() !== 'ready')
244+
return 'Type "ready" to retry.'
245+
},
246+
})
247+
if (pIsCancel(ready)) {
248+
await cancelCommand(ready, orgId, apikey)
249+
}
250+
if ((ready as string).trim().toLowerCase() === 'ready') {
251+
return
252+
}
253+
}
201254
}
202255

203256
function validateAppId(value: string | undefined): string | undefined {
@@ -589,7 +642,7 @@ async function addUpdaterStep(orgId: string, apikey: string, appId: string) {
589642
const coreVersion = dependencies.get('@capacitor/core')
590643
if (!coreVersion) {
591644
s.stop('Error')
592-
pLog.warn(`Cannot find @capacitor/core in package.json, please run \`capgo init\` in a capacitor project`)
645+
pLog.warn(`Cannot find @capacitor/core in package.json, please run \`@capgo/cli init\` in a capacitor project`)
593646
pOutro(`Bye 👋\n💡 You can resume the onboarding anytime by running the same command again`)
594647
exit()
595648
}
@@ -626,7 +679,7 @@ async function addUpdaterStep(orgId: string, apikey: string, appId: string) {
626679
}
627680
if (pm.pm === 'unknown') {
628681
s.stop('Error')
629-
pLog.warn(`Cannot recognize package manager, please run \`capgo init\` in a capacitor project with npm, pnpm, bun or yarn`)
682+
pLog.warn(`Cannot recognize package manager, please run \`@capgo/cli init\` in a capacitor project with npm, pnpm, bun or yarn`)
630683
pOutro(`Bye 👋\n💡 You can resume the onboarding anytime by running the same command again`)
631684
exit()
632685
}
@@ -859,14 +912,13 @@ async function buildProjectStep(orgId: string, apikey: string, appId: string, pl
859912
const doBuild = await pConfirm({ message: `Automatic build ${appId} with "${pm.pm} run build" ?` })
860913
await cancelCommand(doBuild, orgId, apikey)
861914
if (doBuild) {
862-
const s = pSpinner()
863-
s.start(`Checking project type`)
864915
const projectType = await findProjectType()
865916
const buildCommand = await findBuildCommandForProjectType(projectType)
866-
s.message(`Running: ${pm.pm} run ${buildCommand} && ${pm.runner} cap sync ${platform}`)
867917
const packScripts = getPackageScripts()
868918
// check in script build exist
869919
if (!packScripts[buildCommand]) {
920+
const s = pSpinner()
921+
s.start(`Checking project type`)
870922
s.stop('Missing build script')
871923
pLog.warn(`❌ Cannot find "${buildCommand}" script in package.json`)
872924
pLog.info(`💡 Your package.json needs a "${buildCommand}" script to build the app`)
@@ -886,17 +938,30 @@ async function buildProjectStep(orgId: string, apikey: string, appId: string, pl
886938
pOutro(`Bye 👋\n💡 Add a "${buildCommand}" script to package.json and run the command again`)
887939
exit()
888940
}
889-
execSync(`${pm.pm} run ${buildCommand} && ${pm.runner} cap sync ${platform}`, execOption as ExecSyncOptions)
890941

891-
if (platform === 'ios') {
892-
const syncValidation = validateIosUpdaterSync(cwd(), globalPathToPackageJson)
893-
if (syncValidation.shouldCheck && !syncValidation.valid) {
894-
s.stop('iOS sync check failed ❌')
895-
stopForBrokenIosSync(pm.runner, syncValidation.details)
942+
const buildAndSyncCommand = `${pm.pm} run ${buildCommand} && ${pm.runner} cap sync ${platform}`
943+
let iosSyncFailureCount = 0
944+
945+
while (true) {
946+
const s = pSpinner()
947+
s.start('Checking project type')
948+
s.message(`Running: ${buildAndSyncCommand}`)
949+
execSync(buildAndSyncCommand, execOption as ExecSyncOptions)
950+
951+
if (platform === 'ios') {
952+
const syncValidation = validateIosUpdaterSync(cwd(), globalPathToPackageJson)
953+
if (syncValidation.shouldCheck && !syncValidation.valid) {
954+
iosSyncFailureCount += 1
955+
s.stop('iOS sync check failed ❌')
956+
await handleBrokenIosSync(pm.runner, syncValidation.details, orgId, apikey, iosSyncFailureCount)
957+
pLog.info(`Retrying build and sync for iOS (attempt ${iosSyncFailureCount + 1})`)
958+
continue
959+
}
896960
}
897-
}
898961

899-
s.stop(`Build & Sync Done ✅`)
962+
s.stop('Build & Sync Done ✅')
963+
break
964+
}
900965
}
901966
else {
902967
pLog.info(`Build yourself with command: ${pm.pm} run build && ${pm.runner} cap sync ${platform}`)
@@ -1439,7 +1504,7 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
14391504
const pm = getPMAndCommand()
14401505
pIntro(`Capgo onboarding 🛫`)
14411506
pLog.info(`📖 See the complete onboarding guide: https://capgo.app/docs/getting-started/onboarding/`)
1442-
pLog.info(`⏱️ Estimated time: 10-20 minutes`)
1507+
pLog.info(`⏱️ Estimated time: 2-10 minutes`)
14431508
await warnIfNotInCapacitorRoot()
14441509
await checkAlerts()
14451510

webdocs/init.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ npx @capgo/cli@latest init
1717
This includes adding code for updates, building, uploading your app, and verifying update functionality.
1818
Capgo bundles are web assets and can be fetched by anyone who knows the URL. Use encryption for banking, regulated, or other high-security apps.
1919

20+
If iOS sync validation fails during onboarding, the CLI can offer to run a one-line reset command for the native iOS folder, wait for you to type `ready` after a manual fix, and offer cancellation every third failed retry.
21+
2022
At the end of onboarding, the CLI can also offer to install the Capgo agent skills package from GitHub using the Skills CLI before showing the GitHub support prompt.
2123

2224
**Example:**

0 commit comments

Comments
 (0)