play data-safety: write the conditional required rows (PSL_ACCOUNT_DELETION_URL from accounts.deletionUrl, PSL_SUPPORT_DATA_DELETION_BY_USER from dataSafety.partialDeletion, PSL_DATA_DELETION_URL); a rule table checks every conditional row in check, declarations and --dry before Play answers 400

This commit is contained in:
CRTkafa 2026-10-12 08:25:14 +08:00
parent c9a7f095c4
commit 97ab76e9c8
14 changed files with 295 additions and 24 deletions

View file

@ -1,6 +1,7 @@
// Play Data safety form through applications.dataSafety (CSV import; replaces every earlier answer).
// storebound play data-safety [--csv out.csv] [--dry] [--force]
// Answers come from `dataSafety` in storebound.config.ts. --dry only writes / prints the CSV.
// Answers come from `dataSafety` (and accounts.deletionUrl) in storebound.config.ts. --dry only writes / prints the CSV.
// Both --dry and a real run stop on a missing required row (dataSafetyIssues) before anything is sent.
// Play cannot read the form back, so a run compares with what this project uploaded last: the sha256 of that CSV
// (per package) is kept in .storebound/data-safety.sha256 (gitignored). The same CSV is not sent again; --force sends it
// anyway (e.g. after the form was changed in Play Console by hand).
@ -8,7 +9,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import type { Ctx } from '../lib/ctx';
import { need } from '../lib/ctx';
import { dataSafetyCsv } from '../lib/data-safety';
import { dataSafetyCsv, dataSafetyProblem, dataSafetyRows } from '../lib/data-safety';
import { DATA_SAFETY_UPLOADED } from '../lib/declarations';
import { done, log } from '../lib/log';
import { playClient } from './client';
@ -36,7 +37,9 @@ function rememberUpload(root: string, pkg: string, sha256: string): void {
export async function run(ctx: Ctx): Promise<void> {
const { config, root, resolve } = ctx.loaded;
const csv = dataSafetyCsv(need(config.dataSafety, 'dataSafety'));
const ds = need(config.dataSafety, 'dataSafety');
const csv = dataSafetyCsv(ds, config.accounts);
const problem = dataSafetyProblem(dataSafetyRows(ds, config.accounts));
const out = ctx.args.get('csv');
if (out) { writeFileSync(resolve(out), csv); log('play', `wrote ${out}`); }
const types = Object.keys(config.dataSafety!.data);
@ -47,9 +50,11 @@ export async function run(ctx: Ctx): Promise<void> {
const same = last?.sha256 === hash;
if (ctx.args.has('dry')) {
if (!out) process.stdout.write(csv);
if (problem) throw new Error(problem);
log('play', same ? `the same CSV was uploaded ${last!.at.slice(0, 16).replace('T', ' ')} UTC: a real run sends nothing (--force sends it again)` : 'a real run uploads it (it differs from the last upload, or none is recorded)');
return;
}
if (problem) throw new Error(problem);
if (same && !ctx.args.has('force')) {
done('play', `data safety unchanged since the upload of ${last!.at.slice(0, 16).replace('T', ' ')} UTC: nothing sent (--force sends it again, e.g. after a change in Play Console)`);
return;