Console pages as absolute deep links: cli/lib/console-links.ts builds the URL of every App Store Connect / Play Console page from apple.appId, play.developerId and play.appId (app list / developer chooser when unknown); every plain 'App Store Connect > ...' / 'Play Console > ...' direction in cli/ and templates/ now carries it, validate / scan / declarations / release plan JSON carry url, the desktop shows an Open button; tests/console-links.test.ts guards it; AGENTS rule 11, pitfall 71
This commit is contained in:
parent
f898d642d0
commit
e500e88e40
50 changed files with 381 additions and 92 deletions
|
|
@ -1,6 +1,7 @@
|
|||
// Store readiness rules for `storebound validate`. Pure: the command collects a snapshot from both APIs (read-only),
|
||||
// these functions turn it into an ordered list of findings. Order = the order you would fix things in.
|
||||
|
||||
import { consoleLink, consoleUrl } from './console-links';
|
||||
import { type FixAction, fixAppleListing, fixPlayListing, fixReviewDetails } from './fix-actions';
|
||||
|
||||
export type Level = 'blocker' | 'warn' | 'ok';
|
||||
|
|
@ -16,6 +17,8 @@ export const ACCEPTED_INSTEAD: Record<string, string[]> = {
|
|||
APP_IPAD_PRO_129: ['APP_IPAD_PRO_3GEN_129'],
|
||||
};
|
||||
export interface Finding { store: 'apple' | 'play'; level: Level; code: string; msg: string; fix?: string;
|
||||
/** Absolute deep link to the console page the message points to (desktop / MCP render it as an Open link). */
|
||||
url?: string;
|
||||
/** The desktop's Fix button: a write fully determined by an existing command (lib/fix-actions.ts). */
|
||||
action?: FixAction }
|
||||
|
||||
|
|
@ -181,7 +184,7 @@ export function copyrightRules(s: Pick<AppleSnapshot, 'version' | 'configCopyrig
|
|||
|
||||
export function appleRules(s: AppleSnapshot): Finding[] {
|
||||
const f: Finding[] = [];
|
||||
const add = (level: Level, code: string, msg: string, fix?: string, action?: FixAction) => f.push({ store: 'apple', level, code, msg, fix, ...(action ? { action } : {}) });
|
||||
const add = (level: Level, code: string, msg: string, fix?: string, action?: FixAction, url?: string) => f.push({ store: 'apple', level, code, msg, fix, ...(action ? { action } : {}), ...(url ? { url } : {}) });
|
||||
const listing = s.version && EDITABLE.includes(s.version.state) ? fixAppleListing(s.version.versionString) : undefined;
|
||||
|
||||
if (!s.version) add('blocker', 'version', 'no App Store version being prepared', 'storebound apple listing --version x.y.z');
|
||||
|
|
@ -230,11 +233,11 @@ export function appleRules(s: AppleSnapshot): Finding[] {
|
|||
const { open, soft } = ageOpen(s.ageRating);
|
||||
if (open.length) add('blocker', 'age-rating', `age rating unanswered: ${open.join(', ')}`, 'storebound apple appinfo');
|
||||
else add('ok', 'age-rating', 'age rating answered');
|
||||
if (soft.length) add('warn', 'age-rating', `age rating question not answered yet: ${soft.join(', ')} (App Store Connect > App Information > Age Rating)`);
|
||||
if (soft.length) add('warn', 'age-rating', `age rating question not answered yet: ${soft.join(', ')} (${consoleLink('asc.age-rating')})`, undefined, undefined, consoleUrl('asc.age-rating'));
|
||||
}
|
||||
add(s.priceSchedule ? 'ok' : 'blocker', 'price', s.priceSchedule ? 'app price set' : 'no app price schedule', 'storebound apple appinfo');
|
||||
add(s.availability ? 'ok' : 'blocker', 'availability', s.availability ? 'availability set' : 'no territory availability',
|
||||
'storebound apple appinfo; if the API refuses the first availability record, set it once in App Store Connect > Pricing and Availability');
|
||||
'storebound apple appinfo; if the API refuses the first availability record, set it once in ' + consoleLink('asc.pricing'));
|
||||
|
||||
const iaps = new Map(s.iaps.map(i => [i.productId, i] as const));
|
||||
for (const id of s.expectedProducts) if (!iaps.has(id)) add('blocker', 'iap', `${id}: not on App Store Connect`, 'storebound apple iap');
|
||||
|
|
@ -380,7 +383,7 @@ export function appleSubscriptionRules(s: Pick<AppleSnapshot, 'subscriptions' |
|
|||
*/
|
||||
export function profileRules(store: 'apple' | 'play', p: ProfileFacts, o: { reviewDetail?: AppleSnapshot['reviewDetail']; descriptions?: { locale: string; text: string }[] }): Finding[] {
|
||||
const f: Finding[] = [];
|
||||
const add = (level: Level, code: string, msg: string, fix?: string) => f.push({ store, level, code, msg, fix });
|
||||
const add = (level: Level, code: string, msg: string, fix?: string, _action?: undefined, url?: string) => f.push({ store, level, code, msg, fix, ...(url ? { url } : {}) });
|
||||
if (p.accounts) {
|
||||
if (store === 'play') {
|
||||
if (!p.deletionUrl) add('blocker', 'account-deletion', 'the app has accounts but accounts.deletionUrl is not set (Play Data safety asks for a delete-account URL)', 'account-deletion skill');
|
||||
|
|
@ -393,7 +396,7 @@ export function profileRules(store: 'apple' | 'play', p: ProfileFacts, o: { revi
|
|||
if (!p.demoAccountLocal) add('blocker', 'demo-account', 'sign-in required for review but no demo account in .storebound/credentials.json (reviewDemoAccount) or REVIEW_DEMO_USER / REVIEW_DEMO_PASSWORD', 'review-access skill');
|
||||
if (store === 'apple' && o.reviewDetail && !(o.reviewDetail.demoAccountRequired && o.reviewDetail.demoAccountName))
|
||||
add('blocker', 'demo-account', 'App Review information has no demo account although the app needs a sign-in (Guideline 2.1)', 'storebound apple review-details');
|
||||
if (store === 'play') add('warn', 'app-access', 'sign-in required: Play Console > App content > App access must list the demo credentials and steps (no API)', 'review-access skill');
|
||||
if (store === 'play') add('warn', 'app-access', `sign-in required: ${consoleLink('play.app-access')} must list the demo credentials and steps (no API)`, 'review-access skill', undefined, consoleUrl('play.app-access'));
|
||||
if (!f.some(x => x.code === 'demo-account')) add('ok', 'demo-account', 'demo account for review present');
|
||||
}
|
||||
if (p.subscriptions) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue