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
|
|
@ -2,6 +2,7 @@
|
|||
// storebound apple bundle [--name "My App"] [--dry]
|
||||
// The App Store Connect app record itself can NOT be created through the API: do that in the web UI afterwards
|
||||
// (Apps > + > New App, pick this bundle id), see the appstore-first-release skill.
|
||||
import { consoleLink } from '../lib/console-links';
|
||||
import type { Ctx } from '../lib/ctx';
|
||||
import { need } from '../lib/ctx';
|
||||
import { done, log } from '../lib/log';
|
||||
|
|
@ -25,6 +26,6 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
}
|
||||
const apps = (await c.asc('GET', `/v1/apps?filter[bundleId]=${encodeURIComponent(bundleId)}`)).data;
|
||||
if (apps.length) log('apple', `app record: ${apps[0].attributes.name} (Apple ID ${apps[0].id}); put it in apple.appId`);
|
||||
else log('apple', 'no app record yet: App Store Connect > Apps > + > New App (UI only), then re-run `apple status`');
|
||||
else log('apple', `no app record yet: ${consoleLink('asc.new-app')} (UI only), then re-run \`apple status\``);
|
||||
done('apple', 'bundle done');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
// with documents where required). This command lists the declarations and links the approved one to the builds; it
|
||||
// never creates a declaration or claims an exemption for you (export-compliance skill).
|
||||
// Not set: refuses. storebound never guesses this answer.
|
||||
import { consoleLink } from '../lib/console-links';
|
||||
import type { Ctx } from '../lib/ctx';
|
||||
import { need } from '../lib/ctx';
|
||||
import { done, log, warn } from '../lib/log';
|
||||
|
|
@ -39,7 +40,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
for (const d of decls) log('apple', `declaration ${d.id}: ${d.attributes.appEncryptionDeclarationState} exempt=${d.attributes.exempt} code=${d.attributes.codeValue ?? '-'}`);
|
||||
const approved = decls.find(d => d.attributes.appEncryptionDeclarationState === 'APPROVED');
|
||||
if (!approved) {
|
||||
warn('apple', 'no APPROVED encryption declaration: create it in App Store Connect > App Information > App Encryption Documentation (export-compliance skill), then re-run');
|
||||
warn('apple', `no APPROVED encryption declaration: create it in ${consoleLink('asc.encryption')} (export-compliance skill), then re-run`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
// screenshot sets, MD5-idempotent like `apple screenshots`. start / stop change what users see (stop is final, a test
|
||||
// cannot be restarted) and ask first; they are not MCP tools. Google Play store listing experiments have no API: they
|
||||
// stay in Play Console (store-screenshots skill). Pure planners: cli/lib/ppo.ts.
|
||||
import { consoleLink } from '../lib/console-links';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import type { Ctx } from '../lib/ctx';
|
||||
|
|
@ -66,7 +67,7 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
log(TAG, `"${x.name}" treatment "${t.attributes.name}": ${locs.length} locale(s), ${shots} screenshot(s)`);
|
||||
}
|
||||
}
|
||||
log(TAG, 'results (conversion per treatment) are in App Store Connect > Product Page Optimization; no API returns them');
|
||||
log(TAG, `results (conversion per treatment) are in ${consoleLink('asc.ppo')}; no API returns them`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
// exit codes: 0 approved, 1 rejected, 7 timeout (2 = nothing in review)
|
||||
// Also compares the App ID's capabilities, apple.signing.capabilities and the iOS project's entitlements and warns,
|
||||
// naming what is missing where.
|
||||
import { consoleLink } from '../lib/console-links';
|
||||
import type { Ctx } from '../lib/ctx';
|
||||
import { confirm, need } from '../lib/ctx';
|
||||
import { log, printJson, warn } from '../lib/log';
|
||||
|
|
@ -37,7 +38,7 @@ async function waitReview(ctx: Ctx, c: Asc, appId: string): Promise<never> {
|
|||
const outcome = classifyReviewState(st);
|
||||
if (st !== last) { log('apple', `${v.attributes.versionString}: ${st}`); last = st; }
|
||||
if (outcome === 'approved') { log('apple', 'approved (release it yourself, or it goes out automatically if the release type says so)'); process.exit(WAIT_EXIT.approved); }
|
||||
if (outcome === 'rejected') { log('apple', 'rejected: read the message in App Store Connect > App Review, then the review-rejections skill'); process.exit(WAIT_EXIT.rejected); }
|
||||
if (outcome === 'rejected') { log('apple', `rejected: read the message in ${consoleLink('asc.app-review')}, then the review-rejections skill`); process.exit(WAIT_EXIT.rejected); }
|
||||
if (outcome === 'not-submitted') { log('apple', 'nothing is waiting for review'); process.exit(2); }
|
||||
delay = nextPollDelay(delay);
|
||||
if (Date.now() + delay > deadline) { log('apple', 'timeout, still in review'); process.exit(WAIT_EXIT.timeout); }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue