@gamecrate/cli 2.4.0 → 2.5.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/gamecrate.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { existsSync as existsSync16 } from "node:fs";
5
- import { chown, cp, mkdir as mkdir7, readdir as readdir12, readFile as readFile13, rm as rm6, rmdir, stat as stat6, writeFile as writeFile7 } from "node:fs/promises";
5
+ import { chown, cp, mkdir as mkdir7, readdir as readdir12, readFile as readFile14, rm as rm6, rmdir, stat as stat6, writeFile as writeFile7 } from "node:fs/promises";
6
6
  import { homedir as homedir7 } from "node:os";
7
7
  import { basename as basename11, dirname as dirname14, join as join28 } from "node:path";
8
8
  import { setTimeout as sleep8 } from "node:timers/promises";
@@ -1038,7 +1038,7 @@ function requireGame(args, config) {
1038
1038
 
1039
1039
  // src/image/refs.ts
1040
1040
  import { existsSync as existsSync8, readlinkSync } from "node:fs";
1041
- import { mkdir as mkdir3, readdir as readdir7, rename, rm as rm3, symlink, unlink as unlink3 } from "node:fs/promises";
1041
+ import { mkdir as mkdir3, readFile as readFile6, readdir as readdir7, rename, rm as rm3, symlink, unlink as unlink3 } from "node:fs/promises";
1042
1042
  import { dirname as dirname6, join as join11 } from "node:path";
1043
1043
 
1044
1044
  // src/cli/output.ts
@@ -4558,8 +4558,24 @@ function byPath(index, raw, game) {
4558
4558
  function refsRoot() {
4559
4559
  return join11(cacheDir(), "refs");
4560
4560
  }
4561
- function refsLink(game) {
4562
- return join11(refsRoot(), "current", game);
4561
+ function refsLink(game, version) {
4562
+ if (version === undefined)
4563
+ return join11(refsRoot(), "current", game);
4564
+ return join11(refsRoot(), "version", game, version);
4565
+ }
4566
+ async function readVersion(dir) {
4567
+ const text = await readFile6(join11(dir, "Version.txt"), "utf8").catch(() => {
4568
+ return;
4569
+ });
4570
+ const hit = text === undefined ? null : /^(\d+\.\d+)/.exec(text.trim());
4571
+ return hit === null ? undefined : hit[1];
4572
+ }
4573
+ async function pointBoth(game, dir) {
4574
+ const version = await readVersion(dir);
4575
+ const link = await point(refsLink(game), dir);
4576
+ if (version !== undefined)
4577
+ await point(refsLink(game, version), dir);
4578
+ return { link, version };
4563
4579
  }
4564
4580
  function currentRefs(game) {
4565
4581
  try {
@@ -4581,6 +4597,7 @@ function extractScript(candidates, container) {
4581
4597
  ' [ -f "$1" ] || continue',
4582
4598
  ` echo "${MARK2}$d"`,
4583
4599
  ' cp "$@" /out/',
4600
+ ` cp '${join11(container, "Version.txt")}' /out/ 2>/dev/null || true`,
4584
4601
  " exit 0",
4585
4602
  "done",
4586
4603
  "exit 3"
@@ -4598,9 +4615,8 @@ async function extractRefs(game, config) {
4598
4615
  throw new GamecrateError(`${ref} is not present, so ${game} has no assemblies to extract`, Exit.Environment, `gamecrate steam build ${game}, or docker pull ${ref}`);
4599
4616
  }
4600
4617
  const dir = join11(refsRoot(), digest.replace(/[^A-Za-z0-9]/g, "-"));
4601
- const link = refsLink(game);
4602
4618
  if (existsSync8(dir)) {
4603
- return { dir, link: await point(link, dir), digest, source: "(cached)", count: await dllCount(dir) };
4619
+ return { dir, ...await pointBoth(game, dir), digest, source: "(cached)", count: await dllCount(dir) };
4604
4620
  }
4605
4621
  const partial = `${dir}.partial`;
4606
4622
  await rm3(partial, { recursive: true, force: true });
@@ -4632,7 +4648,7 @@ async function extractRefs(game, config) {
4632
4648
  throw new GamecrateError(`${ref} reported assemblies at ${source} but none arrived`, Exit.Environment, "the copy ran inside the image; check /out was writable by the calling uid");
4633
4649
  }
4634
4650
  await rename(partial, dir);
4635
- return { dir, link: await point(link, dir), digest, source, count };
4651
+ return { dir, ...await pointBoth(game, dir), digest, source, count };
4636
4652
  }
4637
4653
  async function dllCount(dir) {
4638
4654
  const entries = await readdir7(dir).catch(() => []);
@@ -4720,11 +4736,11 @@ function profileRows(profile, spec, width, notes) {
4720
4736
 
4721
4737
  // src/cli/mods.ts
4722
4738
  import { existsSync as existsSync9 } from "node:fs";
4723
- import { mkdir as mkdir4, readFile as readFile8, readdir as readdir8, writeFile as writeFile4 } from "node:fs/promises";
4739
+ import { mkdir as mkdir4, readFile as readFile9, readdir as readdir8, writeFile as writeFile4 } from "node:fs/promises";
4724
4740
  import { dirname as dirname9, join as join14, relative as relative3, resolve as resolve6 } from "node:path";
4725
4741
 
4726
4742
  // src/config/write.ts
4727
- import { chmod, readFile as readFile6, realpath, rename as rename2, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
4743
+ import { chmod, readFile as readFile7, realpath, rename as rename2, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
4728
4744
  import { dirname as dirname7, extname as extname2, join as join12 } from "node:path";
4729
4745
  import { applyEdits, modify } from "jsonc-parser";
4730
4746
  import { isMap as isMap2, parseDocument as parseDocument2 } from "yaml";
@@ -4747,7 +4763,7 @@ async function writeConfig(file, edits) {
4747
4763
  return;
4748
4764
  const yaml = isYaml2(file);
4749
4765
  const target = await realpath(file);
4750
- const before = await readFile6(target, "utf8");
4766
+ const before = await readFile7(target, "utf8");
4751
4767
  const after = yaml ? editYaml(before, edits) : editJson(before, edits);
4752
4768
  const mode = (await stat3(target)).mode & 511;
4753
4769
  const temp = join12(dirname7(target), `.gamecrate-write-${process.pid}.tmp`);
@@ -4789,7 +4805,7 @@ function editYaml(text, edits) {
4789
4805
  }
4790
4806
 
4791
4807
  // src/mods/workshopapi.ts
4792
- import { readFile as readFile7, stat as stat4 } from "node:fs/promises";
4808
+ import { readFile as readFile8, stat as stat4 } from "node:fs/promises";
4793
4809
  import { basename as basename5, dirname as dirname8, join as join13 } from "node:path";
4794
4810
  var ENDPOINT = "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/";
4795
4811
  var CHUNK = 100;
@@ -4903,7 +4919,7 @@ async function exists(dir) {
4903
4919
  }
4904
4920
  async function readText(path) {
4905
4921
  try {
4906
- return await readFile7(path, "utf8");
4922
+ return await readFile8(path, "utf8");
4907
4923
  } catch {
4908
4924
  return "";
4909
4925
  }
@@ -5200,7 +5216,7 @@ async function walk2(root, manifestFile, plugin) {
5200
5216
  return out;
5201
5217
  }
5202
5218
  async function readId(dir, manifestFile, plugin) {
5203
- const text = await readFile8(join14(dir, manifestFile), "utf8").catch(() => {
5219
+ const text = await readFile9(join14(dir, manifestFile), "utf8").catch(() => {
5204
5220
  return;
5205
5221
  });
5206
5222
  if (text === undefined)
@@ -5235,7 +5251,7 @@ import { createInterface } from "node:readline/promises";
5235
5251
 
5236
5252
  // src/image/build.ts
5237
5253
  import { mkdirSync as mkdirSync4, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "node:fs";
5238
- import { readFile as readFile9 } from "node:fs/promises";
5254
+ import { readFile as readFile10 } from "node:fs/promises";
5239
5255
  import { tmpdir } from "node:os";
5240
5256
  import { join as join17 } from "node:path";
5241
5257
 
@@ -5673,7 +5689,7 @@ async function cell(input, opts, ctx) {
5673
5689
  }
5674
5690
  try {
5675
5691
  const dir = await download(input, opts, ctx, say);
5676
- const raw = await readFile9(join17(dir, input.versionFile), "utf8");
5692
+ const raw = await readFile10(join17(dir, input.versionFile), "utf8");
5677
5693
  const version = sanitizeVersion(raw, ctx.published ?? "unknown");
5678
5694
  const tags = tagsFor({ ...tagInput, version });
5679
5695
  const layers = join17(steamHome(opts.config.dataRoot), "layers");
@@ -6720,7 +6736,7 @@ function parseAtoms(stdout) {
6720
6736
  }
6721
6737
 
6722
6738
  // src/launch/generate.ts
6723
- import { mkdir as mkdir5, readdir as readdir9, readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
6739
+ import { mkdir as mkdir5, readdir as readdir9, readFile as readFile11, writeFile as writeFile5 } from "node:fs/promises";
6724
6740
  import { dirname as dirname12, join as join20 } from "node:path";
6725
6741
  async function readInstallVersion(game, plugin) {
6726
6742
  const raw = await installVersionText(game);
@@ -6737,7 +6753,7 @@ async function installVersionText(game) {
6737
6753
  if (host === undefined)
6738
6754
  return null;
6739
6755
  try {
6740
- return await readFile10(join20(expandHome(host), game.version.file), "utf8");
6756
+ return await readFile11(join20(expandHome(host), game.version.file), "utf8");
6741
6757
  } catch {
6742
6758
  return null;
6743
6759
  }
@@ -6762,7 +6778,7 @@ async function readKnownExpansions(game, plugin, warnings) {
6762
6778
  continue;
6763
6779
  let id = null;
6764
6780
  try {
6765
- id = plugin.parseManifest(await readFile10(join20(dataDir, entry.name, game.manifest.file), "utf8"))?.packageId ?? null;
6781
+ id = plugin.parseManifest(await readFile11(join20(dataDir, entry.name, game.manifest.file), "utf8"))?.packageId ?? null;
6766
6782
  } catch {
6767
6783
  continue;
6768
6784
  }
@@ -6833,7 +6849,7 @@ async function mergePrefs(plan) {
6833
6849
  await mkdir5(dirname12(target), { recursive: true });
6834
6850
  let existing = null;
6835
6851
  try {
6836
- existing = await readFile10(target, "utf8");
6852
+ existing = await readFile11(target, "utf8");
6837
6853
  } catch (error) {
6838
6854
  if (error.code !== "ENOENT")
6839
6855
  throw error;
@@ -6922,7 +6938,7 @@ async function offerRebuild(input) {
6922
6938
 
6923
6939
  // src/launch/supervisor.ts
6924
6940
  import { existsSync as existsSync14 } from "node:fs";
6925
- import { readFile as readFile11, readlink, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
6941
+ import { readFile as readFile12, readlink, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
6926
6942
  import { basename as basename8, join as join22 } from "node:path";
6927
6943
  import { setTimeout as sleep7 } from "node:timers/promises";
6928
6944
  async function forkSupervisor(plan, argv) {
@@ -6989,7 +7005,7 @@ async function writeExit(instanceDir, record) {
6989
7005
  `);
6990
7006
  }
6991
7007
  async function lastExit(instanceDir) {
6992
- const text = await readFile11(join22(instanceDir, ".gamecrate", "last-exit.json"), "utf8").catch(() => {
7008
+ const text = await readFile12(join22(instanceDir, ".gamecrate", "last-exit.json"), "utf8").catch(() => {
6993
7009
  return;
6994
7010
  });
6995
7011
  if (text === undefined)
@@ -7650,7 +7666,7 @@ async function detectForeignOwnership(dir, uid, limit = 100) {
7650
7666
 
7651
7667
  // src/mods/workshop.ts
7652
7668
  import { existsSync as existsSync15 } from "node:fs";
7653
- import { readFile as readFile12 } from "node:fs/promises";
7669
+ import { readFile as readFile13 } from "node:fs/promises";
7654
7670
  import { join as join27, resolve as resolvePath2 } from "node:path";
7655
7671
  var ROUNDS = 5;
7656
7672
  async function prepareWorkshop(game, profileName, args, config, allowFetch, plugin, sources) {
@@ -7713,7 +7729,7 @@ async function manifestAt(dir, game, plugin, problems) {
7713
7729
  if (!existsSync15(file))
7714
7730
  return null;
7715
7731
  try {
7716
- return plugin.parseManifest(await readFile12(file, "utf8"));
7732
+ return plugin.parseManifest(await readFile13(file, "utf8"));
7717
7733
  } catch (error) {
7718
7734
  problems.push({ where: file, message: error instanceof Error ? error.message : String(error) });
7719
7735
  return null;
@@ -7785,7 +7801,7 @@ function published2(value) {
7785
7801
  }
7786
7802
 
7787
7803
  // src/index.ts
7788
- var VERSION = "2.4.0";
7804
+ var VERSION = "2.5.0";
7789
7805
  async function main(argv) {
7790
7806
  const supervised = supervisedDir(argv);
7791
7807
  try {
@@ -8365,7 +8381,7 @@ async function logs(args, config, defaults) {
8365
8381
  }
8366
8382
  status(runDir);
8367
8383
  for (const name of files) {
8368
- const text = await readFile13(join28(runDir, name), "utf8").catch(() => "");
8384
+ const text = await readFile14(join28(runDir, name), "utf8").catch(() => "");
8369
8385
  for (const line of text.split(`
8370
8386
  `)) {
8371
8387
  if (line.length > 0)
@@ -8,9 +8,13 @@ export interface ManagedRefs {
8
8
  digest: string;
9
9
  /** Container path they were found at. */
10
10
  source: string;
11
+ /** `1.6` from the game's own Version.txt, absent when the image does not ship one. */
12
+ version?: string;
11
13
  count: number;
12
14
  }
13
- export declare function refsLink(game: string): string;
15
+ export declare function refsLink(game: string, version?: string): string;
16
+ /** `1.6.4633 rev1254` in the file, `1.6` as the folder a mod builds into. */
17
+ export declare function readVersion(dir: string): Promise<string | undefined>;
14
18
  /** What `current` points at, for a failure message. A build that broke on the wrong game
15
19
  * generation says nothing about refs on its own. */
16
20
  export declare function currentRefs(game: string): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamecrate/cli",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Run a modded game in a container, with mods resolved from your local checkouts.",
5
5
  "license": "MIT",
6
6
  "type": "module",