@arcadible/github-release-tools 1.0.2 → 1.0.4

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/README.md CHANGED
@@ -13,25 +13,26 @@
13
13
  <p align="center">
14
14
  <a href="https://developer.arcadible.com/publish/deploying-from-github">Docs</a> ·
15
15
  <a href="https://www.npmjs.com/package/arcadible">Arcadible CLI</a> ·
16
- <a href="https://arcadible.com">Arcadible</a>
16
+ <a href="https://arcadible.com">Arcadible</a> ·
17
+ <a href="https://studio.arcadible.com">Studio</a>
17
18
  </p>
18
19
 
19
20
  <br>
20
21
 
21
- When `arc deploy git` pushes a release tag, the Arcadible Release workflow builds the game on GitHub Actions. It installs this package alongside the [Arcadible CLI](https://www.npmjs.com/package/arcadible) and uses its `arctool-*` commands to read the game's manifest. You don't need to install it yourself.
22
+ When `arc deploy git` pushes a release tag, the Arcadible Release workflow builds the game on GitHub Actions. It installs this package alongside the [Arcadible CLI](https://www.npmjs.com/package/arcadible), on Node.js 24, and runs its `arctool-*` commands between the CLI's. You don't need to install it yourself.
22
23
 
23
- | Command | What it does |
24
- | ------------------------ | --------------------------------------------------------------------- |
25
- | `arctool-assert-tag` | Checks that the release tag matches the manifest's version |
26
- | `arctool-assert-dir` | Checks that the build produced the deploy directory |
27
- | `arctool-put-version` | Outputs the manifest's version |
28
- | `arctool-put-prerelease` | Outputs whether the version is a prerelease |
29
- | `arctool-put-build` | Outputs the build command |
30
- | `arctool-put-deploy-dir` | Outputs the deploy directory |
31
- | `arctool-put-cache` | Outputs the package manager to cache, from the project's lockfile |
32
- | `arctool-install-deps` | Installs the project's dependencies exactly as its lockfile pins them |
24
+ | Command | What it does |
25
+ | ------------------------------------- | -------------------------------------------------------------------------- |
26
+ | `arctool-assert-tag <manifest> <tag>` | Checks that the tag is `arcadible/release/v<version>`, with the manifest's |
27
+ | `arctool-assert-dir <dir>` | Checks that the build wrote the deploy directory, a folder |
28
+ | `arctool-put-version <manifest>` | Outputs `version`, the manifest's |
29
+ | `arctool-put-prerelease <manifest>` | Outputs `prerelease`, whether the version is a prerelease |
30
+ | `arctool-put-build <manifest>` | Outputs `build`, the build command on one line, or nothing without one |
31
+ | `arctool-put-deploy-dir <manifest>` | Outputs `deploy_dir` |
32
+ | `arctool-put-cache <manifest>` | Outputs `cache`, the package manager whose lockfile is beside the manifest |
33
+ | `arctool-install-deps <dir>` | Installs the project's dependencies exactly as its lockfile pins them |
33
34
 
34
- Each `put` command writes its value to the step's `GITHUB_OUTPUT`. `arctool-install-deps` runs `npm ci`, or pnpm or Yarn through corepack, and does nothing without a lockfile.
35
+ `<manifest>` is the file `arc inspect --json` writes. Each `put` command writes its value to the step's `GITHUB_OUTPUT`. `arctool-install-deps` runs `npm ci`, or pnpm or Yarn through corepack, and does nothing without a lockfile. A command that fails says why on stderr, and exits 1.
35
36
 
36
37
  ## License
37
38
 
@@ -2,8 +2,4 @@
2
2
  set -e
3
3
  SCRIPT="$(realpath "$0")"
4
4
  DIR="$(dirname "$SCRIPT")"
5
- RESULT=$(node "$DIR/../src/assert-dir.js" "$1")
6
- if [ "$RESULT" != "true" ]; then
7
- echo "Unable to find: $1"
8
- exit 1
9
- fi
5
+ node "$DIR/../src/assert-dir.js" "$@"
@@ -2,8 +2,4 @@
2
2
  set -e
3
3
  SCRIPT="$(realpath "$0")"
4
4
  DIR="$(dirname "$SCRIPT")"
5
- RESULT=$(node "$DIR/../src/assert-tag.js" "$1" "$2")
6
- if [ "$RESULT" != "true" ]; then
7
- echo "Invalid tag: $2"
8
- exit 1
9
- fi
5
+ node "$DIR/../src/assert-tag.js" "$@"
package/package.json CHANGED
@@ -32,5 +32,5 @@
32
32
  "scripts": {
33
33
  "test": "node --test \"test/**/*.test.js\""
34
34
  },
35
- "version": "1.0.2"
35
+ "version": "1.0.4"
36
36
  }
package/src/assert-dir.js CHANGED
@@ -1,30 +1,35 @@
1
1
  "use strict";
2
+ // arctool-assert-dir <dir>: passes when the build wrote deploy_dir, a folder.
2
3
 
3
4
  // [import]
4
5
 
5
6
  const fs = require("node:fs");
6
7
 
8
+ // [import]
9
+
10
+ const io = require("./io");
11
+
7
12
  // [use]
8
13
 
9
- const existsSync = fs.existsSync; // eslint-disable-line no-sync
14
+ const fail = io.fail;
15
+ const run = io.run;
16
+ const statSync = fs.statSync; // eslint-disable-line no-sync
10
17
 
11
18
  // [define]
12
19
 
13
20
  const dir = process.argv[2];
14
21
 
15
- // [function]
16
-
17
- const write = function (data) {
18
- process.stdout.write(data.trim().concat("\n"));
19
- };
20
-
21
22
  // [main]
22
23
 
23
- try {
24
+ run(() => {
24
25
  if (!dir) {
25
- throw new Error("No directory provided.");
26
+ fail("No directory provided.");
27
+ }
28
+ const info = statSync(dir, { throwIfNoEntry: false });
29
+ if (!info) {
30
+ fail(`Unable to find: ${dir}`, "The build didn't write deploy_dir.");
31
+ }
32
+ if (!info.isDirectory()) {
33
+ fail(`Unable to find: ${dir}`, "It's a file, and deploy_dir is a folder.");
26
34
  }
27
- write(existsSync(dir) ? "true" : "false");
28
- } catch {
29
- write("false");
30
- }
35
+ });
package/src/assert-tag.js CHANGED
@@ -1,51 +1,47 @@
1
1
  "use strict";
2
+ // arctool-assert-tag <manifest> <tag>: passes when the tag is
3
+ // arcadible/release/v<version>, with the manifest's version.
2
4
 
3
5
  // [import]
4
6
 
5
- const fs = require("node:fs");
7
+ const semver = require("semver");
6
8
 
7
9
  // [import]
8
10
 
9
- const semver = require("semver");
11
+ const io = require("./io");
10
12
 
11
13
  // [use]
12
14
 
13
- const cleanSemver = semver.clean;
14
- const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
15
+ const fail = io.fail;
16
+ const readManifest = io.readManifest;
17
+ const run = io.run;
15
18
  const validSemver = semver.valid;
16
19
 
20
+ // [declare]
21
+
22
+ const PREFIX = "arcadible/release/v";
23
+
17
24
  // [define]
18
25
 
19
26
  const file = process.argv[2];
20
27
  const tag = process.argv[3];
21
28
 
22
- // [function]
23
-
24
- const write = function (data) {
25
- process.stdout.write(data.trim().concat("\n"));
26
- };
27
-
28
29
  // [main]
29
30
 
30
- try {
31
- if (!file) {
32
- throw new Error("No file provided.");
33
- }
31
+ run(() => {
34
32
  if (!tag) {
35
- throw new Error("No tag provided.");
33
+ fail("No tag provided.");
34
+ }
35
+ const invalid = `Invalid tag: ${tag}`;
36
+ if (!tag.startsWith(PREFIX)) {
37
+ fail(invalid, `A release tag is ${PREFIX}<version>.`);
38
+ }
39
+ const release = validSemver(tag.slice(PREFIX.length));
40
+ if (!release) {
41
+ fail(invalid, `${tag.slice(PREFIX.length)} isn't a version.`);
36
42
  }
37
- if (!tag.startsWith("arcadible/release/v")) {
38
- throw new Error("Invalid tag prefix.");
43
+ const version = validSemver(readManifest(file)?.game?.version);
44
+ if (release !== version) {
45
+ fail(invalid, `The project's version is ${version ?? "not set"}.`);
39
46
  }
40
- const buf = readFileSync(file, "utf8");
41
- const conf = JSON.parse(buf);
42
- const version = validSemver(conf?.game?.version);
43
- const release = cleanSemver(tag.split("/").pop());
44
- write(
45
- version !== null && release !== null && version === release
46
- ? "true"
47
- : "false",
48
- );
49
- } catch {
50
- write("false");
51
- }
47
+ });
@@ -7,9 +7,12 @@ const cp = require("node:child_process");
7
7
  // [import]
8
8
 
9
9
  const detect = require("./lockfile");
10
+ const io = require("./io");
10
11
 
11
12
  // [use]
12
13
 
14
+ const fail = io.fail;
15
+ const run = io.run;
13
16
  const spawnSync = cp.spawnSync; // eslint-disable-line no-sync
14
17
 
15
18
  // [declare]
@@ -27,34 +30,26 @@ const COMMANDS = {
27
30
 
28
31
  const dir = process.argv[2];
29
32
 
30
- // [function]
31
-
32
- const writeErr = function (data) {
33
- process.stderr.write(data.trim().concat("\n"));
34
- };
35
-
36
33
  // [main]
37
34
 
38
- try {
35
+ run(() => {
39
36
  if (!dir) {
40
- throw new Error("No directory provided.");
37
+ fail("No directory provided.");
41
38
  }
42
39
  const manager = detect(dir);
43
- if (manager) {
44
- const [command, args] = COMMANDS[manager];
45
- const { error, status } = spawnSync(command, args, {
46
- cwd: dir,
47
- env: { ...process.env, COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
48
- stdio: "inherit",
49
- });
50
- if (error) {
51
- throw error;
52
- }
53
- if (status !== 0) {
54
- process.exit(status ?? 1);
55
- }
40
+ if (!manager) {
41
+ return;
42
+ }
43
+ const [command, args] = COMMANDS[manager];
44
+ const { error, status } = spawnSync(command, args, {
45
+ cwd: dir,
46
+ env: { ...process.env, COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
47
+ stdio: "inherit",
48
+ });
49
+ if (error) {
50
+ throw error;
51
+ }
52
+ if (status !== 0) {
53
+ process.exit(status ?? 1);
56
54
  }
57
- } catch (ex) {
58
- writeErr(ex.message);
59
- process.exit(1);
60
- }
55
+ });
package/src/io.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ // what every arctool command shares: its result on stdout, which the bash
3
+ // wrapper writes to $GITHUB_OUTPUT, and a failure on stderr, in arc's shape
4
+ // (the CLI's OUTPUT.md): what failed, then why.
5
+
6
+ // [import]
7
+
8
+ const fs = require("node:fs");
9
+
10
+ // [use]
11
+
12
+ const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
13
+
14
+ // [function]
15
+
16
+ // writes `data` to stdout, one line.
17
+ const write = function (data) {
18
+ process.stdout.write(String(data).trim().concat("\n"));
19
+ };
20
+
21
+ // [function]
22
+
23
+ // writes `lines` to stderr, then exits 1.
24
+ const fail = function (...lines) {
25
+ process.stderr.write(lines.join("\n").trim().concat("\n"));
26
+ process.exit(1);
27
+ };
28
+
29
+ // [function]
30
+
31
+ // the resolved manifest `arc inspect --json` wrote to `file`.
32
+ const readManifest = function (file) {
33
+ if (!file) {
34
+ return fail("No manifest file provided.");
35
+ }
36
+ try {
37
+ return JSON.parse(readFileSync(file, "utf8"));
38
+ } catch (ex) {
39
+ return fail(`Unable to read ${file}.`, ex.message);
40
+ }
41
+ };
42
+
43
+ // [function]
44
+
45
+ // runs `main`, turning anything it throws into a failure.
46
+ const run = function (main) {
47
+ try {
48
+ main();
49
+ } catch (ex) {
50
+ fail(ex.message);
51
+ }
52
+ };
53
+
54
+ // [export]
55
+
56
+ module.exports = { fail, readManifest, run, write };
package/src/put-build.js CHANGED
@@ -1,49 +1,32 @@
1
1
  "use strict";
2
+ // arctool-put-build <manifest>: the build command, on one line, or nothing
3
+ // when there's none. The workflow only asks whether there is one; a command on
4
+ // several lines would break $GITHUB_OUTPUT, so its lines are joined with &&.
2
5
 
3
6
  // [import]
4
7
 
5
- const fs = require("node:fs");
8
+ const io = require("./io");
6
9
 
7
10
  // [use]
8
11
 
9
- const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
12
+ const readManifest = io.readManifest;
13
+ const run = io.run;
14
+ const write = io.write;
10
15
 
11
16
  // [define]
12
17
 
13
18
  const file = process.argv[2];
14
19
 
15
- // [function]
16
-
17
- const write = function (data) {
18
- process.stdout.write(data.trim().concat("\n"));
19
- };
20
-
21
- // [function]
22
-
23
- const writeErr = function (data) {
24
- process.stderr.write(data.trim().concat("\n"));
25
- };
26
-
27
20
  // [main]
28
21
 
29
- try {
30
- if (!file) {
31
- throw new Error("No file provided.");
32
- }
33
- const buf = readFileSync(file, "utf8");
34
- const conf = JSON.parse(buf);
35
- const build = conf?.workflow?.build ?? "";
36
- if (Array.isArray(build)) {
37
- write(
38
- build
39
- .map((command) => command.trim())
40
- .filter(Boolean)
41
- .join(" && "),
42
- );
43
- } else {
44
- write(build.trim());
45
- }
46
- } catch (ex) {
47
- writeErr(ex.message);
48
- process.exit(1);
49
- }
22
+ run(() => {
23
+ const build = readManifest(file)?.workflow?.build ?? "";
24
+ write(
25
+ [build]
26
+ .flat()
27
+ .flatMap((command) => String(command).split("\n"))
28
+ .map((command) => command.trim())
29
+ .filter(Boolean)
30
+ .join(" && "),
31
+ );
32
+ });
package/src/put-cache.js CHANGED
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ // arctool-put-cache <manifest>: the package manager whose lockfile is beside
3
+ // the manifest's project, for setup-node's cache, or nothing.
2
4
 
3
5
  // [import]
4
6
 
@@ -7,35 +9,24 @@ const path = require("node:path");
7
9
  // [import]
8
10
 
9
11
  const detect = require("./lockfile");
12
+ const io = require("./io");
10
13
 
11
14
  // [use]
12
15
 
13
16
  const dirname = path.dirname;
17
+ const fail = io.fail;
18
+ const run = io.run;
19
+ const write = io.write;
14
20
 
15
21
  // [define]
16
22
 
17
23
  const file = process.argv[2];
18
24
 
19
- // [function]
20
-
21
- const write = function (data) {
22
- process.stdout.write(data.trim().concat("\n"));
23
- };
24
-
25
- // [function]
26
-
27
- const writeErr = function (data) {
28
- process.stderr.write(data.trim().concat("\n"));
29
- };
30
-
31
25
  // [main]
32
26
 
33
- try {
27
+ run(() => {
34
28
  if (!file) {
35
- throw new Error("No file provided.");
29
+ fail("No manifest file provided.");
36
30
  }
37
31
  write(detect(dirname(file)));
38
- } catch (ex) {
39
- writeErr(ex.message);
40
- process.exit(1);
41
- }
32
+ });
@@ -1,39 +1,23 @@
1
1
  "use strict";
2
+ // arctool-put-deploy-dir <manifest>: deploy_dir, which arc inspect always
3
+ // gives, its default included.
2
4
 
3
5
  // [import]
4
6
 
5
- const fs = require("node:fs");
7
+ const io = require("./io");
6
8
 
7
9
  // [use]
8
10
 
9
- const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
11
+ const readManifest = io.readManifest;
12
+ const run = io.run;
13
+ const write = io.write;
10
14
 
11
15
  // [define]
12
16
 
13
17
  const file = process.argv[2];
14
18
 
15
- // [function]
16
-
17
- const write = function (data) {
18
- process.stdout.write(data.trim().concat("\n"));
19
- };
20
-
21
- // [function]
22
-
23
- const writeErr = function (data) {
24
- process.stderr.write(data.trim().concat("\n"));
25
- };
26
-
27
19
  // [main]
28
20
 
29
- try {
30
- if (!file) {
31
- throw new Error("No file provided.");
32
- }
33
- const buf = readFileSync(file, "utf8");
34
- const conf = JSON.parse(buf);
35
- write(conf?.workflow?.deploy_dir || ".arcadible-public");
36
- } catch (ex) {
37
- writeErr(ex.message);
38
- process.exit(1);
39
- }
21
+ run(() => {
22
+ write(readManifest(file)?.workflow?.deploy_dir ?? "");
23
+ });
@@ -1,48 +1,30 @@
1
1
  "use strict";
2
+ // arctool-put-prerelease <manifest>: whether the version is a prerelease,
3
+ // which goes to staging.
2
4
 
3
5
  // [import]
4
6
 
5
- const fs = require("node:fs");
7
+ const semver = require("semver");
6
8
 
7
9
  // [import]
8
10
 
9
- const semver = require("semver");
11
+ const io = require("./io");
10
12
 
11
13
  // [use]
12
14
 
13
15
  const prereleaseSemver = semver.prerelease;
14
- const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
16
+ const readManifest = io.readManifest;
17
+ const run = io.run;
15
18
  const validSemver = semver.valid;
19
+ const write = io.write;
16
20
 
17
21
  // [define]
18
22
 
19
23
  const file = process.argv[2];
20
24
 
21
- // [function]
22
-
23
- const write = function (data) {
24
- process.stdout.write(data.trim().concat("\n"));
25
- };
26
-
27
- // [function]
28
-
29
- const writeErr = function (data) {
30
- process.stderr.write(data.trim().concat("\n"));
31
- };
32
-
33
25
  // [main]
34
26
 
35
- try {
36
- if (!file) {
37
- throw new Error("No file provided.");
38
- }
39
- const buf = readFileSync(file, "utf8");
40
- const conf = JSON.parse(buf);
41
- const version = validSemver(conf?.game?.version);
42
- write(
43
- version !== null && prereleaseSemver(version) !== null ? "true" : "false",
44
- );
45
- } catch (ex) {
46
- writeErr(ex.message);
47
- process.exit(1);
48
- }
27
+ run(() => {
28
+ const version = validSemver(readManifest(file)?.game?.version);
29
+ write(version !== null && prereleaseSemver(version) !== null);
30
+ });
@@ -1,48 +1,33 @@
1
1
  "use strict";
2
+ // arctool-put-version <manifest>: the game's version.
2
3
 
3
4
  // [import]
4
5
 
5
- const fs = require("node:fs");
6
+ const semver = require("semver");
6
7
 
7
8
  // [import]
8
9
 
9
- const semver = require("semver");
10
+ const io = require("./io");
10
11
 
11
12
  // [use]
12
13
 
13
- const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
14
+ const fail = io.fail;
15
+ const readManifest = io.readManifest;
16
+ const run = io.run;
14
17
  const validSemver = semver.valid;
18
+ const write = io.write;
15
19
 
16
20
  // [define]
17
21
 
18
22
  const file = process.argv[2];
19
23
 
20
- // [function]
21
-
22
- const write = function (data) {
23
- process.stdout.write(data.trim().concat("\n"));
24
- };
25
-
26
- // [function]
27
-
28
- const writeErr = function (data) {
29
- process.stderr.write(data.trim().concat("\n"));
30
- };
31
-
32
24
  // [main]
33
25
 
34
- try {
35
- if (!file) {
36
- throw new Error("No file provided.");
37
- }
38
- const buf = readFileSync(file, "utf8");
39
- const conf = JSON.parse(buf);
40
- const version = validSemver(conf?.game?.version);
26
+ run(() => {
27
+ const value = readManifest(file)?.game?.version;
28
+ const version = validSemver(value);
41
29
  if (version === null) {
42
- throw new Error("Invalid semver.");
30
+ fail(`Invalid version: ${value ?? "not set"}`);
43
31
  }
44
32
  write(version);
45
- } catch (ex) {
46
- writeErr(ex.message);
47
- process.exit(1);
48
- }
33
+ });