feat(play): finalize store graphics and copy

This commit is contained in:
Yun Chan 2026-08-29 20:11:54 +09:00
parent 563d4cb1de
commit 0a8cf4396f
9 changed files with 316 additions and 92 deletions

View file

@ -235,12 +235,9 @@ function verifyFeatureGraphics(root) {
'feature_asset_directory'
)
const manifest = readJson(assetDirectory, 'manifest.json', 'feature_manifest')
assert(manifest.schemaVersion === 1, 'manifest_schema')
assert(manifest.schemaVersion === 2, 'manifest_schema')
assert(Array.isArray(manifest.candidates) && manifest.candidates.length === 4, 'candidate_count')
assert(
manifest.selection?.status === 'pending_user_selection_and_console_preview',
'selection_status'
)
assert(manifest.selection?.status === 'selected_console_preview_verified', 'selection_status')
const candidateIds = new Set()
for (const candidate of manifest.candidates) {
@ -272,8 +269,45 @@ function verifyFeatureGraphics(root) {
assert(png.colorType === manifest.export.pngColorType, `png_color_type_${candidate.id}`)
}
assert(candidateIds.has(manifest.selection.recommended), 'recommended_candidate_missing')
return manifest.candidates.length
const canonical = manifest.canonical
assert(canonical?.id === manifest.selection.recommended, 'canonical_selection')
assert(!candidateIds.has(canonical.id), 'canonical_id_conflict')
const canonicalSource = readRequired(assetDirectory, canonical.source, 'feature_canonical_source')
const canonicalPrompt = readRequired(assetDirectory, canonical.prompt, 'feature_canonical_prompt')
const canonicalOutput = readRequired(assetDirectory, canonical.output, 'feature_canonical_output')
assert(
statSync(canonicalSource.absolutePath).size === canonical.sourceBytes,
'feature_canonical_source_size'
)
assert(
statSync(canonicalPrompt.absolutePath).size === canonical.promptBytes,
'feature_canonical_prompt_size'
)
assert(
statSync(canonicalOutput.absolutePath).size === canonical.outputBytes,
'feature_canonical_output_size'
)
assert(sha256(canonicalSource.file) === canonical.sourceSha256, 'feature_canonical_source_hash')
assert(sha256(canonicalPrompt.file) === canonical.promptSha256, 'feature_canonical_prompt_hash')
verifyPng(
assetDirectory,
canonical.output,
{
sha256: canonical.outputSha256,
width: manifest.export.width,
height: manifest.export.height,
bitDepth: manifest.export.bitDepth,
colorType: manifest.export.pngColorType,
alpha: 'no_alpha_channel'
},
'feature_canonical'
)
return {
candidateCount: manifest.candidates.length,
canonicalId: canonical.id
}
}
function verifyAndroidIcons(root, android) {
@ -537,11 +571,11 @@ function verifyMobileIcons(root) {
}
function verifyAll(root, printSummary = true) {
const featureCandidateCount = verifyFeatureGraphics(root)
const feature = verifyFeatureGraphics(root)
const mobile = verifyMobileIcons(root)
if (printSummary) {
console.log(
`Play store assets verified: ${featureCandidateCount} feature candidates, canonical 512 icon, ${mobile.androidLegacyIconCount} Android legacy icons + adaptive/monochrome, ${mobile.iosIconCount} iOS/iPad icons, hashes and alpha contracts GREEN`
`Play store assets verified: ${feature.candidateCount} historical feature candidates + canonical ${feature.canonicalId}, canonical 512 icon, ${mobile.androidLegacyIconCount} Android legacy icons + adaptive/monochrome, ${mobile.iosIconCount} iOS/iPad icons, hashes and alpha contracts GREEN`
)
}
}
@ -695,7 +729,28 @@ function runSelfTests() {
'ios_icon_iphone_20x20_2x_color_type'
)
console.log('Play/mobile icon negative self-tests GREEN: 6 fail-closed cases')
expectSelfTestFailure(
'feature_canonical_hash',
(root) => {
const manifestPath = resolveInside(
root,
`${featureAssetDirectoryRelative}/manifest.json`,
'self_test_feature_manifest'
)
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
const path = resolveInside(
root,
`${featureAssetDirectoryRelative}/${manifest.canonical.output}`,
'self_test_feature_canonical'
)
const bytes = readFileSync(path)
bytes[bytes.length - 1] ^= 0xff
writeFileSync(path, bytes)
},
'feature_canonical_hash'
)
console.log('Play/mobile icon negative self-tests GREEN: 7 fail-closed cases')
}
const argumentsSet = new Set(process.argv.slice(2))