Google API errors as typed findings, never raw JSON: cli/lib/google-errors.ts parses SERVICE_DISABLED (api-disabled: service, Cloud project, console page), PERMISSION_DENIED (no-app-access: which app, Users and permissions), 401 / invalid_grant (auth-revoked) and 429 (quota); health / analytics print one line with the URL, raw text in detail and the log; desktop Health tab: the line, 'Turn it on in Google Cloud' and 'Check again', raw text in a collapsed Details; Portfolio shows a Cloud project blocking several apps once. Findings exit codes are results: the sidecar reports outcome (ok / findings / declined / failed) and the command's own error line; scan / check / validate / health exit 1 with findings show and cache the findings; no bare 'Exit code N' anywhere. tests/google-errors.test.ts, boot check errors cases (api-disabled, scan-findings, scan-failed) (pitfall 57)

This commit is contained in:
CRTkafa 2026-10-13 23:20:04 +08:00
parent f69c10d3a0
commit 89256f1e80
28 changed files with 666 additions and 47 deletions

View file

@ -2,6 +2,7 @@
// rollout %), then every version read from the stores, then sending for review (Submit.tsx): App Store submit /
// resubmit / in-app purchases and the Google Play production release as owner actions, the Play steps without an API
// as console plans. Releasing an approved version (manual release) stays in App Store Connect.
import { plainError } from '../../../../../cli/lib/google-errors';
import { useState } from 'react';
import type { ReleaseTrain, StoreRelease } from '../../api';
import type { UnifiedApp } from '../../lib/identity';
@ -155,7 +156,7 @@ export function ReleasesTab({ app }: { app: UnifiedApp }) {
const errs = Object.entries(l.storeErrors);
return (
<>
{errs.map(([s, msg]) => <Notice key={s} tone="warn">{t('releases.storeUnreadable', { store: storeName(s as 'ios'), error: msg })}</Notice>)}
{errs.map(([s, msg]) => <Notice key={s} tone="warn">{t('releases.storeUnreadable', { store: storeName(s as 'ios'), error: plainError(msg) })}</Notice>)}
{l.release.next && <Train title={t('tl.next')} train={l.release.next} tf={tf.data} tone="accent" app={app} />}
{l.release.current && <Train title={t('tl.live')} train={l.release.current} tf={tf.data} tone="ok" app={app} />}
{!l.release.next && !l.release.current && <Card pad={false}><Empty compact illo="unknown" title={t('releases.noVersions')}>{t('releases.noVersionsText')}</Empty></Card>}