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:
parent
f69c10d3a0
commit
89256f1e80
28 changed files with 666 additions and 47 deletions
|
|
@ -17,6 +17,7 @@ import {
|
|||
issuesQuery, latestDailyEnd, metricQuery, parseIssues, parseMetricRows, parsePerfPower, type PlayHealth, ymd, ymdString,
|
||||
} from './lib/health';
|
||||
import { done, log, printJson, warn } from './lib/log';
|
||||
import { plainError, storeReadError } from './lib/google-errors';
|
||||
import { formatFindings } from './lib/validate-rules';
|
||||
import { googleClient, REPORTING_API } from './play/google';
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ export async function collectPlayHealth(root: string, pkg: string, days: number)
|
|||
// the top issues are extra: a failure there must not hide the crash and ANR rates already read
|
||||
let issues: PlayHealth['issues'] = [], issuesError: string | undefined;
|
||||
try { issues = parseIssues(await g.json('GET', `${app}/errorIssues:search?${issuesQuery(end, days)}`)); } catch (e) {
|
||||
issuesError = (e as Error).message;
|
||||
issuesError = plainError((e as Error).message, { app: pkg });
|
||||
warn('health', `play: top issues not read: ${issuesError.slice(0, 200)}`);
|
||||
}
|
||||
const start = ymd(new Date(Date.UTC(end.year, end.month - 1, end.day)), days);
|
||||
|
|
@ -69,10 +70,10 @@ export async function collectHealth(ctx: Ctx, o: { apple: boolean; play: boolean
|
|||
const { config, root } = ctx.loaded;
|
||||
const h: HealthSnapshot = { date: new Date().toISOString().slice(0, 10) };
|
||||
if (o.play && config.play) {
|
||||
try { h.play = await collectPlayHealth(root, config.play.packageName, o.days); } catch (e) { h.play = { error: (e as Error).message }; }
|
||||
try { h.play = await collectPlayHealth(root, config.play.packageName, o.days); } catch (e) { h.play = storeReadError((e as Error).message, { app: config.play.packageName }); }
|
||||
}
|
||||
if (o.apple && config.apple) {
|
||||
try { h.apple = await collectAppleHealth(root, config.apple.bundleId, config.apple.appId, o.days); } catch (e) { h.apple = { error: (e as Error).message }; }
|
||||
try { h.apple = await collectAppleHealth(root, config.apple.bundleId, config.apple.appId, o.days); } catch (e) { h.apple = storeReadError((e as Error).message); }
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
|
@ -93,6 +94,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const h = await collectHealth(ctx, { apple: both || a.has('apple'), play: both || a.has('play'), days });
|
||||
if (a.has('json')) { printJson(h); return; }
|
||||
console.log(healthTable(h));
|
||||
for (const [s, x] of [['Google Play', h.play], ['App Store', h.apple]] as const) if (x && 'error' in x) console.log(`\n${s}: ${x.error}`);
|
||||
console.log('\n' + formatFindings(healthFindings(h)));
|
||||
if (weekly) {
|
||||
const dir = ctx.loaded.resolve(a.get('out') ?? '.storebound/health');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue