@mpgd/cli 0.13.0 → 0.15.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/index.d.ts +3 -0
- package/dist/index.js +500 -11
- package/dist/microsoft-store-package-acceptance.d.ts +54 -0
- package/dist/microsoft-store-package-acceptance.js +787 -0
- package/dist/microsoft-store-package-generation-contract.d.ts +129 -0
- package/dist/microsoft-store-package-generation-contract.js +3 -0
- package/dist/microsoft-store-package-generation-download.d.ts +22 -0
- package/dist/microsoft-store-package-generation-download.js +453 -0
- package/dist/microsoft-store-package-generation-input.d.ts +3 -0
- package/dist/microsoft-store-package-generation-input.js +384 -0
- package/dist/microsoft-store-package-generation-integrity.d.ts +5 -0
- package/dist/microsoft-store-package-generation-integrity.js +100 -0
- package/dist/microsoft-store-package-generation-manifest.d.ts +3 -0
- package/dist/microsoft-store-package-generation-manifest.js +67 -0
- package/dist/microsoft-store-package-generation-zip.d.ts +1 -0
- package/dist/microsoft-store-package-generation-zip.js +180 -0
- package/dist/microsoft-store-package-generation.d.ts +24 -0
- package/dist/microsoft-store-package-generation.js +430 -0
- package/dist/microsoft-store-starter.d.ts +28 -0
- package/dist/microsoft-store-starter.js +655 -0
- package/dist/microsoft-store-submission.d.ts +79 -0
- package/dist/microsoft-store-submission.js +952 -0
- package/dist/microsoft-store-supported-locales.d.ts +1 -0
- package/dist/microsoft-store-supported-locales.js +33 -0
- package/dist/production-target-readiness.js +3 -1
- package/package.json +9 -8
- package/templates/phaser-game/.agents/skills/release-microsoft-store/SKILL.md +60 -0
- package/templates/phaser-game/.agents/skills/release-microsoft-store/agents/openai.yaml +4 -0
- package/templates/phaser-game/.agents/skills/use-mpgd-kit/SKILL.md +45 -0
- package/templates/phaser-game/.agents/skills/use-mpgd-kit/agents/openai.yaml +4 -0
- package/templates/phaser-game/AGENTS.md +34 -0
- package/templates/phaser-game/README.md +88 -25
- package/templates/phaser-game/agent/acceptance.md +8 -0
- package/templates/phaser-game/agent/brief.md +13 -1
- package/templates/phaser-game/agent/game-manifest.json +7 -0
- package/templates/phaser-game/apps/target-ait/granite.config.ts +35 -0
- package/templates/phaser-game/apps/target-ait/index.html +15 -0
- package/templates/phaser-game/apps/target-ait/package.json +33 -0
- package/templates/phaser-game/apps/target-ait/src/main.ts +34 -0
- package/templates/phaser-game/apps/target-ait/src/styles.css +33 -0
- package/templates/phaser-game/apps/target-ait/tsconfig.json +8 -0
- package/templates/phaser-game/apps/target-ait/vite.config.ts +53 -0
- package/templates/phaser-game/docs/MPGD_KIT_WORKFLOWS.md +128 -0
- package/templates/phaser-game/gitignore +7 -0
- package/templates/phaser-game/mpgd.microsoft-store.json +29 -0
- package/templates/phaser-game/mpgd.targets.json +9 -2
- package/templates/phaser-game/package.json +11 -9
- package/templates/phaser-game/vite.shared.ts +13 -0
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export { renderGameAcceptanceMarkdown, resolveGameAcceptanceReleaseManifestFile,
|
|
|
2
2
|
export { createBrowserGameplayE2EDriver, type BrowserGameplayE2EInspectInput, type BrowserGameplayE2EPage, type BrowserGameplayE2EScreenshotOptions, type BrowserGameplayE2EViewport, type CreateBrowserGameplayE2EDriverInput, } from './browser-gameplay-e2e.js';
|
|
3
3
|
export { collectGameplayE2EPathEvidence, defaultGameplayE2EReportFile, maximumGameplayE2EStates, parseGameplayE2EPlan, readGameplayE2EPlan, renderGameplayE2EMarkdown, resolveGameplayE2EReportFile, runGameplayE2E, type GameplayE2EAction, type GameplayE2EActionResult, type GameplayE2EDriver, type GameplayE2EHashLimits, type GameplayE2EInputAction, type GameplayE2EObservation, type GameplayE2EPauseResumeAction, type GameplayE2EPathEvidence, type GameplayE2EPlan, type GameplayE2EReport, type GameplayE2EResultStatus, type GameplayE2EState, type GameplayE2EStateResult, type GameplayE2EStatus, type RunGameplayE2EInput, type RunGameplayE2EResult, } from './gameplay-e2e.js';
|
|
4
4
|
export { assertMicrosoftStorePwaCacheTransition, assertMicrosoftStorePwaPrecacheUrl, assertMicrosoftStorePwaReleaseEvidence, inspectMicrosoftStorePwaBrowserCacheTransition, microsoftStorePwaCacheSchema, readMicrosoftStorePwaBrowserReleaseEvidence, requestMicrosoftStorePwaBrowserUpdate, resolveMicrosoftStorePwaScopedCacheName, type AssertMicrosoftStorePwaCacheTransitionInput, type InspectMicrosoftStorePwaCacheTransitionInput, type MicrosoftStorePwaBrowserPage, type MicrosoftStorePwaCachedIndexObservation, type MicrosoftStorePwaCacheTransitionEvidence, type MicrosoftStorePwaCacheTransitionObservation, type MicrosoftStorePwaReleaseEvidence, } from './microsoft-store-pwa-e2e.js';
|
|
5
|
+
export { createMicrosoftStorePackageAcceptanceRuntime, microsoftStorePackageAcceptanceSchemaVersion, parseMicrosoftStorePackageIdentity, parseWindowsAppCertificationResult, renderMicrosoftStorePackageAcceptanceMarkdown, runMicrosoftStorePackageAcceptance, type CreateMicrosoftStorePackageAcceptanceRuntimeInput, type MicrosoftStorePackageAcceptanceEvidence, type MicrosoftStorePackageAcceptanceRuntime, type MicrosoftStorePackageIdentity, type RunMicrosoftStorePackageAcceptanceInput, } from './microsoft-store-package-acceptance.js';
|
|
6
|
+
export { createMicrosoftStorePackageGenerationRuntime, microsoftStorePackageGeneratorEndpoint, microsoftStorePackageGeneratorSourceRevision, microsoftStorePackageGenerationSchemaVersion, renderMicrosoftStorePackageGenerationMarkdown, runMicrosoftStorePackageGeneration, type CreateMicrosoftStorePackageGenerationRuntimeInput, type MicrosoftStoreAddressResolver, type MicrosoftStorePackageGenerationEvidence, type MicrosoftStorePackageGenerationRuntime, type MicrosoftStoreResolvedAddress, type RunMicrosoftStorePackageGenerationInput, } from './microsoft-store-package-generation.js';
|
|
7
|
+
export { microsoftStoreSubmissionSchemaVersion, parseMicrosoftStoreSubmissionConfig, renderMicrosoftStoreSubmissionMarkdown, runMicrosoftStoreSubmissionPreflight, type MicrosoftStoreSubmissionConfig, type MicrosoftStoreSubmissionEvidence, type RunMicrosoftStoreSubmissionPreflightInput, } from './microsoft-store-submission.js';
|
|
5
8
|
export { assertProductionTargetReadiness, type ProductionTargetReadinessInput, } from './production-target-readiness.js';
|
|
6
9
|
export declare function runMpgdCli(args: readonly string[]): Promise<void>;
|
|
7
10
|
export declare function runCreateGameCli(args: readonly string[]): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import completion from '@gunshi/plugin-completion';
|
|
@@ -8,10 +8,17 @@ import resources from '@gunshi/resources';
|
|
|
8
8
|
import { cli } from 'gunshi';
|
|
9
9
|
import { defaultGameAcceptanceCommandTimeoutMs, resolveGameAcceptanceReleaseManifestFile, runGameAcceptance, } from './game-acceptance.js';
|
|
10
10
|
import { resolveGameplayE2EReportFile } from './gameplay-e2e.js';
|
|
11
|
+
import { createMicrosoftStorePackageAcceptanceRuntime, runMicrosoftStorePackageAcceptance, } from './microsoft-store-package-acceptance.js';
|
|
12
|
+
import { runMicrosoftStorePackageGeneration } from './microsoft-store-package-generation.js';
|
|
13
|
+
import { defaultMpgdKitPath, initializeMicrosoftStoreStarter, listMicrosoftStoreInitializerTemplateFiles, prepareBaseGameTemplateFile, } from './microsoft-store-starter.js';
|
|
14
|
+
import { runMicrosoftStoreSubmissionPreflight } from './microsoft-store-submission.js';
|
|
11
15
|
export { renderGameAcceptanceMarkdown, resolveGameAcceptanceReleaseManifestFile, runGameAcceptance, defaultGameAcceptanceCommandTimeoutMs, maximumGameplayE2EReportBytes, } from './game-acceptance.js';
|
|
12
16
|
export { createBrowserGameplayE2EDriver, } from './browser-gameplay-e2e.js';
|
|
13
17
|
export { collectGameplayE2EPathEvidence, defaultGameplayE2EReportFile, maximumGameplayE2EStates, parseGameplayE2EPlan, readGameplayE2EPlan, renderGameplayE2EMarkdown, resolveGameplayE2EReportFile, runGameplayE2E, } from './gameplay-e2e.js';
|
|
14
18
|
export { assertMicrosoftStorePwaCacheTransition, assertMicrosoftStorePwaPrecacheUrl, assertMicrosoftStorePwaReleaseEvidence, inspectMicrosoftStorePwaBrowserCacheTransition, microsoftStorePwaCacheSchema, readMicrosoftStorePwaBrowserReleaseEvidence, requestMicrosoftStorePwaBrowserUpdate, resolveMicrosoftStorePwaScopedCacheName, } from './microsoft-store-pwa-e2e.js';
|
|
19
|
+
export { createMicrosoftStorePackageAcceptanceRuntime, microsoftStorePackageAcceptanceSchemaVersion, parseMicrosoftStorePackageIdentity, parseWindowsAppCertificationResult, renderMicrosoftStorePackageAcceptanceMarkdown, runMicrosoftStorePackageAcceptance, } from './microsoft-store-package-acceptance.js';
|
|
20
|
+
export { createMicrosoftStorePackageGenerationRuntime, microsoftStorePackageGeneratorEndpoint, microsoftStorePackageGeneratorSourceRevision, microsoftStorePackageGenerationSchemaVersion, renderMicrosoftStorePackageGenerationMarkdown, runMicrosoftStorePackageGeneration, } from './microsoft-store-package-generation.js';
|
|
21
|
+
export { microsoftStoreSubmissionSchemaVersion, parseMicrosoftStoreSubmissionConfig, renderMicrosoftStoreSubmissionMarkdown, runMicrosoftStoreSubmissionPreflight, } from './microsoft-store-submission.js';
|
|
15
22
|
export { assertProductionTargetReadiness, } from './production-target-readiness.js';
|
|
16
23
|
const sourceDir = path.dirname(fileURLToPath(import.meta.url));
|
|
17
24
|
const packageRoot = resolvePackageRoot(sourceDir);
|
|
@@ -19,6 +26,10 @@ const detectedKitRoot = resolveDefaultKitRoot();
|
|
|
19
26
|
const gameTemplateDir = path.resolve(packageRoot, 'templates/phaser-game');
|
|
20
27
|
const cliVersion = readPackageVersion(packageRoot);
|
|
21
28
|
const recommendedMatrixTargets = 'web,microsoft-store,verse8,ait,reddit';
|
|
29
|
+
const recommendedMatrixTargetsWithoutMicrosoftStore = recommendedMatrixTargets
|
|
30
|
+
.split(',')
|
|
31
|
+
.filter((target) => target !== 'microsoft-store')
|
|
32
|
+
.join(',');
|
|
22
33
|
const defaultDependencyVersion = `^${cliVersion}`;
|
|
23
34
|
const standaloneTemplateDependencyVersionFallbacks = {
|
|
24
35
|
// Initial-published independently from the CLI release line; package metadata wins once concrete.
|
|
@@ -183,6 +194,10 @@ const gameCommand = defineI18n({
|
|
|
183
194
|
en: 'Print files that would be generated without writing them.',
|
|
184
195
|
ko: '파일을 쓰지 않고 생성 예정 목록만 출력합니다.',
|
|
185
196
|
},
|
|
197
|
+
'microsoft-store': {
|
|
198
|
+
en: 'Include the Microsoft Store PWA target and release workflow.',
|
|
199
|
+
ko: 'Microsoft Store PWA 타깃과 출시 워크플로를 포함합니다.',
|
|
200
|
+
},
|
|
186
201
|
}),
|
|
187
202
|
args: {
|
|
188
203
|
directory: {
|
|
@@ -220,6 +235,11 @@ const gameCommand = defineI18n({
|
|
|
220
235
|
required: false,
|
|
221
236
|
description: 'Print files that would be generated without writing them.',
|
|
222
237
|
},
|
|
238
|
+
'microsoft-store': {
|
|
239
|
+
type: 'boolean',
|
|
240
|
+
required: false,
|
|
241
|
+
description: 'Include the Microsoft Store PWA target and release workflow.',
|
|
242
|
+
},
|
|
223
243
|
},
|
|
224
244
|
run: (ctx) => {
|
|
225
245
|
const positionals = readLocalPositionals(ctx.positionals, ['game', 'create']);
|
|
@@ -238,6 +258,7 @@ const gameCommand = defineI18n({
|
|
|
238
258
|
workspace: ctx.values.workspace === true,
|
|
239
259
|
...(kitPath === undefined ? {} : { kitPath }),
|
|
240
260
|
dryRun: ctx.values['dry-run'] === true,
|
|
261
|
+
microsoftStore: ctx.values['microsoft-store'] === true,
|
|
241
262
|
});
|
|
242
263
|
},
|
|
243
264
|
}),
|
|
@@ -727,12 +748,85 @@ const legalCommand = defineI18n({
|
|
|
727
748
|
});
|
|
728
749
|
const targetCommand = defineI18n({
|
|
729
750
|
name: 'target',
|
|
730
|
-
description: 'Build
|
|
751
|
+
description: 'Build, smoke, generate, preflight, and accept target release artifacts.',
|
|
731
752
|
resource: commandResource({
|
|
732
|
-
en: 'Build
|
|
733
|
-
ko: '
|
|
753
|
+
en: 'Build, smoke, generate, preflight, and accept target release artifacts.',
|
|
754
|
+
ko: '타깃 출시 산출물을 빌드하고 스모크, 패키지 생성, 제출 사전 검증 및 인수 검증을 수행합니다.',
|
|
734
755
|
}),
|
|
735
756
|
subCommands: {
|
|
757
|
+
init: defineI18n({
|
|
758
|
+
name: 'init',
|
|
759
|
+
description: 'Initialize an optional target workflow in an existing game.',
|
|
760
|
+
resource: commandResource({
|
|
761
|
+
en: 'Initialize an optional target workflow in an existing game without overwriting conflicts.',
|
|
762
|
+
ko: '충돌 파일을 덮어쓰지 않고 기존 게임에 선택적 타깃 워크플로를 초기화합니다.',
|
|
763
|
+
}, {
|
|
764
|
+
target: {
|
|
765
|
+
en: 'Target to initialize. Currently supports microsoft-store.',
|
|
766
|
+
ko: '초기화할 타깃. 현재 microsoft-store를 지원합니다.',
|
|
767
|
+
},
|
|
768
|
+
game: {
|
|
769
|
+
en: 'Existing game root. Defaults to the current directory.',
|
|
770
|
+
ko: '기존 게임 루트. 기본값은 현재 디렉터리입니다.',
|
|
771
|
+
},
|
|
772
|
+
'kit-path': {
|
|
773
|
+
en: 'Default mpgd-kit path written into generated target scripts.',
|
|
774
|
+
ko: '생성되는 타깃 스크립트에 기록할 기본 mpgd-kit 경로.',
|
|
775
|
+
},
|
|
776
|
+
'dry-run': {
|
|
777
|
+
en: 'Validate and print planned file changes without writing them.',
|
|
778
|
+
ko: '파일을 쓰지 않고 예정 변경을 검증하고 출력합니다.',
|
|
779
|
+
},
|
|
780
|
+
}),
|
|
781
|
+
args: {
|
|
782
|
+
target: {
|
|
783
|
+
type: 'positional',
|
|
784
|
+
required: true,
|
|
785
|
+
description: 'Target to initialize. Currently supports microsoft-store.',
|
|
786
|
+
},
|
|
787
|
+
game: {
|
|
788
|
+
type: 'string',
|
|
789
|
+
required: false,
|
|
790
|
+
default: '.',
|
|
791
|
+
description: 'Existing game root. Defaults to the current directory.',
|
|
792
|
+
},
|
|
793
|
+
'kit-path': {
|
|
794
|
+
type: 'string',
|
|
795
|
+
required: false,
|
|
796
|
+
description: 'Default mpgd-kit path written into generated target scripts.',
|
|
797
|
+
},
|
|
798
|
+
'dry-run': {
|
|
799
|
+
type: 'boolean',
|
|
800
|
+
required: false,
|
|
801
|
+
description: 'Validate and print planned file changes without writing them.',
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
run: (ctx) => {
|
|
805
|
+
const positionals = readLocalPositionals(ctx.positionals, ['target', 'init']);
|
|
806
|
+
const requestedTarget = readRequiredPositional(positionals, 0, 'target');
|
|
807
|
+
const target = normalizeConfiguredBuildTarget(requestedTarget);
|
|
808
|
+
if (target !== 'microsoft-store') {
|
|
809
|
+
throw new Error(`Target initialization is only available for microsoft-store; received: ${requestedTarget}`);
|
|
810
|
+
}
|
|
811
|
+
const gameRoot = path.resolve(readOptionalString(ctx.values.game) ?? '.');
|
|
812
|
+
const configuredKitPath = readOptionalString(ctx.values['kit-path']);
|
|
813
|
+
const resolvedKitPath = configuredKitPath === undefined
|
|
814
|
+
? detectedKitRoot
|
|
815
|
+
: assertKitRoot(path.resolve(configuredKitPath));
|
|
816
|
+
const defaultKitPath = resolvedKitPath === undefined
|
|
817
|
+
? defaultMpgdKitPath
|
|
818
|
+
: toTemplatePath(path.relative(gameRoot, resolvedKitPath) || '.');
|
|
819
|
+
const dryRun = ctx.values['dry-run'] === true;
|
|
820
|
+
const result = initializeMicrosoftStoreStarter({
|
|
821
|
+
gameRoot,
|
|
822
|
+
templateRoot: gameTemplateDir,
|
|
823
|
+
defaultKitPath,
|
|
824
|
+
dryRun,
|
|
825
|
+
});
|
|
826
|
+
for (const file of result.changedFiles) {
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
}),
|
|
736
830
|
build: defineI18n({
|
|
737
831
|
name: 'build',
|
|
738
832
|
description: 'Build one target artifact.',
|
|
@@ -833,6 +927,319 @@ const targetCommand = defineI18n({
|
|
|
833
927
|
});
|
|
834
928
|
},
|
|
835
929
|
}),
|
|
930
|
+
'generate-package': defineI18n({
|
|
931
|
+
name: 'generate-package',
|
|
932
|
+
description: 'Generate a packaged target artifact with an external platform service.',
|
|
933
|
+
resource: commandResource({
|
|
934
|
+
en: 'Generate a packaged target artifact with an external platform service.',
|
|
935
|
+
ko: '외부 플랫폼 서비스로 패키징된 타깃 산출물을 생성합니다.',
|
|
936
|
+
}, {
|
|
937
|
+
target: {
|
|
938
|
+
en: 'Package target. Currently supports microsoft-store.',
|
|
939
|
+
ko: '패키지 타깃. 현재 microsoft-store를 지원합니다.',
|
|
940
|
+
},
|
|
941
|
+
'pwa-url': {
|
|
942
|
+
en: 'Public HTTPS URL of the deployed PWA.',
|
|
943
|
+
ko: '배포된 PWA의 공개 HTTPS URL.',
|
|
944
|
+
},
|
|
945
|
+
'manifest-url': {
|
|
946
|
+
en: 'Public HTTPS URL of the deployed web app manifest.',
|
|
947
|
+
ko: '배포된 웹 앱 매니페스트의 공개 HTTPS URL.',
|
|
948
|
+
},
|
|
949
|
+
'package-version': {
|
|
950
|
+
en: 'Modern Microsoft Store package version.',
|
|
951
|
+
ko: '최신 Microsoft Store 패키지 버전.',
|
|
952
|
+
},
|
|
953
|
+
'classic-version': {
|
|
954
|
+
en: 'Lower classic Microsoft Store package version.',
|
|
955
|
+
ko: '최신 버전보다 낮은 클래식 Microsoft Store 패키지 버전.',
|
|
956
|
+
},
|
|
957
|
+
'submission-evidence': {
|
|
958
|
+
en: 'Submission preflight evidence file.',
|
|
959
|
+
ko: '제출 사전 검증 증적 파일.',
|
|
960
|
+
},
|
|
961
|
+
'output-file': {
|
|
962
|
+
en: 'New game-owned ZIP file for the package generator response.',
|
|
963
|
+
ko: '패키지 생성기 응답을 기록할 새 게임 소유 ZIP 파일.',
|
|
964
|
+
},
|
|
965
|
+
'output-dir': {
|
|
966
|
+
en: 'Directory for package generation evidence.',
|
|
967
|
+
ko: '패키지 생성 증적 디렉터리.',
|
|
968
|
+
},
|
|
969
|
+
'targets-file': {
|
|
970
|
+
en: 'Game target config file.',
|
|
971
|
+
ko: '게임 타깃 설정 파일.',
|
|
972
|
+
},
|
|
973
|
+
}),
|
|
974
|
+
args: {
|
|
975
|
+
target: {
|
|
976
|
+
type: 'positional',
|
|
977
|
+
required: true,
|
|
978
|
+
description: 'Package target. Currently supports microsoft-store.',
|
|
979
|
+
},
|
|
980
|
+
'pwa-url': {
|
|
981
|
+
type: 'string',
|
|
982
|
+
required: true,
|
|
983
|
+
description: 'Public HTTPS URL of the deployed PWA.',
|
|
984
|
+
},
|
|
985
|
+
'manifest-url': {
|
|
986
|
+
type: 'string',
|
|
987
|
+
required: true,
|
|
988
|
+
description: 'Public HTTPS URL of the deployed web app manifest.',
|
|
989
|
+
},
|
|
990
|
+
'package-version': {
|
|
991
|
+
type: 'string',
|
|
992
|
+
required: true,
|
|
993
|
+
description: 'Modern Microsoft Store package version.',
|
|
994
|
+
},
|
|
995
|
+
'classic-version': {
|
|
996
|
+
type: 'string',
|
|
997
|
+
required: true,
|
|
998
|
+
description: 'Lower classic Microsoft Store package version.',
|
|
999
|
+
},
|
|
1000
|
+
'submission-evidence': {
|
|
1001
|
+
type: 'string',
|
|
1002
|
+
required: false,
|
|
1003
|
+
default: 'release-output/microsoft-store/submission-preflight.json',
|
|
1004
|
+
description: 'Submission preflight evidence file.',
|
|
1005
|
+
},
|
|
1006
|
+
'output-file': {
|
|
1007
|
+
type: 'string',
|
|
1008
|
+
required: false,
|
|
1009
|
+
default: 'release-input/microsoft-store/pwabuilder-package.zip',
|
|
1010
|
+
description: 'New game-owned ZIP file for the package generator response.',
|
|
1011
|
+
},
|
|
1012
|
+
'output-dir': {
|
|
1013
|
+
type: 'string',
|
|
1014
|
+
required: false,
|
|
1015
|
+
default: 'release-output/microsoft-store',
|
|
1016
|
+
description: 'Directory for package generation evidence.',
|
|
1017
|
+
},
|
|
1018
|
+
'targets-file': {
|
|
1019
|
+
type: 'string',
|
|
1020
|
+
required: false,
|
|
1021
|
+
default: 'mpgd.targets.json',
|
|
1022
|
+
description: 'Game target config file.',
|
|
1023
|
+
},
|
|
1024
|
+
},
|
|
1025
|
+
run: async (ctx) => {
|
|
1026
|
+
const positionals = readLocalPositionals(ctx.positionals, ['target', 'generate-package']);
|
|
1027
|
+
const target = normalizeBuildTarget(readRequiredPositional(positionals, 0, 'target'));
|
|
1028
|
+
if (target !== 'microsoft-store') {
|
|
1029
|
+
throw new Error(`Package generation is not available for target: ${target}`);
|
|
1030
|
+
}
|
|
1031
|
+
const targetsFile = path.resolve(readOptionalString(ctx.values['targets-file']) ?? 'mpgd.targets.json');
|
|
1032
|
+
const gameRoot = path.dirname(targetsFile);
|
|
1033
|
+
const outputDir = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-dir']) ?? 'release-output/microsoft-store');
|
|
1034
|
+
assertGameOwnedOutputDirectory(gameRoot, outputDir);
|
|
1035
|
+
resolveMicrosoftStoreArtifactRoot(targetsFile);
|
|
1036
|
+
const evidence = await runMicrosoftStorePackageGeneration({
|
|
1037
|
+
gameRoot,
|
|
1038
|
+
submissionEvidenceFile: resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['submission-evidence'])
|
|
1039
|
+
?? 'release-output/microsoft-store/submission-preflight.json'),
|
|
1040
|
+
pwaUrl: readOptionalString(ctx.values['pwa-url']) ?? '',
|
|
1041
|
+
manifestUrl: readOptionalString(ctx.values['manifest-url']) ?? '',
|
|
1042
|
+
modernVersion: readOptionalString(ctx.values['package-version']) ?? '',
|
|
1043
|
+
classicVersion: readOptionalString(ctx.values['classic-version']) ?? '',
|
|
1044
|
+
outputFile: resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-file'])
|
|
1045
|
+
?? 'release-input/microsoft-store/pwabuilder-package.zip'),
|
|
1046
|
+
jsonFile: path.join(outputDir, 'package-generation.json'),
|
|
1047
|
+
markdownFile: path.join(outputDir, 'package-generation.md'),
|
|
1048
|
+
});
|
|
1049
|
+
},
|
|
1050
|
+
}),
|
|
1051
|
+
preflight: defineI18n({
|
|
1052
|
+
name: 'preflight',
|
|
1053
|
+
description: 'Validate game-owned target submission inputs and write release evidence.',
|
|
1054
|
+
resource: commandResource({
|
|
1055
|
+
en: 'Validate game-owned target submission inputs and write release evidence.',
|
|
1056
|
+
ko: '게임 소유 타깃 제출 입력을 검증하고 출시 증적을 작성합니다.',
|
|
1057
|
+
}, {
|
|
1058
|
+
target: {
|
|
1059
|
+
en: 'Submission target. Currently supports microsoft-store.',
|
|
1060
|
+
ko: '제출 타깃. 현재 microsoft-store를 지원합니다.',
|
|
1061
|
+
},
|
|
1062
|
+
'targets-file': {
|
|
1063
|
+
en: 'Game target config file.',
|
|
1064
|
+
ko: '게임 타깃 설정 파일.',
|
|
1065
|
+
},
|
|
1066
|
+
'kit-path': {
|
|
1067
|
+
en: 'Path to the mpgd-kit checkout used to resolve target config tokens.',
|
|
1068
|
+
ko: '타깃 설정 토큰 해석에 사용할 mpgd-kit 체크아웃 경로.',
|
|
1069
|
+
},
|
|
1070
|
+
'resolved-targets-file': {
|
|
1071
|
+
en: 'Output path for the resolved target config file.',
|
|
1072
|
+
ko: '해석된 타깃 설정 파일의 출력 경로.',
|
|
1073
|
+
},
|
|
1074
|
+
'submission-file': {
|
|
1075
|
+
en: 'Game-owned Microsoft Store submission config file.',
|
|
1076
|
+
ko: '게임 소유 Microsoft Store 제출 설정 파일.',
|
|
1077
|
+
},
|
|
1078
|
+
'output-dir': {
|
|
1079
|
+
en: 'Directory for deterministic submission evidence.',
|
|
1080
|
+
ko: '결정적 제출 증적을 기록할 디렉터리.',
|
|
1081
|
+
},
|
|
1082
|
+
}),
|
|
1083
|
+
args: {
|
|
1084
|
+
target: {
|
|
1085
|
+
type: 'positional',
|
|
1086
|
+
required: true,
|
|
1087
|
+
description: 'Submission target. Currently supports microsoft-store.',
|
|
1088
|
+
},
|
|
1089
|
+
...targetConfigArgs(),
|
|
1090
|
+
'submission-file': {
|
|
1091
|
+
type: 'string',
|
|
1092
|
+
required: false,
|
|
1093
|
+
default: 'mpgd.microsoft-store.json',
|
|
1094
|
+
description: 'Game-owned Microsoft Store submission config file.',
|
|
1095
|
+
},
|
|
1096
|
+
'output-dir': {
|
|
1097
|
+
type: 'string',
|
|
1098
|
+
required: false,
|
|
1099
|
+
default: 'release-output/microsoft-store',
|
|
1100
|
+
description: 'Directory for deterministic submission evidence.',
|
|
1101
|
+
},
|
|
1102
|
+
},
|
|
1103
|
+
run: (ctx) => {
|
|
1104
|
+
const positionals = readLocalPositionals(ctx.positionals, ['target', 'preflight']);
|
|
1105
|
+
const target = normalizeBuildTarget(readRequiredPositional(positionals, 0, 'target'));
|
|
1106
|
+
if (target !== 'microsoft-store') {
|
|
1107
|
+
throw new Error(`Submission preflight is not available for target: ${target}`);
|
|
1108
|
+
}
|
|
1109
|
+
const targetsFile = path.resolve(readOptionalString(ctx.values['targets-file']) ?? 'mpgd.targets.json');
|
|
1110
|
+
const gameRoot = path.dirname(targetsFile);
|
|
1111
|
+
const commandEnv = createTargetCommandEnv(ctx.values);
|
|
1112
|
+
const preparedTargetsFile = commandEnv.MPGD_PLATFORM_TARGETS_FILE;
|
|
1113
|
+
if (preparedTargetsFile === undefined || preparedTargetsFile.length === 0) {
|
|
1114
|
+
throw new Error('Failed to prepare the game target config for submission preflight.');
|
|
1115
|
+
}
|
|
1116
|
+
const configFile = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['submission-file']) ?? 'mpgd.microsoft-store.json');
|
|
1117
|
+
const outputDir = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-dir']) ?? 'release-output/microsoft-store');
|
|
1118
|
+
assertGameOwnedOutputDirectory(gameRoot, outputDir);
|
|
1119
|
+
mkdirSync(outputDir, { recursive: true });
|
|
1120
|
+
const evidence = runMicrosoftStoreSubmissionPreflight({
|
|
1121
|
+
gameRoot,
|
|
1122
|
+
artifactRoot: resolveMicrosoftStoreArtifactRoot(preparedTargetsFile),
|
|
1123
|
+
configFile,
|
|
1124
|
+
jsonFile: path.join(outputDir, 'submission-preflight.json'),
|
|
1125
|
+
markdownFile: path.join(outputDir, 'submission-preflight.md'),
|
|
1126
|
+
});
|
|
1127
|
+
},
|
|
1128
|
+
}),
|
|
1129
|
+
'accept-package': defineI18n({
|
|
1130
|
+
name: 'accept-package',
|
|
1131
|
+
description: 'Inspect packaged target artifacts and optionally run WACK.',
|
|
1132
|
+
resource: commandResource({
|
|
1133
|
+
en: 'Inspect packaged target artifacts and optionally run WACK.',
|
|
1134
|
+
ko: '패키징된 타깃 산출물을 검사하고 선택적으로 WACK를 실행합니다.',
|
|
1135
|
+
}, {
|
|
1136
|
+
target: {
|
|
1137
|
+
en: 'Packaged target. Currently supports microsoft-store.',
|
|
1138
|
+
ko: '패키징 타깃. 현재 microsoft-store를 지원합니다.',
|
|
1139
|
+
},
|
|
1140
|
+
packages: {
|
|
1141
|
+
en: 'Comma-separated game-relative MSIX or AppX package paths.',
|
|
1142
|
+
ko: '쉼표로 구분한 게임 상대 MSIX 또는 AppX 패키지 경로.',
|
|
1143
|
+
},
|
|
1144
|
+
'targets-file': {
|
|
1145
|
+
en: 'Game target config file.',
|
|
1146
|
+
ko: '게임 타깃 설정 파일.',
|
|
1147
|
+
},
|
|
1148
|
+
'submission-evidence': {
|
|
1149
|
+
en: 'Submission preflight evidence file.',
|
|
1150
|
+
ko: '제출 사전 검증 증적 파일.',
|
|
1151
|
+
},
|
|
1152
|
+
'output-dir': {
|
|
1153
|
+
en: 'Directory for optional WACK reports and acceptance evidence.',
|
|
1154
|
+
ko: '선택적 WACK 리포트와 인수 증적을 기록할 디렉터리.',
|
|
1155
|
+
},
|
|
1156
|
+
appcert: {
|
|
1157
|
+
en: 'Optional appcert.exe path to opt into the recommended WACK check.',
|
|
1158
|
+
ko: '권장 WACK 검사를 선택 실행하기 위한 appcert.exe 경로.',
|
|
1159
|
+
},
|
|
1160
|
+
makeappx: {
|
|
1161
|
+
en: 'Optional path to makeappx.exe.',
|
|
1162
|
+
ko: '선택적 makeappx.exe 경로.',
|
|
1163
|
+
},
|
|
1164
|
+
}),
|
|
1165
|
+
args: {
|
|
1166
|
+
target: {
|
|
1167
|
+
type: 'positional',
|
|
1168
|
+
required: true,
|
|
1169
|
+
description: 'Packaged target. Currently supports microsoft-store.',
|
|
1170
|
+
},
|
|
1171
|
+
packages: {
|
|
1172
|
+
type: 'string',
|
|
1173
|
+
required: true,
|
|
1174
|
+
description: 'Comma-separated game-relative MSIX or AppX package paths.',
|
|
1175
|
+
},
|
|
1176
|
+
'targets-file': {
|
|
1177
|
+
type: 'string',
|
|
1178
|
+
required: false,
|
|
1179
|
+
default: 'mpgd.targets.json',
|
|
1180
|
+
description: 'Game target config file.',
|
|
1181
|
+
},
|
|
1182
|
+
'submission-evidence': {
|
|
1183
|
+
type: 'string',
|
|
1184
|
+
required: false,
|
|
1185
|
+
default: 'release-output/microsoft-store/submission-preflight.json',
|
|
1186
|
+
description: 'Submission preflight evidence file.',
|
|
1187
|
+
},
|
|
1188
|
+
'output-dir': {
|
|
1189
|
+
type: 'string',
|
|
1190
|
+
required: false,
|
|
1191
|
+
default: 'release-output/microsoft-store',
|
|
1192
|
+
description: 'Directory for optional WACK reports and acceptance evidence.',
|
|
1193
|
+
},
|
|
1194
|
+
appcert: {
|
|
1195
|
+
type: 'string',
|
|
1196
|
+
required: false,
|
|
1197
|
+
description: 'Optional appcert.exe path to opt into the recommended WACK check.',
|
|
1198
|
+
},
|
|
1199
|
+
makeappx: {
|
|
1200
|
+
type: 'string',
|
|
1201
|
+
required: false,
|
|
1202
|
+
description: 'Optional path to makeappx.exe.',
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
1205
|
+
run: (ctx) => {
|
|
1206
|
+
const positionals = readLocalPositionals(ctx.positionals, ['target', 'accept-package']);
|
|
1207
|
+
const target = normalizeBuildTarget(readRequiredPositional(positionals, 0, 'target'));
|
|
1208
|
+
if (target !== 'microsoft-store') {
|
|
1209
|
+
throw new Error(`Package acceptance is not available for target: ${target}`);
|
|
1210
|
+
}
|
|
1211
|
+
const targetsFile = path.resolve(readOptionalString(ctx.values['targets-file']) ?? 'mpgd.targets.json');
|
|
1212
|
+
const gameRoot = path.dirname(targetsFile);
|
|
1213
|
+
const packageList = readOptionalString(ctx.values.packages);
|
|
1214
|
+
if (packageList === undefined) {
|
|
1215
|
+
throw new Error('--packages must be a non-empty comma-separated list.');
|
|
1216
|
+
}
|
|
1217
|
+
const packages = packageList
|
|
1218
|
+
.split(',')
|
|
1219
|
+
.map((file) => file.trim())
|
|
1220
|
+
.filter((file) => file.length > 0)
|
|
1221
|
+
.map((file) => resolveGameRelativePath(gameRoot, file));
|
|
1222
|
+
if (packages.length === 0) {
|
|
1223
|
+
throw new Error('--packages must be a non-empty comma-separated list.');
|
|
1224
|
+
}
|
|
1225
|
+
const outputDir = resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['output-dir']) ?? 'release-output/microsoft-store');
|
|
1226
|
+
const appCertExecutable = readOptionalString(ctx.values.appcert);
|
|
1227
|
+
const makeAppxExecutable = readOptionalString(ctx.values.makeappx);
|
|
1228
|
+
resolveMicrosoftStoreArtifactRoot(targetsFile);
|
|
1229
|
+
assertGameOwnedOutputDirectory(gameRoot, outputDir);
|
|
1230
|
+
mkdirSync(outputDir, { recursive: true });
|
|
1231
|
+
const evidence = runMicrosoftStorePackageAcceptance({
|
|
1232
|
+
gameRoot,
|
|
1233
|
+
submissionEvidenceFile: resolveGameRelativePath(gameRoot, readOptionalString(ctx.values['submission-evidence'])
|
|
1234
|
+
?? 'release-output/microsoft-store/submission-preflight.json'),
|
|
1235
|
+
packageFiles: packages,
|
|
1236
|
+
outputDir,
|
|
1237
|
+
}, createMicrosoftStorePackageAcceptanceRuntime({
|
|
1238
|
+
...(appCertExecutable === undefined ? {} : { appCertExecutable }),
|
|
1239
|
+
...(makeAppxExecutable === undefined ? {} : { makeAppxExecutable }),
|
|
1240
|
+
}));
|
|
1241
|
+
},
|
|
1242
|
+
}),
|
|
836
1243
|
'build-all': defineI18n({
|
|
837
1244
|
name: 'build-all',
|
|
838
1245
|
description: 'Build multiple target artifacts.',
|
|
@@ -1164,17 +1571,54 @@ function createGameApp(input) {
|
|
|
1164
1571
|
workspace: input.workspace,
|
|
1165
1572
|
...(input.kitPath === undefined ? {} : { kitPath: input.kitPath }),
|
|
1166
1573
|
...(input.title === undefined ? {} : { title: input.title }),
|
|
1574
|
+
microsoftStore: input.microsoftStore,
|
|
1575
|
+
});
|
|
1576
|
+
const files = collectTemplateFiles(gameTemplateDir).flatMap((file) => {
|
|
1577
|
+
const prepared = prepareBaseGameTemplateFile({
|
|
1578
|
+
relativePath: file.relativePath,
|
|
1579
|
+
content: file.content,
|
|
1580
|
+
});
|
|
1581
|
+
return prepared === undefined
|
|
1582
|
+
? []
|
|
1583
|
+
: [{ ...file, content: renderTemplate(prepared, context) }];
|
|
1167
1584
|
});
|
|
1168
|
-
const files = collectTemplateFiles(gameTemplateDir);
|
|
1169
1585
|
if (input.dryRun) {
|
|
1170
|
-
|
|
1586
|
+
const plannedFiles = input.microsoftStore
|
|
1587
|
+
? [...files.map((file) => file.relativePath), ...listMicrosoftStoreInitializerTemplateFiles()]
|
|
1588
|
+
: files.map((file) => file.relativePath);
|
|
1589
|
+
for (const relativePath of [...new Set(plannedFiles)].sort()) {
|
|
1171
1590
|
}
|
|
1172
1591
|
return;
|
|
1173
1592
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
mkdirSync(path.dirname(
|
|
1177
|
-
|
|
1593
|
+
let createdAppDirectory = false;
|
|
1594
|
+
try {
|
|
1595
|
+
mkdirSync(path.dirname(appDir), { recursive: true });
|
|
1596
|
+
mkdirSync(appDir);
|
|
1597
|
+
createdAppDirectory = true;
|
|
1598
|
+
for (const file of files) {
|
|
1599
|
+
const outputFile = path.join(appDir, file.relativePath);
|
|
1600
|
+
mkdirSync(path.dirname(outputFile), { recursive: true });
|
|
1601
|
+
writeFileSync(outputFile, file.content);
|
|
1602
|
+
}
|
|
1603
|
+
if (input.microsoftStore) {
|
|
1604
|
+
initializeMicrosoftStoreStarter({
|
|
1605
|
+
gameRoot: appDir,
|
|
1606
|
+
templateRoot: gameTemplateDir,
|
|
1607
|
+
defaultKitPath: context.defaultKitPath,
|
|
1608
|
+
dryRun: false,
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
catch (error) {
|
|
1613
|
+
if (createdAppDirectory) {
|
|
1614
|
+
try {
|
|
1615
|
+
rmSync(appDir, { force: true, recursive: true });
|
|
1616
|
+
}
|
|
1617
|
+
catch (cleanupError) {
|
|
1618
|
+
console.warn(`Failed to clean up incomplete game directory ${appDir}: ${formatError(cleanupError)}`);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
throw error;
|
|
1178
1622
|
}
|
|
1179
1623
|
}
|
|
1180
1624
|
function assertValidGameName(name) {
|
|
@@ -1248,7 +1692,10 @@ function createTemplateContext(input) {
|
|
|
1248
1692
|
workspaceI18nBuildPrefix: input.workspace
|
|
1249
1693
|
? `pnpm --dir ${workspacePrefix} i18n:build && `
|
|
1250
1694
|
: '',
|
|
1251
|
-
defaultKitPath: kitPath === undefined ?
|
|
1695
|
+
defaultKitPath: kitPath === undefined ? defaultMpgdKitPath : workspacePrefix,
|
|
1696
|
+
recommendedMatrixTargets: input.microsoftStore
|
|
1697
|
+
? recommendedMatrixTargets
|
|
1698
|
+
: recommendedMatrixTargetsWithoutMicrosoftStore,
|
|
1252
1699
|
pnpmWorkspaceKitPackages: input.workspace
|
|
1253
1700
|
? [
|
|
1254
1701
|
` - '${workspacePrefix}/packages/*'`,
|
|
@@ -1340,6 +1787,7 @@ function renderTemplate(content, context) {
|
|
|
1340
1787
|
.replaceAll('__TSCONFIG_WORKSPACE_EXCLUDES__', context.tsconfigWorkspaceExcludes)
|
|
1341
1788
|
.replaceAll('__WORKSPACE_I18N_BUILD_PREFIX__', context.workspaceI18nBuildPrefix)
|
|
1342
1789
|
.replaceAll('__DEFAULT_KIT_PATH__', context.defaultKitPath)
|
|
1790
|
+
.replaceAll('__RECOMMENDED_MATRIX_TARGETS__', context.recommendedMatrixTargets)
|
|
1343
1791
|
.replaceAll('__PNPM_WORKSPACE_KIT_PACKAGES__', context.pnpmWorkspaceKitPackages)
|
|
1344
1792
|
.replaceAll('__PASCAL_NAME__', context.pascalName)
|
|
1345
1793
|
.replaceAll('__CAMEL_NAME__', context.camelName);
|
|
@@ -1620,6 +2068,47 @@ function readConfiguredTargetsFile(env) {
|
|
|
1620
2068
|
}
|
|
1621
2069
|
return file;
|
|
1622
2070
|
}
|
|
2071
|
+
function resolveMicrosoftStoreArtifactRoot(targetsFile) {
|
|
2072
|
+
const parsed = readJsonForCli(targetsFile);
|
|
2073
|
+
assertJsonObject(parsed, `targets file ${targetsFile}`);
|
|
2074
|
+
assertJsonObject(parsed.targets, `targets in ${targetsFile}`);
|
|
2075
|
+
const target = parsed.targets['microsoft-store'];
|
|
2076
|
+
assertJsonObject(target, `microsoft-store target in ${targetsFile}`);
|
|
2077
|
+
if (target.kind !== 'web') {
|
|
2078
|
+
throw new Error(`Microsoft Store target in ${targetsFile} must use kind web.`);
|
|
2079
|
+
}
|
|
2080
|
+
const output = readOptionalString(target.output);
|
|
2081
|
+
if (output === undefined) {
|
|
2082
|
+
throw new Error(`Microsoft Store target in ${targetsFile} must declare output.`);
|
|
2083
|
+
}
|
|
2084
|
+
return resolveGameRelativePath(path.dirname(targetsFile), output);
|
|
2085
|
+
}
|
|
2086
|
+
function resolveGameRelativePath(gameRoot, candidate) {
|
|
2087
|
+
return path.resolve(gameRoot, candidate);
|
|
2088
|
+
}
|
|
2089
|
+
function assertGameOwnedOutputDirectory(gameRoot, outputDir) {
|
|
2090
|
+
const canonicalGameRoot = realpathSync(gameRoot);
|
|
2091
|
+
const canonicalOutputDir = resolvePotentiallyMissingPath(outputDir);
|
|
2092
|
+
const relative = path.relative(canonicalGameRoot, canonicalOutputDir);
|
|
2093
|
+
if (relative.length === 0
|
|
2094
|
+
|| relative === '..'
|
|
2095
|
+
|| relative.startsWith(`..${path.sep}`)
|
|
2096
|
+
|| path.isAbsolute(relative)) {
|
|
2097
|
+
throw new Error(`Microsoft Store submission output directory must stay inside the game root: ${canonicalOutputDir} is not inside ${canonicalGameRoot}.`);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
function resolvePotentiallyMissingPath(input) {
|
|
2101
|
+
const resolved = path.resolve(input);
|
|
2102
|
+
let existingAncestor = resolved;
|
|
2103
|
+
while (!existsSync(existingAncestor)) {
|
|
2104
|
+
const parent = path.dirname(existingAncestor);
|
|
2105
|
+
if (parent === existingAncestor) {
|
|
2106
|
+
throw new Error(`Could not find an existing ancestor for output path: ${resolved}`);
|
|
2107
|
+
}
|
|
2108
|
+
existingAncestor = parent;
|
|
2109
|
+
}
|
|
2110
|
+
return path.resolve(realpathSync(existingAncestor), path.relative(existingAncestor, resolved));
|
|
2111
|
+
}
|
|
1623
2112
|
function normalizeConfiguredBuildTarget(target) {
|
|
1624
2113
|
try {
|
|
1625
2114
|
return normalizeBuildTarget(target);
|