guideUpgrade method
Walk the user through upgrading to Blaze.
Prints the consequence of staying on Spark, the upgrade URL, then
prompts the user. After they confirm completion the status is
re-checked. The loop runs at most maxLoops times so a misclick can
never hang the wizard.
Returns the final BillingCheckResult.
Implementation
Future<BillingCheckResult> guideUpgrade({
String? projectId,
int maxLoops = 3,
bool interactive = true,
}) async {
final String pid = projectId ?? this.projectId;
final String url = upgradeUrl(pid);
UserPrompt.printDivider(title: 'Upgrade to Blaze (pay-as-you-go)');
UserPrompt.printList(<String>[
'Spark plan covers: Hosting, Firestore (small), Auth, Storage (small).',
'Blaze plan is required for: Cloud Run, Artifact Registry cleanup,',
' scheduled jobs, callable functions, and most production workloads.',
'Open: $url',
'Sign in with an account that owns the project and link a billing',
' account. The upgrade is reversible — you can downgrade at any time.',
]);
print('');
if (!interactive) {
info('Non-interactive mode: skipping Blaze upgrade hand-off for $pid');
return BillingCheckResult(
status: BlazeStatus.unknown,
message:
'Run `oracular check billing` after upgrading to Blaze at $url',
);
}
for (int i = 0; i < maxLoops; i++) {
final bool ready = await UserPrompt.askYesNo(
i == 0
? 'Have you completed the Blaze upgrade in the browser?'
: 'Re-check billing now? (will run gcloud billing describe)',
defaultValue: i == 0,
);
if (!ready) {
warn('Skipping Blaze verification for $pid.');
return const BillingCheckResult(
status: BlazeStatus.unknown,
message: 'User skipped Blaze verification',
);
}
final BillingCheckResult check = await checkBlazeStatus(projectId: pid);
switch (check.status) {
case BlazeStatus.enabled:
success('Blaze plan confirmed for $pid.');
if (check.billingAccountName != null) {
info('Linked billing account: ${check.billingAccountName}');
}
return check;
case BlazeStatus.notEnabled:
warn(
'Project $pid is still on Spark. The upgrade may not have completed — refresh the Firebase Console and try again.',
);
continue;
case BlazeStatus.unknown:
warn(
'Could not verify billing for $pid: ${check.message ?? 'unknown error'}',
);
continue;
}
}
return BillingCheckResult(
status: BlazeStatus.unknown,
message:
'Could not confirm Blaze upgrade after $maxLoops attempts. Run `oracular check billing` later, or skip Blaze-only steps for now.',
);
}