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
|
|
@ -16,6 +16,7 @@
|
|||
// sha256_cert_fingerprints ["AB:CD:..."] }; https://<domain>/.well-known/assetlinks.json, content-type
|
||||
// application/json, HTTPS, no redirects; relation_extensions.dynamic_app_link_components on Android 15+)
|
||||
// https://developer.android.com/training/app-links/verify-android-applinks (autoVerify, per-host lookup)
|
||||
import { consoleLink, consoleUrl } from './console-links';
|
||||
import type { ShipConfig } from './config';
|
||||
|
||||
export const AASA_PATH = '/.well-known/apple-app-site-association';
|
||||
|
|
@ -25,7 +26,7 @@ export const HANDLE_ALL_URLS = 'delegate_permission/common.handle_all_urls';
|
|||
export const AASA_MAX_BYTES = 128 * 1024;
|
||||
|
||||
export type Level = 'ok' | 'problem' | 'info';
|
||||
export interface Finding { level: Level; message: string; fix?: string }
|
||||
export interface Finding { level: Level; message: string; fix?: string; url?: string }
|
||||
|
||||
type Cfg = Pick<ShipConfig, 'app' | 'apple' | 'play' | 'web'>;
|
||||
|
||||
|
|
@ -187,7 +188,7 @@ export function validateAssetLinks(json: unknown, ids: { packageName?: string; f
|
|||
f.push({ level: 'ok', message: `${ids.packageName} has ${HANDLE_ALL_URLS}` });
|
||||
const live = new Set(linked.flatMap(s => ((s.target as Record<string, unknown>).sha256_cert_fingerprints as string[] | undefined) ?? []).map(x => normalizeFingerprint(x) ?? x));
|
||||
const want = (ids.fingerprints ?? []).map(x => normalizeFingerprint(x) ?? x);
|
||||
if (!want.length) { f.push({ level: 'info', message: 'web.androidSha256 not in the config: fingerprints not compared', fix: 'copy the SHA-256 from Play Console > App integrity > Play app signing' }); return f; }
|
||||
if (!want.length) { f.push({ level: 'info', message: 'web.androidSha256 not in the config: fingerprints not compared', fix: `copy the SHA-256 from ${consoleLink('play.app-signing')}`, url: consoleUrl('play.app-signing') }); return f; }
|
||||
const missing = want.filter(x => !live.has(x)), extra = [...live].filter(x => !want.includes(x));
|
||||
for (const m of missing) bad(`fingerprint ${m.slice(0, 11)}... from web.androidSha256 is missing: app links do not verify for builds signed with it`);
|
||||
if (!missing.length) f.push({ level: 'ok', message: `all ${want.length} fingerprint(s) from web.androidSha256 are listed` });
|
||||
|
|
@ -277,10 +278,10 @@ export function planWeb(c: Cfg, o: { landing: boolean; template?: string; marks?
|
|||
const fps = o.fingerprints ?? c.web?.androidSha256 ?? [];
|
||||
const links = assetLinksFor(c, fps);
|
||||
if (links) files.push({ path: '.well-known/assetlinks.json', content: JSON.stringify(links, null, 2) + '\n' });
|
||||
else if (c.play) skipped.push('.well-known/assetlinks.json: no app signing fingerprint: copy the SHA-256 from Play Console > Test and release > App integrity > Play app signing into web.androidSha256, or run `storebound web init --version-code <uploaded versionCode>` to read it from the Play Developer API');
|
||||
else if (c.play) skipped.push('.well-known/assetlinks.json: no app signing fingerprint: copy the SHA-256 from ' + consoleLink('play.app-signing') + ' into web.androidSha256, or run `storebound web init --version-code <uploaded versionCode>` to read it from the Play Developer API');
|
||||
const tag = smartBannerTag(c);
|
||||
if (tag) files.push({ path: 'smart-app-banner.html', content: `<!-- Smart App Banner: paste this tag into the <head> of every page that should show it (Safari on iPhone and iPad). -->\n${tag}\n` });
|
||||
else if (c.apple) skipped.push('smart-app-banner.html: apple.appId is not set (App Store Connect > App Information > Apple ID)');
|
||||
else if (c.apple) skipped.push(`smart-app-banner.html: apple.appId is not set (${consoleLink('asc.app-info', '> Apple ID')})`);
|
||||
if (o.landing) {
|
||||
if (!o.template) skipped.push('index.html: landing template not found');
|
||||
else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue