diff --git a/scripts/ship-release.mjs b/scripts/ship-release.mjs index d6e257c..4e9dcb7 100644 --- a/scripts/ship-release.mjs +++ b/scripts/ship-release.mjs @@ -1,9 +1,12 @@ import { spawnSync } from 'node:child_process'; +import { mkdirSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; const qaVersion = process.env.PUBLIC_QA_VERSION ?? makeQaVersion(); runPnpmScript('verify:local-release'); runPnpmScript('qa:early'); +writeReleaseManifest(qaVersion); runPnpmScript('deploy:nas:dist'); runPnpmScript('verify:public-deploy', { PUBLIC_QA_VERSION: qaVersion }); @@ -14,6 +17,30 @@ function makeQaVersion() { return `ship-${stamp}`; } +function writeReleaseManifest(qaVersion) { + const manifest = { + app: 'heros_web', + qaVersion, + commit: gitValue(['rev-parse', 'HEAD'], 'unknown'), + shortCommit: gitValue(['rev-parse', '--short', 'HEAD'], 'unknown'), + branch: gitValue(['branch', '--show-current'], 'unknown'), + generatedAt: new Date().toISOString(), + verification: ['verify:local-release', 'qa:early', 'verify:public-deploy'] + }; + + mkdirSync('dist', { recursive: true }); + writeFileSync(join('dist', 'release-manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8'); + console.log(`Wrote dist/release-manifest.json for ${manifest.shortCommit} (${qaVersion})`); +} + +function gitValue(args, fallback) { + const result = spawnSync('git', args, { cwd: process.cwd(), encoding: 'utf8' }); + if (result.status !== 0) { + return fallback; + } + return result.stdout.trim() || fallback; +} + function runPnpmScript(scriptName, extraEnv = {}) { const label = `pnpm run ${scriptName}`; console.log(`\n$ ${label}`); diff --git a/scripts/verify-public-deploy.mjs b/scripts/verify-public-deploy.mjs index 786fca6..ca4d885 100644 --- a/scripts/verify-public-deploy.mjs +++ b/scripts/verify-public-deploy.mjs @@ -4,6 +4,7 @@ import { chromium } from 'playwright'; const targetUrl = withQaParams(process.env.PUBLIC_QA_URL ?? 'https://comtropy.synology.me/heros_web/'); const screenshotPath = process.env.PUBLIC_QA_SCREENSHOT ?? 'dist/public-deploy-qa.png'; +const expectedQaVersion = process.env.PUBLIC_QA_VERSION; const expectedTitle = '\uC0BC\uAD6D\uC9C0: \uC138 \uD615\uC81C\uC758 \uB9F9\uC138'; const relevantLogPattern = /asset|audio|cannot|failed|map|missing|portrait|sprite|story|texture|unit/i; @@ -99,6 +100,17 @@ try { assert(state.canvas.pixelProbe.nonTransparentRatio > 0.5, `Expected non-empty canvas pixels: ${JSON.stringify(state)}`); assert(state.canvas.pixelProbe.distinctColorBuckets >= 12, `Expected visually varied canvas pixels: ${JSON.stringify(state)}`); + const releaseManifest = await readReleaseManifest(targetUrl); + assert(releaseManifest.app === 'heros_web', `Expected deployed release manifest app id: ${JSON.stringify(releaseManifest)}`); + assert(typeof releaseManifest.commit === 'string' && releaseManifest.commit.length >= 7, `Expected deployed release manifest commit: ${JSON.stringify(releaseManifest)}`); + assert(typeof releaseManifest.generatedAt === 'string' && releaseManifest.generatedAt.length > 0, `Expected deployed release manifest timestamp: ${JSON.stringify(releaseManifest)}`); + if (expectedQaVersion) { + assert( + releaseManifest.qaVersion === expectedQaVersion, + `Expected deployed release manifest qaVersion ${expectedQaVersion}: ${JSON.stringify(releaseManifest)}` + ); + } + const relevantLogs = consoleMessages.filter( (message) => message.type === 'error' || (isWarning(message.type) && relevantLogPattern.test(message.text)) ); @@ -108,6 +120,7 @@ try { assert(blockingRequestFailures.length === 0, `Unexpected public deploy request failures: ${JSON.stringify(blockingRequestFailures, null, 2)}`); console.log(`Verified public deploy at ${targetUrl}`); + console.log(`Verified release manifest ${releaseManifest.shortCommit ?? releaseManifest.commit} (${releaseManifest.qaVersion ?? 'no qaVersion'})`); console.log(`Captured ${screenshotPath}`); } finally { await browser.close(); @@ -124,6 +137,14 @@ function isWarning(type) { return type === 'warning' || type === 'warn'; } +async function readReleaseManifest(pageUrl) { + const manifestUrl = new URL('release-manifest.json', pageUrl); + manifestUrl.searchParams.set('v', expectedQaVersion ?? String(Date.now())); + const response = await fetch(manifestUrl, { signal: AbortSignal.timeout(90000) }); + assert(response.ok, `Expected deployed release manifest at ${manifestUrl.href}: ${response.status} ${response.statusText}`); + return response.json(); +} + function assert(condition, message) { if (!condition) { throw new Error(message);