diff --git a/CHANGELOG.md b/CHANGELOG.md index f9989d4..af100a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.4.0 + +- Fix: `gm-cli package` now works correctly on Linux. +- New feature: Configurable package format for Windows (`zip` or `nsis`), macOS (`zip` or `dmg`), and Linux (`zip` or `appimage`) via `gm-options.json` or `--toolchain-options`. Example: `--toolchain-options '{"windows": {"packageType": "nsis"}}'`. + # 1.3.0 - Fix: Create blank games with latest ResourceTool release diff --git a/package.json b/package.json index 7fce33c..99fd5f4 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/YoYoGames/gm-cli" }, "type": "module", - "version": "1.3.0", + "version": "1.4.0", "files": [ "dist", "NOTICE" diff --git a/scripts/e2e.ps1 b/scripts/e2e.ps1 index efb504a..cdeaa54 100644 --- a/scripts/e2e.ps1 +++ b/scripts/e2e.ps1 @@ -30,9 +30,11 @@ function Invoke-Test { node "$gmCommand" compile @compileArgs if ($LASTEXITCODE -ne 0) { throw "compile (cold) failed" } - Write-Output "--- compile (warm) ---" - node "$gmCommand" compile @compileArgs - if ($LASTEXITCODE -ne 0) { throw "compile (warm) failed" } + # run again with a warm cache, this time with package + # FIXME: the package command seems to be broken on windows! We need to fix this + #Write-Output "--- package ---" + #node "$gmCommand" package @compileArgs + #if ($LASTEXITCODE -ne 0) { throw "package failed" } Write-Output "--- resourcetool ---" node "$gmCommand" resourcetool eval "resource list" --cache-dir "$gmCacheDir" diff --git a/scripts/e2e.sh b/scripts/e2e.sh index 182dda3..5515111 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -6,16 +6,19 @@ GM_CACHE_DIR="$(pwd)/.gmcache" run_test() { local template="$1" - local target="$2" + local target="${2:-}" local toolchain="${3:-}" - echo "=== E2E: template='$template' target='$target' toolchain='${toolchain:-default}' ===" + echo "=== E2E: template='$template' target='${target:-default}' toolchain='${toolchain:-default}' ===" rm -rf test-game node "$GM_COMMAND" init --template "$template" --name="test-game" --no-interactive --cache-dir "$GM_CACHE_DIR" cd test-game - local compile_args=(--target "$target" --cache-dir "$GM_CACHE_DIR") + local compile_args=(--cache-dir "$GM_CACHE_DIR") + if [ -n "$target" ]; then + compile_args+=(--target "$target") + fi if [ -n "$toolchain" ]; then compile_args+=(--toolchain "$toolchain") fi @@ -23,8 +26,15 @@ run_test() { echo "--- compile (cold) ---" node "$GM_COMMAND" compile "${compile_args[@]}" - echo "--- compile (warm) ---" - node "$GM_COMMAND" compile "${compile_args[@]}" + # Run again with a warm cache and the package command + # unless we are on a mac runner since it looks like we need a signing key for the game + if [ "$(uname)" != "Darwin" ]; then + echo "--- package ---" + node "$GM_COMMAND" package "${compile_args[@]}" + else + echo "--- compile ---" + node "$GM_COMMAND" compile "${compile_args[@]}" + fi echo "--- resourcetool ---" node "$GM_COMMAND" resourcetool eval "resource list" --cache-dir "$GM_CACHE_DIR" @@ -36,6 +46,7 @@ run_test() { export NO_COLOR=1 run_test "Blank Game" operagx +run_test "Blank Game" #run_test "Blank Game" operagx gmrt diff --git a/src/gm-options.ts b/src/gm-options.ts index 435dd5d..f7d2f15 100644 --- a/src/gm-options.ts +++ b/src/gm-options.ts @@ -77,6 +77,21 @@ export const gms2Schema = z .describe("Path to the Emscripten SDK used when building with YYC"), }) .partial(), + windows: z + .object({ + packageType: z.enum(["zip", "nsis"]), + }) + .partial(), + mac: z + .object({ + packageType: z.enum(["zip", "dmg"]), + }) + .partial(), + linux: z + .object({ + packageType: z.enum(["zip", "appimage"]), + }) + .partial(), }) .partial(); diff --git a/src/gms2/options.ts b/src/gms2/options.ts index b874c20..e46da5b 100644 --- a/src/gms2/options.ts +++ b/src/gms2/options.ts @@ -19,6 +19,15 @@ export interface Gms2ToolchainOptions { packageType?: "zip" | "wallpaper" | "gamestrip"; emscriptenSdk?: string; }; + windows: { + packageType?: "zip" | "nsis"; + }; + mac: { + packageType?: "zip" | "dmg"; + }; + linux: { + packageType?: "zip" | "appimage"; + }; } export function defaultGms2ToolchainOptions(): Gms2ToolchainOptions { @@ -26,5 +35,14 @@ export function defaultGms2ToolchainOptions(): Gms2ToolchainOptions { operagx: { packageType: "zip", }, + windows: { + packageType: "zip", + }, + mac: { + packageType: "zip", + }, + linux: { + packageType: "zip", + }, }; } diff --git a/src/gms2/use-gms2.ts b/src/gms2/use-gms2.ts index d2df3ed..087611a 100644 --- a/src/gms2/use-gms2.ts +++ b/src/gms2/use-gms2.ts @@ -57,6 +57,9 @@ export async function useGms2( const defaults = defaultGms2ToolchainOptions(); const toolchainOptions: Gms2ToolchainOptions = { operagx: options.toolchainOptions.operagx ?? defaults.operagx, + windows: options.toolchainOptions.windows ?? defaults.windows, + mac: options.toolchainOptions.mac ?? defaults.mac, + linux: options.toolchainOptions.linux ?? defaults.linux, }; if (runtime === "YYC" && options.target === "windows") { @@ -106,27 +109,24 @@ export async function useGms2( successMessage = "Game exited"; } else { label = `Packaging for ${options.target}`; - let targetFile: string; - if (command.outputPath === undefined) { - const ext = packageExtension(options.target); - const projectDir = ctx.path.dirname(options.projectPath); - const projectName = getProjectName(ctx, options.projectPath); - targetFile = ctx.path.join(projectDir, `${projectName}${ext ?? ""}`); - } else { - const projectDir = ctx.path.dirname(options.projectPath); - targetFile = ctx.path.resolve(projectDir, command.outputPath); - } - igorAction = getPackageAction(options.target); - extraArgs = [ - "-tf", + const projectDir = ctx.path.dirname(options.projectPath); + const projectName = getProjectName(ctx, options.projectPath); + const resolvedOutputPath = + command.outputPath !== undefined + ? ctx.path.resolve(projectDir, command.outputPath) + : undefined; + const { + action, targetFile, - ...(options.target === "operagx" - ? [ - "-packagetype", - gxPackageTypeArg(toolchainOptions.operagx.packageType), - ] - : []), - ]; + extraArgs: packageArgs, + } = getPackageAction( + options.target, + toolchainOptions, + resolvedOutputPath, + ctx.path.join(projectDir, projectName), + ); + igorAction = action; + extraArgs = ["-tf", targetFile, ...packageArgs]; successMessage = `Package created: ${targetFile}`; } @@ -171,44 +171,76 @@ export async function useGms2( actionLog.success(successMessage); } -function gxPackageTypeArg( - packageType: Gms2ToolchainOptions["operagx"]["packageType"], -): string { - switch (packageType) { - case undefined: - case "zip": - return "OperaGXPackage_Zip"; - case "gamestrip": - return "OperaGXPackage_Gamestrip"; - case "wallpaper": - return "OperaGXPackage_Wallpaper"; - default: - packageType satisfies never; - throw new Error("Unreachable"); - } -} - -function getPackageAction(target: Target): string { +function getPackageAction( + target: Target, + options: Gms2ToolchainOptions, + outputPath: string | undefined, + defaultBasePath: string, +): { + action: string; + targetFile: string; + extraArgs: string[]; +} { switch (target) { - case "windows": - case "mac": - case "linux": - return "PackageZip"; - default: - return "Package"; - // FIXME: exhaustiveness checking and fix for platforms like xbox: PackageSubmissionXboxOne", PackageSubmissionXboxSeriesXS - } -} - -function packageExtension(target: Target): string | undefined { - switch (target) { - case "windows": - case "linux": - case "mac": - case "operagx": - return ".zip"; + case "windows": { + const nsis = options.windows.packageType === "nsis"; + return { + action: nsis ? "PackageNsis" : "PackageZip", + targetFile: outputPath ?? `${defaultBasePath}${nsis ? ".exe" : ".zip"}`, + extraArgs: [], + }; + } + case "mac": { + const dmg = options.mac.packageType === "dmg"; + return { + action: dmg ? "PackageDMG" : "PackageZip", + targetFile: outputPath ?? `${defaultBasePath}${dmg ? ".dmg" : ".zip"}`, + extraArgs: [], + }; + } + case "operagx": { + let packageTypeArg: string; + switch (options.operagx.packageType) { + case undefined: + case "zip": + packageTypeArg = "OperaGXPackage_Zip"; + break; + case "gamestrip": + packageTypeArg = "OperaGXPackage_Gamestrip"; + break; + case "wallpaper": + packageTypeArg = "OperaGXPackage_Wallpaper"; + break; + default: + options.operagx.packageType satisfies never; + throw new Error("Unreachable"); + } + return { + action: "Package", + targetFile: outputPath ?? `${defaultBasePath}.zip`, + extraArgs: ["-packagetype", packageTypeArg], + }; + } + case "linux": { + const appimage = options.linux.packageType === "appimage"; + if ( + appimage && + outputPath !== undefined && + !outputPath.endsWith(".AppImage") + ) { + throw new KnownError( + "When packaging for Linux with AppImage format, the output filename must end in .AppImage.", + ); + } + return { + action: "Package", + targetFile: + outputPath ?? `${defaultBasePath}${appimage ? ".AppImage" : ".zip"}`, + extraArgs: [], + }; + } default: - return undefined; + throw new KnownError("Target not supported in GM-CLI yet."); } }