apple submit --build <n> (first submission, the resubmit steps; plan table with --json under serve); desktop owner actions to send for review: owner.apple_submit, owner.apple_resubmit, owner.apple_iap_submit, owner.play_production, and the price gate owner actions (owner.prices_play_products / _play_subscriptions / _apple_subscriptions; owner.prices_import passes --allow-price-changes after the dialog); OWNER_ALLOWED_ARGS ties each forbidden flag to its one action, dry runs never carry owner flags, MCP still has no submit or production release; submit dialog text in the host; system.tools androidSdk, project.inspect playRelease, declarations dataDeletion
This commit is contained in:
parent
2e7dd44ae6
commit
33eb24a3dc
17 changed files with 335 additions and 49 deletions
|
|
@ -6,7 +6,7 @@
|
|||
// config does not prove is an "ask the owner" item, never a guess (AGENTS.md rule 5). Pure.
|
||||
import { parseCsv } from './csv';
|
||||
import { type AccountSignUp, type DataSafety, type PlayPurpose, profileOf, type ShipConfig } from './config';
|
||||
import { CATEGORIES, dataSafetyCsv, dataSafetyIssues, dataSafetyRows } from './data-safety';
|
||||
import { CATEGORIES, DATA_DELETION_RESPONSE, dataDeletionResponse, dataSafetyCsv, dataSafetyIssues, dataSafetyRows } from './data-safety';
|
||||
import type { DeclarationsResult, DeclAsk } from './desktop-protocol';
|
||||
import { NOTES_MAX_BYTES, reviewNotes } from './review-details';
|
||||
|
||||
|
|
@ -112,6 +112,16 @@ export interface DeclarationInputs {
|
|||
uploaded: { csv: string; at: string } | null;
|
||||
}
|
||||
|
||||
/** The general data deletion question (PSL_SUPPORT_DATA_DELETION_BY_USER) as the CSV answers it, where the answer
|
||||
* comes from and the "Delete data URL" it sends. Pure. */
|
||||
export function deletionAnswer(ds: DataSafety, deletionUrl?: string): { dataDeletion: 'yes' | 'no' | 'auto90' | null; dataDeletionSource: string | null; dataDeletionUrl: string | null } {
|
||||
const r = dataDeletionResponse(ds, deletionUrl ? { deletionUrl } : undefined);
|
||||
const dataDeletion = (Object.entries(DATA_DELETION_RESPONSE).find(([, v]) => v === r)?.[0] ?? null) as 'yes' | 'no' | 'auto90' | null;
|
||||
const dataDeletionSource = ds.dataDeletion ? 'dataSafety.dataDeletion' : deletionUrl ? 'accounts.deletionUrl' : ds.deletionRequest !== undefined ? 'dataSafety.deletionRequest'
|
||||
: ds.partialDeletion === true || ds.partialDeletion === 'auto90' ? 'dataSafety.partialDeletion' : null;
|
||||
return { dataDeletion, dataDeletionSource, dataDeletionUrl: dataDeletion === 'yes' ? ds.partialDeletionUrl ?? deletionUrl ?? null : null };
|
||||
}
|
||||
|
||||
export function declarations(config: ShipConfig, inp: DeclarationInputs): DeclarationsResult {
|
||||
const ask: DeclAsk[] = [];
|
||||
const play = !!config.play, apple = !!config.apple;
|
||||
|
|
@ -128,6 +138,7 @@ export function declarations(config: ShipConfig, inp: DeclarationInputs): Declar
|
|||
const types = Object.entries(ds.data).map(([id, u]) => ({ id, category: categoryOf(id), shared: !!u.shared, ephemeral: !!u.ephemeral, optional: !!u.optional, collect: [...u.collect], share: [...(u.shared ? u.share ?? [] : [])] }));
|
||||
dataSafety = {
|
||||
collectsData: ds.collectsData, encryptedInTransit: ds.encryptedInTransit, partialDeletion: ds.partialDeletion ?? null, accountCreation: ds.accountCreation ?? ['PSL_ACM_NONE'],
|
||||
...deletionAnswer(ds, config.accounts?.deletionUrl),
|
||||
types, extra: ds.extra?.length ?? 0, csv, rows: csv ? csv.trim().split('\n').length - 1 : 0,
|
||||
uploaded: inp.uploaded && csv ? { at: inp.uploaded.at, changes: dataSafetyChanges(inp.uploaded.csv, csv) } : null,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue