diff --git a/src/bin/cli.ts b/src/bin/cli.ts index 20778d1..edf43be 100644 --- a/src/bin/cli.ts +++ b/src/bin/cli.ts @@ -17,4 +17,7 @@ import { run } from "@stricli/core"; import { buildContext } from "~/context"; import { app } from "~/app"; -await run(app, process.argv.slice(2), buildContext(process)); +const plainOutput = process.argv.includes("--plain-output"); +const inputs = process.argv.slice(2).filter((arg) => arg !== "--plain-output"); + +await run(app, inputs, buildContext(process, plainOutput)); diff --git a/src/context.ts b/src/context.ts index ed69458..0f95fdb 100644 --- a/src/context.ts +++ b/src/context.ts @@ -34,7 +34,7 @@ export interface Context extends CommandContext { readonly fetch: typeof globalThis.fetch; readonly open: typeof open; readonly http: typeof http; - readonly makeTaskLogger: TaskLogger; + makeTaskLogger: TaskLogger; readonly env: ParsedEnv; } @@ -50,7 +50,10 @@ export async function exists( } } -export function buildContext(process: NodeJS.Process): Context { +export function buildContext( + process: NodeJS.Process, + plainOutput = false, +): Context { const env = getParsedEnv(); return { process, @@ -63,6 +66,8 @@ export function buildContext(process: NodeJS.Process): Context { path, fetch, makeTaskLogger: - env.NO_COLOR === true ? plainTaskLogger() : fancyTaskLogger(), + plainOutput || env.NO_COLOR === true + ? plainTaskLogger() + : fancyTaskLogger(), }; } diff --git a/src/gm-options.ts b/src/gm-options.ts index bb6eea3..fcb2f9e 100644 --- a/src/gm-options.ts +++ b/src/gm-options.ts @@ -217,7 +217,7 @@ export async function readGmOptions( gmOptions = parseResults.options; } else { throw new KnownError( - "Failed to parse 'gm-options.json file. Errors:\n{parseResults.error}", + `Failed to parse 'gm-options.json file. Errors:\n${parseResults.error}`, ); } diff --git a/src/gms2/install-runtime.ts b/src/gms2/install-runtime.ts index 5f644e4..e2e7b81 100644 --- a/src/gms2/install-runtime.ts +++ b/src/gms2/install-runtime.ts @@ -233,10 +233,22 @@ export async function installRuntimeIfNeeded( }); let runtimeLocation = await findRuntimeLocation(ctx, runtimeDir, version); - // FIXME: if this fails, we should delete the runtime dir and try again - const installedModules = runtimeLocation - ? await getInstalledRuntimeModules(ctx, runtimeLocation) - : []; + // A runtime directory can be left behind by an interrupted or failed + // installation. In that case receipt.json may be missing or corrupt. + // Treat the cached runtime as invalid and reinstall it instead of failing + // every subsequent build. + let installedModules: Module[] = []; + if (runtimeLocation) { + try { + installedModules = await getInstalledRuntimeModules(ctx, runtimeLocation); + } catch { + log.message( + `Cached runtime at '${runtimeLocation}' is invalid; reinstalling...`, + ); + await ctx.fs.rm(runtimeLocation, { recursive: true, force: true }); + runtimeLocation = undefined; + } + } if (runtimeLocation && installedModules.includes(target)) { log.success("Runtime found"); diff --git a/src/gms2/use-gms2.ts b/src/gms2/use-gms2.ts index b9367fb..38e007b 100644 --- a/src/gms2/use-gms2.ts +++ b/src/gms2/use-gms2.ts @@ -144,6 +144,16 @@ export async function useGms2( ); igorAction = action; extraArgs = ["-tf", targetFile, ...packageArgs]; + + // Igor uses the build cache output directory for intermediate package files. + // Clear those intermediates before every package operation so a previous + // package with a different output name cannot leave a file that Igor tries + // to move again. See https://github.com/YoYoGames/gm-cli/issues/243. + await ctx.fs.rm(ctx.path.join(buildCacheDir, "output"), { + recursive: true, + force: true, + }); + successMessage = `Package created: ${targetFile}`; }