@mpgd/cli 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/game-acceptance.d.ts +65 -0
- package/dist/game-acceptance.js +245 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +469 -2
- package/dist/production-target-readiness.d.ts +8 -0
- package/dist/production-target-readiness.js +207 -0
- package/package.json +12 -12
- package/templates/phaser-game/README.md +71 -1
- package/templates/phaser-game/agent/acceptance.md +14 -6
- package/templates/phaser-game/agent/brief.md +3 -1
- package/templates/phaser-game/agent/game-manifest.json +11 -0
- package/templates/phaser-game/apps/target-devvit/README.md +30 -0
- package/templates/phaser-game/apps/target-devvit/devvit.json +7 -0
- package/templates/phaser-game/apps/target-devvit/package.json +8 -5
- package/templates/phaser-game/apps/target-devvit/src/server/index.ts +46 -1
- package/templates/phaser-game/apps/target-devvit/src/server/postOperationStore.ts +9 -0
- package/templates/phaser-game/apps/target-devvit/vite.server.config.ts +2 -1
- package/templates/phaser-game/game.html +13 -0
- package/templates/phaser-game/gitignore +2 -0
- package/templates/phaser-game/index.html +2 -2
- package/templates/phaser-game/mpgd.game.json +8 -0
- package/templates/phaser-game/mpgd.targets.json +6 -0
- package/templates/phaser-game/package.json +7 -1
- package/templates/phaser-game/public/manifest.webmanifest +1 -0
- package/templates/phaser-game/src/entry.ts +7 -0
- package/templates/phaser-game/src/env.d.ts +4 -0
- package/templates/phaser-game/src/gameEntry.ts +3 -0
- package/templates/phaser-game/src/main.ts +48 -3
- package/templates/phaser-game/src/platform/buildGatewayModule.ts +5 -0
- package/templates/phaser-game/src/platform/buildGateways/ait.ts +11 -0
- package/templates/phaser-game/src/platform/buildGateways/aitSandbox.ts +12 -0
- package/templates/phaser-game/src/platform/buildGateways/browser.ts +8 -0
- package/templates/phaser-game/src/platform/buildGateways/capacitorAndroid.ts +12 -0
- package/templates/phaser-game/src/platform/buildGateways/capacitorIos.ts +12 -0
- package/templates/phaser-game/src/platform/buildGateways/reddit.ts +11 -0
- package/templates/phaser-game/src/platform/buildGateways/redditSandbox.ts +15 -0
- package/templates/phaser-game/src/platform/devvitEntrypoint.ts +57 -0
- package/templates/phaser-game/src/platform/devvitInlinePreview.css +75 -0
- package/templates/phaser-game/src/platform/gameServices.ts +18 -61
- package/templates/phaser-game/src/platform/installPlatform.ts +6 -50
- package/templates/phaser-game/src/platform/microsoftStorePwa.ts +99 -0
- package/templates/phaser-game/src/runtime/gameContext.ts +3 -1
- package/templates/phaser-game/tools/run-game-acceptance.mjs +22 -0
- package/templates/phaser-game/vite.config.ts +161 -2
package/dist/index.js
CHANGED
|
@@ -6,6 +6,9 @@ import completion from '@gunshi/plugin-completion';
|
|
|
6
6
|
import i18n, { defineI18n } from '@gunshi/plugin-i18n';
|
|
7
7
|
import resources from '@gunshi/resources';
|
|
8
8
|
import { cli } from 'gunshi';
|
|
9
|
+
import { defaultGameAcceptanceCommandTimeoutMs, resolveGameAcceptanceReleaseManifestFile, runGameAcceptance, } from './game-acceptance.js';
|
|
10
|
+
export { renderGameAcceptanceMarkdown, resolveGameAcceptanceReleaseManifestFile, runGameAcceptance, defaultGameAcceptanceCommandTimeoutMs, } from './game-acceptance.js';
|
|
11
|
+
export { assertProductionTargetReadiness, } from './production-target-readiness.js';
|
|
9
12
|
const sourceDir = path.dirname(fileURLToPath(import.meta.url));
|
|
10
13
|
const packageRoot = resolvePackageRoot(sourceDir);
|
|
11
14
|
const detectedKitRoot = resolveDefaultKitRoot();
|
|
@@ -50,6 +53,15 @@ const supportedBuildTargets = [
|
|
|
50
53
|
'reddit',
|
|
51
54
|
];
|
|
52
55
|
const supportedVariants = ['wrapper', 'sync', 'simulator', 'archive'];
|
|
56
|
+
const acceptanceStepIds = {
|
|
57
|
+
check: 'check',
|
|
58
|
+
test: 'test',
|
|
59
|
+
build: 'build',
|
|
60
|
+
graph: 'graph-preflight',
|
|
61
|
+
playtest: 'playtest',
|
|
62
|
+
targetBuild: 'target-build',
|
|
63
|
+
targetSmoke: 'target-smoke',
|
|
64
|
+
};
|
|
53
65
|
export async function runMpgdCli(args) {
|
|
54
66
|
await cli([...args], entryCommand, {
|
|
55
67
|
name: 'mpgd',
|
|
@@ -213,15 +225,402 @@ const gameCommand = defineI18n({
|
|
|
213
225
|
});
|
|
214
226
|
},
|
|
215
227
|
}),
|
|
228
|
+
accept: defineI18n({
|
|
229
|
+
name: 'accept',
|
|
230
|
+
description: 'Run the reusable game handoff acceptance workflow.',
|
|
231
|
+
resource: commandResource({
|
|
232
|
+
en: 'Run check, test, build, graph, playtest, and target acceptance with handoff reports.',
|
|
233
|
+
ko: 'check, test, build, graph, playtest, 타깃 인수 검증과 인계 리포트를 실행합니다.',
|
|
234
|
+
}, {
|
|
235
|
+
game: {
|
|
236
|
+
en: 'Game root containing package.json and mpgd.targets.json.',
|
|
237
|
+
ko: 'package.json과 mpgd.targets.json이 있는 게임 루트.',
|
|
238
|
+
},
|
|
239
|
+
targets: {
|
|
240
|
+
en: 'Comma-separated target list, default, or all.',
|
|
241
|
+
ko: '쉼표로 구분한 타깃 목록, default 또는 all.',
|
|
242
|
+
},
|
|
243
|
+
profile: {
|
|
244
|
+
en: 'Target build profile. Defaults to staging.',
|
|
245
|
+
ko: '타깃 빌드 프로필. 기본값은 staging.',
|
|
246
|
+
},
|
|
247
|
+
'playtest-script': {
|
|
248
|
+
en: 'Optional game-owned package script used for automated playtesting.',
|
|
249
|
+
ko: '자동 플레이테스트에 사용할 선택적 게임 소유 패키지 스크립트.',
|
|
250
|
+
},
|
|
251
|
+
'report-dir': {
|
|
252
|
+
en: 'JSON and Markdown handoff report directory.',
|
|
253
|
+
ko: 'JSON 및 Markdown 인계 리포트 디렉터리.',
|
|
254
|
+
},
|
|
255
|
+
'timeout-ms': {
|
|
256
|
+
en: 'Maximum duration for each acceptance command.',
|
|
257
|
+
ko: '각 인수 검증 명령의 최대 실행 시간.',
|
|
258
|
+
},
|
|
259
|
+
'kit-path': {
|
|
260
|
+
en: 'Path to the mpgd-kit checkout.',
|
|
261
|
+
ko: 'mpgd-kit 체크아웃 경로.',
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
args: {
|
|
265
|
+
game: {
|
|
266
|
+
type: 'positional',
|
|
267
|
+
required: true,
|
|
268
|
+
description: 'Game root containing package.json and mpgd.targets.json.',
|
|
269
|
+
},
|
|
270
|
+
targets: {
|
|
271
|
+
type: 'string',
|
|
272
|
+
required: false,
|
|
273
|
+
default: 'default',
|
|
274
|
+
description: 'Comma-separated target list, default, or all.',
|
|
275
|
+
},
|
|
276
|
+
profile: {
|
|
277
|
+
type: 'string',
|
|
278
|
+
required: false,
|
|
279
|
+
default: 'staging',
|
|
280
|
+
description: 'Target build profile.',
|
|
281
|
+
},
|
|
282
|
+
'ait-variant': {
|
|
283
|
+
type: 'enum',
|
|
284
|
+
choices: supportedVariants,
|
|
285
|
+
required: false,
|
|
286
|
+
default: 'wrapper',
|
|
287
|
+
description: 'Variant for the ait target.',
|
|
288
|
+
},
|
|
289
|
+
'ios-variant': {
|
|
290
|
+
type: 'enum',
|
|
291
|
+
choices: supportedVariants,
|
|
292
|
+
required: false,
|
|
293
|
+
description: 'Variant for the ios target.',
|
|
294
|
+
},
|
|
295
|
+
'playtest-script': {
|
|
296
|
+
type: 'string',
|
|
297
|
+
required: false,
|
|
298
|
+
default: 'playtest',
|
|
299
|
+
description: 'Game-owned package script used for automated playtesting.',
|
|
300
|
+
},
|
|
301
|
+
'report-dir': {
|
|
302
|
+
type: 'string',
|
|
303
|
+
required: false,
|
|
304
|
+
description: 'JSON and Markdown handoff report directory.',
|
|
305
|
+
},
|
|
306
|
+
'timeout-ms': {
|
|
307
|
+
type: 'string',
|
|
308
|
+
required: false,
|
|
309
|
+
default: String(defaultGameAcceptanceCommandTimeoutMs),
|
|
310
|
+
description: 'Maximum duration for each acceptance command in milliseconds.',
|
|
311
|
+
},
|
|
312
|
+
'kit-path': {
|
|
313
|
+
type: 'string',
|
|
314
|
+
required: false,
|
|
315
|
+
description: 'Path to the mpgd-kit checkout.',
|
|
316
|
+
},
|
|
317
|
+
'skip-test': {
|
|
318
|
+
type: 'boolean',
|
|
319
|
+
required: false,
|
|
320
|
+
description: 'Skip the game-owned test package script.',
|
|
321
|
+
},
|
|
322
|
+
'skip-graph': {
|
|
323
|
+
type: 'boolean',
|
|
324
|
+
required: false,
|
|
325
|
+
description: 'Skip the kit graph preflight.',
|
|
326
|
+
},
|
|
327
|
+
'skip-playtest': {
|
|
328
|
+
type: 'boolean',
|
|
329
|
+
required: false,
|
|
330
|
+
description: 'Skip the game-owned playtest package script.',
|
|
331
|
+
},
|
|
332
|
+
'skip-target-build': {
|
|
333
|
+
type: 'boolean',
|
|
334
|
+
required: false,
|
|
335
|
+
description: 'Skip the target build matrix.',
|
|
336
|
+
},
|
|
337
|
+
'skip-target-smoke': {
|
|
338
|
+
type: 'boolean',
|
|
339
|
+
required: false,
|
|
340
|
+
description: 'Skip the target smoke matrix.',
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
run: (ctx) => {
|
|
344
|
+
const positionals = readLocalPositionals(ctx.positionals, ['game', 'accept']);
|
|
345
|
+
const gameRoot = path.resolve(readRequiredPositional(positionals, 0, 'game'));
|
|
346
|
+
const iosVariant = readOptionalString(ctx.values['ios-variant']);
|
|
347
|
+
const reportDir = readOptionalString(ctx.values['report-dir']);
|
|
348
|
+
const kitPath = readOptionalString(ctx.values['kit-path']);
|
|
349
|
+
const timeoutMs = readPositiveInteger(readOptionalString(ctx.values['timeout-ms'])
|
|
350
|
+
?? String(defaultGameAcceptanceCommandTimeoutMs), '--timeout-ms');
|
|
351
|
+
acceptGame({
|
|
352
|
+
gameRoot,
|
|
353
|
+
targets: readOptionalString(ctx.values.targets) ?? 'default',
|
|
354
|
+
profile: readOptionalString(ctx.values.profile) ?? 'staging',
|
|
355
|
+
aitVariant: readOptionalString(ctx.values['ait-variant']) ?? 'wrapper',
|
|
356
|
+
...(iosVariant === undefined ? {} : { iosVariant }),
|
|
357
|
+
playtestScript: readOptionalString(ctx.values['playtest-script']) ?? 'playtest',
|
|
358
|
+
...(reportDir === undefined ? {} : { reportDir }),
|
|
359
|
+
...(kitPath === undefined ? {} : { kitPath }),
|
|
360
|
+
timeoutMs,
|
|
361
|
+
skipTest: ctx.values['skip-test'] === true,
|
|
362
|
+
skipGraph: ctx.values['skip-graph'] === true,
|
|
363
|
+
skipPlaytest: ctx.values['skip-playtest'] === true,
|
|
364
|
+
skipTargetBuild: ctx.values['skip-target-build'] === true,
|
|
365
|
+
skipTargetSmoke: ctx.values['skip-target-smoke'] === true,
|
|
366
|
+
});
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
icons: defineI18n({
|
|
370
|
+
name: 'icons',
|
|
371
|
+
description: 'Generate and verify target app icons from the game brand source.',
|
|
372
|
+
resource: commandResource({
|
|
373
|
+
en: 'Generate and verify target app icons from the game brand source.',
|
|
374
|
+
ko: '게임 브랜드 원본에서 타깃별 앱 아이콘을 생성하고 검증합니다.',
|
|
375
|
+
}),
|
|
376
|
+
subCommands: Object.fromEntries(['generate', 'verify', 'inspect'].map((action) => [
|
|
377
|
+
action,
|
|
378
|
+
defineI18n({
|
|
379
|
+
name: action,
|
|
380
|
+
description: `${action} target app icon assets.`,
|
|
381
|
+
resource: commandResource({
|
|
382
|
+
en: `${action} target app icon assets.`,
|
|
383
|
+
ko: `타깃 앱 아이콘 자산을 ${action}합니다.`,
|
|
384
|
+
}),
|
|
385
|
+
args: {
|
|
386
|
+
game: {
|
|
387
|
+
type: 'positional',
|
|
388
|
+
required: true,
|
|
389
|
+
description: 'Game root containing mpgd.game.json and mpgd.targets.json.',
|
|
390
|
+
},
|
|
391
|
+
targets: {
|
|
392
|
+
type: 'string',
|
|
393
|
+
required: false,
|
|
394
|
+
description: 'Comma-separated target names. Defaults to every configured target.',
|
|
395
|
+
},
|
|
396
|
+
profile: {
|
|
397
|
+
type: 'string',
|
|
398
|
+
required: false,
|
|
399
|
+
default: action === 'verify' ? 'production' : 'development',
|
|
400
|
+
description: 'Validation profile (development or production).',
|
|
401
|
+
},
|
|
402
|
+
'kit-path': {
|
|
403
|
+
type: 'string',
|
|
404
|
+
required: false,
|
|
405
|
+
description: 'Path to the mpgd-kit checkout.',
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
run: (ctx) => {
|
|
409
|
+
const positionals = readLocalPositionals(ctx.positionals, ['game', 'icons', action]);
|
|
410
|
+
const gameRoot = path.resolve(readRequiredPositional(positionals, 0, 'game'));
|
|
411
|
+
const env = createTargetCommandEnv({
|
|
412
|
+
'targets-file': path.join(gameRoot, 'mpgd.targets.json'),
|
|
413
|
+
'kit-path': ctx.values['kit-path'],
|
|
414
|
+
});
|
|
415
|
+
const targets = readOptionalString(ctx.values.targets) ?? '';
|
|
416
|
+
const profile = readOptionalString(ctx.values.profile)
|
|
417
|
+
?? (action === 'verify' ? 'production' : 'development');
|
|
418
|
+
runPnpm([`game:icons:${action}`, '--', targets, profile], env);
|
|
419
|
+
},
|
|
420
|
+
}),
|
|
421
|
+
])),
|
|
422
|
+
run: () => {
|
|
423
|
+
console.log('Use "mpgd game icons generate <game>", verify, or inspect.');
|
|
424
|
+
},
|
|
425
|
+
}),
|
|
216
426
|
},
|
|
217
427
|
run: () => {
|
|
218
|
-
console.log('Use "mpgd game create <directory>".');
|
|
428
|
+
console.log('Use "mpgd game create <directory>", "mpgd game accept <game>", or "mpgd game icons generate <game>".');
|
|
219
429
|
},
|
|
220
430
|
});
|
|
221
431
|
const defaultLegalDir = 'legal';
|
|
222
432
|
const defaultLegalOutDir = 'artifacts/legal-site';
|
|
223
433
|
const legalPageSlugs = ['privacy', 'support', 'terms'];
|
|
224
434
|
const legalPageFileList = legalPageSlugs.map((slug) => `${slug}.html`).join(', ');
|
|
435
|
+
function acceptGame(input) {
|
|
436
|
+
if (input.skipTargetBuild && !input.skipTargetSmoke) {
|
|
437
|
+
throw new Error('Use --skip-target-smoke when --skip-target-build is set.');
|
|
438
|
+
}
|
|
439
|
+
const gameRoot = assertGameAcceptanceRoot(input.gameRoot);
|
|
440
|
+
const gamePackage = readPackageJsonOrUndefined(gameRoot, { strictParse: true });
|
|
441
|
+
if (gamePackage === undefined) {
|
|
442
|
+
throw new Error(`Missing game package.json: ${gameRoot}`);
|
|
443
|
+
}
|
|
444
|
+
const targetsFile = path.join(gameRoot, 'mpgd.targets.json');
|
|
445
|
+
if (!existsSync(targetsFile)) {
|
|
446
|
+
throw new Error(`Missing game target config: ${targetsFile}`);
|
|
447
|
+
}
|
|
448
|
+
const kitPath = resolveKitPathForTarget({
|
|
449
|
+
...(input.kitPath === undefined ? {} : { 'kit-path': input.kitPath }),
|
|
450
|
+
});
|
|
451
|
+
const packageManager = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
|
|
452
|
+
const steps = [
|
|
453
|
+
packageScriptAcceptanceStep({
|
|
454
|
+
id: acceptanceStepIds.check,
|
|
455
|
+
label: 'Game check',
|
|
456
|
+
script: 'check',
|
|
457
|
+
gameRoot,
|
|
458
|
+
packageManager,
|
|
459
|
+
scripts: gamePackage.scripts,
|
|
460
|
+
required: true,
|
|
461
|
+
}),
|
|
462
|
+
packageScriptAcceptanceStep({
|
|
463
|
+
id: acceptanceStepIds.test,
|
|
464
|
+
label: 'Game tests',
|
|
465
|
+
script: 'test',
|
|
466
|
+
gameRoot,
|
|
467
|
+
packageManager,
|
|
468
|
+
scripts: gamePackage.scripts,
|
|
469
|
+
required: false,
|
|
470
|
+
skipped: input.skipTest,
|
|
471
|
+
}),
|
|
472
|
+
packageScriptAcceptanceStep({
|
|
473
|
+
id: acceptanceStepIds.build,
|
|
474
|
+
label: 'Game build',
|
|
475
|
+
script: 'build',
|
|
476
|
+
gameRoot,
|
|
477
|
+
packageManager,
|
|
478
|
+
scripts: gamePackage.scripts,
|
|
479
|
+
required: true,
|
|
480
|
+
}),
|
|
481
|
+
input.skipGraph
|
|
482
|
+
? skippedAcceptanceStep(acceptanceStepIds.graph, 'TypeScript graph preflight', 'Disabled by --skip-graph.')
|
|
483
|
+
: {
|
|
484
|
+
id: acceptanceStepIds.graph,
|
|
485
|
+
label: 'TypeScript graph preflight',
|
|
486
|
+
command: packageManager,
|
|
487
|
+
args: ['graph:preflight'],
|
|
488
|
+
cwd: kitPath,
|
|
489
|
+
},
|
|
490
|
+
packageScriptAcceptanceStep({
|
|
491
|
+
id: acceptanceStepIds.playtest,
|
|
492
|
+
label: 'Automated playtest',
|
|
493
|
+
script: input.playtestScript,
|
|
494
|
+
gameRoot,
|
|
495
|
+
packageManager,
|
|
496
|
+
scripts: gamePackage.scripts,
|
|
497
|
+
required: false,
|
|
498
|
+
skipped: input.skipPlaytest,
|
|
499
|
+
}),
|
|
500
|
+
targetMatrixAcceptanceStep({
|
|
501
|
+
action: 'build-all',
|
|
502
|
+
id: acceptanceStepIds.targetBuild,
|
|
503
|
+
label: 'Target build matrix',
|
|
504
|
+
kitPath,
|
|
505
|
+
packageManager,
|
|
506
|
+
targetsFile,
|
|
507
|
+
targets: input.targets,
|
|
508
|
+
profile: input.profile,
|
|
509
|
+
aitVariant: input.aitVariant,
|
|
510
|
+
...(input.iosVariant === undefined ? {} : { iosVariant: input.iosVariant }),
|
|
511
|
+
skipped: input.skipTargetBuild,
|
|
512
|
+
}),
|
|
513
|
+
targetMatrixAcceptanceStep({
|
|
514
|
+
action: 'smoke-all',
|
|
515
|
+
id: acceptanceStepIds.targetSmoke,
|
|
516
|
+
label: 'Target smoke matrix',
|
|
517
|
+
kitPath,
|
|
518
|
+
packageManager,
|
|
519
|
+
targetsFile,
|
|
520
|
+
targets: input.targets,
|
|
521
|
+
profile: input.profile,
|
|
522
|
+
aitVariant: input.aitVariant,
|
|
523
|
+
...(input.iosVariant === undefined ? {} : { iosVariant: input.iosVariant }),
|
|
524
|
+
skipped: input.skipTargetSmoke,
|
|
525
|
+
}),
|
|
526
|
+
];
|
|
527
|
+
const reportDir = path.resolve(gameRoot, input.reportDir ?? 'artifacts/acceptance');
|
|
528
|
+
const releaseManifestFile = resolveGameAcceptanceReleaseManifestFile(gameRoot);
|
|
529
|
+
const result = runGameAcceptance({
|
|
530
|
+
gameRoot,
|
|
531
|
+
reportDir,
|
|
532
|
+
...(input.skipTargetBuild
|
|
533
|
+
? {}
|
|
534
|
+
: { releaseManifestFile }),
|
|
535
|
+
options: {
|
|
536
|
+
targets: input.targets,
|
|
537
|
+
profile: input.profile,
|
|
538
|
+
aitVariant: input.aitVariant,
|
|
539
|
+
iosVariant: input.iosVariant ?? null,
|
|
540
|
+
playtestScript: input.playtestScript,
|
|
541
|
+
skipTest: input.skipTest,
|
|
542
|
+
skipGraph: input.skipGraph,
|
|
543
|
+
skipPlaytest: input.skipPlaytest,
|
|
544
|
+
skipTargetBuild: input.skipTargetBuild,
|
|
545
|
+
skipTargetSmoke: input.skipTargetSmoke,
|
|
546
|
+
timeoutMs: String(input.timeoutMs),
|
|
547
|
+
},
|
|
548
|
+
steps,
|
|
549
|
+
commandTimeoutMs: input.timeoutMs,
|
|
550
|
+
env: {
|
|
551
|
+
...process.env,
|
|
552
|
+
MPGD_KIT_PATH: kitPath,
|
|
553
|
+
MPGD_RELEASE_MANIFEST_FILE: releaseManifestFile,
|
|
554
|
+
},
|
|
555
|
+
});
|
|
556
|
+
console.log(`[mpgd:accept] JSON report: ${result.jsonFile}`);
|
|
557
|
+
console.log(`[mpgd:accept] Markdown report: ${result.markdownFile}`);
|
|
558
|
+
if (result.report.status === 'failed') {
|
|
559
|
+
throw new Error(`Game acceptance failed. Report: ${result.markdownFile}`);
|
|
560
|
+
}
|
|
561
|
+
console.log('[mpgd:accept] passed');
|
|
562
|
+
}
|
|
563
|
+
function assertGameAcceptanceRoot(gameRoot) {
|
|
564
|
+
const resolved = path.resolve(gameRoot);
|
|
565
|
+
if (!existsSync(resolved)) {
|
|
566
|
+
throw new Error(`Game root does not exist: ${resolved}`);
|
|
567
|
+
}
|
|
568
|
+
return realpathSync(resolved);
|
|
569
|
+
}
|
|
570
|
+
function packageScriptAcceptanceStep(input) {
|
|
571
|
+
if (input.skipped === true) {
|
|
572
|
+
return skippedAcceptanceStep(input.id, input.label, 'Disabled by command option.');
|
|
573
|
+
}
|
|
574
|
+
if (input.scripts?.[input.script] === undefined && !input.required) {
|
|
575
|
+
return skippedAcceptanceStep(input.id, input.label, `Optional package script "${input.script}" is not configured.`);
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
id: input.id,
|
|
579
|
+
label: input.label,
|
|
580
|
+
command: input.packageManager,
|
|
581
|
+
args: ['run', input.script],
|
|
582
|
+
cwd: input.gameRoot,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function targetMatrixAcceptanceStep(input) {
|
|
586
|
+
if (input.skipped) {
|
|
587
|
+
return skippedAcceptanceStep(input.id, input.label, 'Disabled by command option.');
|
|
588
|
+
}
|
|
589
|
+
const args = [
|
|
590
|
+
'mpgd',
|
|
591
|
+
'target',
|
|
592
|
+
input.action,
|
|
593
|
+
'--targets-file',
|
|
594
|
+
input.targetsFile,
|
|
595
|
+
'--kit-path',
|
|
596
|
+
input.kitPath,
|
|
597
|
+
'--targets',
|
|
598
|
+
input.targets,
|
|
599
|
+
];
|
|
600
|
+
if (input.action === 'build-all') {
|
|
601
|
+
args.push('--profile', input.profile, '--ait-variant', input.aitVariant);
|
|
602
|
+
if (input.iosVariant !== undefined) {
|
|
603
|
+
args.push('--ios-variant', input.iosVariant);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
id: input.id,
|
|
608
|
+
label: input.label,
|
|
609
|
+
command: input.packageManager,
|
|
610
|
+
args,
|
|
611
|
+
cwd: input.kitPath,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function skippedAcceptanceStep(id, label, skipReason) {
|
|
615
|
+
return { id, label, skipReason };
|
|
616
|
+
}
|
|
617
|
+
function readPositiveInteger(value, label) {
|
|
618
|
+
const parsed = Number(value);
|
|
619
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
620
|
+
throw new Error(`${label} must be a positive integer.`);
|
|
621
|
+
}
|
|
622
|
+
return parsed;
|
|
623
|
+
}
|
|
225
624
|
const legalCommand = defineI18n({
|
|
226
625
|
name: 'legal',
|
|
227
626
|
description: 'Build static legal/support pages for store and release evidence.',
|
|
@@ -738,6 +1137,7 @@ function createGameApp(input) {
|
|
|
738
1137
|
' mpgd target build-all',
|
|
739
1138
|
`--targets-file ${path.join(appDir, 'mpgd.targets.json')}`,
|
|
740
1139
|
`--targets ${recommendedMatrixTargets}`,
|
|
1140
|
+
'--profile staging',
|
|
741
1141
|
'--ait-variant wrapper',
|
|
742
1142
|
'--kit-path <path-to-mpgd-kit>',
|
|
743
1143
|
].join(' '));
|
|
@@ -949,6 +1349,7 @@ function toTemplatePath(value) {
|
|
|
949
1349
|
}
|
|
950
1350
|
function createTargetCommandEnv(values) {
|
|
951
1351
|
const targetsFile = path.resolve(readOptionalString(values['targets-file']) ?? 'mpgd.targets.json');
|
|
1352
|
+
const gameRoot = path.dirname(targetsFile);
|
|
952
1353
|
const kitPath = resolveKitPathForTarget(values);
|
|
953
1354
|
const resolvedTargetsFile = readOptionalString(values['resolved-targets-file']);
|
|
954
1355
|
const preparedTargetsFile = preparePlatformTargetsFile({
|
|
@@ -960,10 +1361,75 @@ function createTargetCommandEnv(values) {
|
|
|
960
1361
|
});
|
|
961
1362
|
return {
|
|
962
1363
|
...process.env,
|
|
1364
|
+
...createGameOwnedReleaseEnv(gameRoot, process.env),
|
|
963
1365
|
MPGD_KIT_PATH: kitPath,
|
|
964
1366
|
MPGD_PLATFORM_TARGETS_FILE: preparedTargetsFile,
|
|
965
1367
|
};
|
|
966
1368
|
}
|
|
1369
|
+
function createGameOwnedReleaseEnv(gameRoot, baseEnv) {
|
|
1370
|
+
const monetizationFiles = resolveGameOwnedMonetizationFiles(gameRoot, baseEnv);
|
|
1371
|
+
const sourceGitSha = readOptionalString(baseEnv.MPGD_SOURCE_GIT_SHA)
|
|
1372
|
+
?? readSourceGitSha(gameRoot);
|
|
1373
|
+
const appVersion = readOptionalString(baseEnv.APP_VERSION)
|
|
1374
|
+
?? readGamePackageVersion(gameRoot);
|
|
1375
|
+
return {
|
|
1376
|
+
...monetizationFiles,
|
|
1377
|
+
MPGD_SOURCE_GIT_SHA: sourceGitSha,
|
|
1378
|
+
...(appVersion === undefined ? {} : { APP_VERSION: appVersion }),
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
function resolveGameOwnedMonetizationFiles(gameRoot, baseEnv) {
|
|
1382
|
+
const configuredCatalog = readConfiguredPath(baseEnv.MPGD_PRODUCT_CATALOG_FILE);
|
|
1383
|
+
const configuredPlacements = readConfiguredPath(baseEnv.MPGD_AD_PLACEMENTS_FILE);
|
|
1384
|
+
if ((configuredCatalog === undefined) !== (configuredPlacements === undefined)) {
|
|
1385
|
+
throw new Error('MPGD_PRODUCT_CATALOG_FILE and MPGD_AD_PLACEMENTS_FILE must be configured together.');
|
|
1386
|
+
}
|
|
1387
|
+
if (configuredCatalog !== undefined && configuredPlacements !== undefined) {
|
|
1388
|
+
return {
|
|
1389
|
+
MPGD_PRODUCT_CATALOG_FILE: path.resolve(gameRoot, configuredCatalog),
|
|
1390
|
+
MPGD_AD_PLACEMENTS_FILE: path.resolve(gameRoot, configuredPlacements),
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
const catalogFile = path.join(gameRoot, 'mpgd.catalog.json');
|
|
1394
|
+
const placementsFile = path.join(gameRoot, 'mpgd.ad-placements.json');
|
|
1395
|
+
const hasCatalog = existsSync(catalogFile);
|
|
1396
|
+
const hasPlacements = existsSync(placementsFile);
|
|
1397
|
+
if (hasCatalog !== hasPlacements) {
|
|
1398
|
+
throw new Error(`Game target builds must provide both mpgd.catalog.json and mpgd.ad-placements.json: ${gameRoot}`);
|
|
1399
|
+
}
|
|
1400
|
+
if (!hasCatalog) {
|
|
1401
|
+
return {};
|
|
1402
|
+
}
|
|
1403
|
+
return {
|
|
1404
|
+
MPGD_PRODUCT_CATALOG_FILE: catalogFile,
|
|
1405
|
+
MPGD_AD_PLACEMENTS_FILE: placementsFile,
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
function readConfiguredPath(value) {
|
|
1409
|
+
const normalized = value?.trim();
|
|
1410
|
+
return normalized === undefined || normalized.length === 0 ? undefined : normalized;
|
|
1411
|
+
}
|
|
1412
|
+
function readSourceGitSha(gameRoot) {
|
|
1413
|
+
const result = spawnSync('git', ['-C', gameRoot, 'rev-parse', 'HEAD'], {
|
|
1414
|
+
encoding: 'utf8',
|
|
1415
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1416
|
+
});
|
|
1417
|
+
if (result.error !== undefined || result.status !== 0) {
|
|
1418
|
+
return 'uncommitted';
|
|
1419
|
+
}
|
|
1420
|
+
return result.stdout.trim() || 'uncommitted';
|
|
1421
|
+
}
|
|
1422
|
+
function readGamePackageVersion(gameRoot) {
|
|
1423
|
+
const packageFile = path.join(gameRoot, 'package.json');
|
|
1424
|
+
if (!existsSync(packageFile)) {
|
|
1425
|
+
return undefined;
|
|
1426
|
+
}
|
|
1427
|
+
const packageJson = readJsonForCli(packageFile);
|
|
1428
|
+
if (typeof packageJson !== 'object' || packageJson === null || Array.isArray(packageJson)) {
|
|
1429
|
+
return undefined;
|
|
1430
|
+
}
|
|
1431
|
+
return readOptionalString(packageJson.version);
|
|
1432
|
+
}
|
|
967
1433
|
function preparePlatformTargetsFile(input) {
|
|
968
1434
|
const parsed = readJsonForCli(input.targetsFile);
|
|
969
1435
|
const gameRoot = path.dirname(input.targetsFile);
|
|
@@ -1022,8 +1488,9 @@ function replaceTargetTokens(value, context) {
|
|
|
1022
1488
|
function runTargetCommand(input) {
|
|
1023
1489
|
const target = normalizeBuildTarget(input.target);
|
|
1024
1490
|
const env = withTargetVariantEnv(target, input.variant, input.env);
|
|
1491
|
+
const profile = input.profile ?? 'production';
|
|
1025
1492
|
const args = input.action === 'build'
|
|
1026
|
-
? ['build:target', target,
|
|
1493
|
+
? ['build:target', target, profile]
|
|
1027
1494
|
: ['smoke:target', target];
|
|
1028
1495
|
console.log(`[mpgd] ${input.action} ${target}`);
|
|
1029
1496
|
runPnpm(args, env);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ProductionTargetReadinessInput {
|
|
2
|
+
readonly target: string;
|
|
3
|
+
readonly profile: string;
|
|
4
|
+
readonly targetsFile: string;
|
|
5
|
+
readonly gameRoot: string;
|
|
6
|
+
readonly gameServicesUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function assertProductionTargetReadiness(input: ProductionTargetReadinessInput): void;
|