arcadible 0.1.11 → 0.1.13

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.
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // [import]
5
+
6
+ const path = require("node:path");
7
+
8
+ // [import]
9
+
10
+ const hideBin = require("yargs/helpers").hideBin;
11
+ const yargs = require("yargs/yargs");
12
+
13
+ // [import]
14
+
15
+ const build = require("../src/cli/build");
16
+ const log = require("../src/util/log");
17
+ const output = require("../src/util/output");
18
+ const phrase = require("../src/phrase");
19
+ const pkg = require("../package.json");
20
+ const rc = require("../src/util/rc");
21
+ const validate = require("../src/cli/validate");
22
+
23
+ // [use]
24
+
25
+ const findRc = rc.findRc;
26
+ const print = output.print;
27
+ const resolve = path.resolve;
28
+
29
+ // [declare]
30
+
31
+ const CMD = "arc";
32
+ const SUBCMD = "build";
33
+
34
+ // [define]
35
+
36
+ const conf = {
37
+ json: false,
38
+ quiet: false,
39
+ ...findRc(CMD, SUBCMD, process.argv, ["--cwd", "-C"]),
40
+ };
41
+
42
+ // [function]
43
+
44
+ const start = function (argv) {
45
+ const json = argv.json;
46
+ const quiet = argv.quiet;
47
+ const root = resolve(process.cwd(), argv.cwd);
48
+ return validate({ root })
49
+ .then((issues) => {
50
+ const count = issues.length;
51
+ if (count) {
52
+ throw new Error(phrase.issuesFound(count));
53
+ }
54
+ return build({ root });
55
+ })
56
+ .then((artifact) => {
57
+ if (json) {
58
+ print({ artifact });
59
+ return;
60
+ }
61
+ if (!quiet) {
62
+ log.hs("Done.");
63
+ if (artifact) {
64
+ print(artifact, { toml: true });
65
+ return;
66
+ }
67
+ log.p("Nothing to do.");
68
+ }
69
+ })
70
+ .catch((ex) => {
71
+ const message = ex.message;
72
+ if (json) {
73
+ print({ error: { message } });
74
+ } else {
75
+ log.hd("Build aborted.");
76
+ log.p(message);
77
+ }
78
+ process.exit(1);
79
+ });
80
+ };
81
+
82
+ // [function]
83
+
84
+ const main = function (argv) {
85
+ return start(
86
+ yargs(hideBin(argv))
87
+ .scriptName(`${CMD} ${SUBCMD}`)
88
+ .usage("$0 [options]")
89
+ .option("json", {
90
+ alias: "j",
91
+ default: conf.json,
92
+ describe: "Format output as JSON",
93
+ type: "boolean",
94
+ })
95
+ .option("quiet", {
96
+ alias: "q",
97
+ default: conf.quiet,
98
+ describe: "Output only errors",
99
+ type: "boolean",
100
+ })
101
+ .option("cwd", {
102
+ alias: "C",
103
+ default: ".",
104
+ describe: "Set the working directory",
105
+ type: "string",
106
+ })
107
+ .help("help")
108
+ .alias("help", "h")
109
+ .version(pkg.version)
110
+ .alias("version", "v")
111
+ .strict()
112
+ .parse(),
113
+ );
114
+ };
115
+
116
+ // [main]
117
+
118
+ if (require.main === module) {
119
+ main(process.argv);
120
+ }
121
+
122
+ // [export]
123
+
124
+ module.exports = { main };
package/bin/arc-deploy.js CHANGED
@@ -12,9 +12,12 @@ const yargs = require("yargs/yargs");
12
12
 
13
13
  // [import]
14
14
 
15
+ const build = require("../src/cli/build");
15
16
  const deploy = require("../src/cli/deploy");
16
17
  const log = require("../src/util/log");
17
18
  const output = require("../src/util/output");
19
+ const pack = require("../src/cli/pack");
20
+ const phrase = require("../src/phrase");
18
21
  const pkg = require("../package.json");
19
22
  const rc = require("../src/util/rc");
20
23
  const validate = require("../src/cli/validate");
@@ -46,7 +49,7 @@ const handler = function ({ json, quiet }) {
46
49
  return {
47
50
  onBegin() {
48
51
  if (!json && !quiet) {
49
- log.p("Deploying project...");
52
+ log.pnl("Deploying...");
50
53
  }
51
54
  },
52
55
  onInfo({ level, message }) {
@@ -58,12 +61,12 @@ const handler = function ({ json, quiet }) {
58
61
  log.pw(`⚠ ${message}`);
59
62
  return;
60
63
  default:
61
- log.p(`• ${message}`);
64
+ log.p(`· ${message}`);
62
65
  }
63
66
  },
64
67
  onRefresh(refresh) {
65
68
  if (!json) {
66
- log.p("Checking login...");
69
+ // log.p("Checking login...");
67
70
  }
68
71
  return refresh(true);
69
72
  },
@@ -73,35 +76,66 @@ const handler = function ({ json, quiet }) {
73
76
  // [function]
74
77
 
75
78
  const start = function (argv) {
79
+ const git = argv.git;
76
80
  const json = argv.json;
77
81
  const quiet = argv.quiet;
78
82
  const root = resolve(process.cwd(), argv.cwd);
79
83
  return whoami({ handler: handler({ json, quiet }) })
80
84
  .then((current) => {
81
- if (!current || !current.profile) {
82
- throw new Error("Not logged in. Run `arc login` to log in.");
85
+ if (!current || !current.session) {
86
+ throw new Error(phrase.notLoggedIn());
83
87
  }
84
- return validate({ root });
88
+ return Promise.all([
89
+ // whoami
90
+ current.session,
91
+ // validate
92
+ validate({ root }),
93
+ ]);
85
94
  })
86
- .then((issues) => {
87
- const valid = !issues.length;
88
- if (!valid) {
89
- throw new Error("Validate failed. Run `arc validate` for details.");
95
+ .then(([session, issues]) => {
96
+ const count = issues.length;
97
+ if (count) {
98
+ throw new Error(phrase.issuesFound(count));
90
99
  }
91
- return deploy({
92
- git: argv.git,
93
- handler: handler({ json, quiet }),
94
- root,
95
- });
100
+ return Promise.all([
101
+ // whoami
102
+ session,
103
+ // build
104
+ git ? null : build({ root }),
105
+ ]);
96
106
  })
97
- .then((data) => {
107
+ .then(([session, artifact]) =>
108
+ Promise.all([
109
+ // whoami
110
+ session,
111
+ // pack
112
+ git ? null : pack({ input: artifact?.path, root }),
113
+ ]),
114
+ )
115
+ .then(([session, artifact]) =>
116
+ deploy(
117
+ git
118
+ ? { handler: handler({ json, quiet }), root }
119
+ : {
120
+ artifact,
121
+ handler: handler({ json, quiet }),
122
+ root,
123
+ session,
124
+ },
125
+ ),
126
+ )
127
+ .then((version) => {
98
128
  if (json) {
99
- print({ version: data });
129
+ print({ version });
100
130
  return;
101
131
  }
102
132
  if (!quiet) {
103
- log.p(`✓ Build v${data.to} submitted for release.`);
104
- log.hs("Deployed.");
133
+ log.hs("Done.");
134
+ if (version) {
135
+ log.p(`Build v${version.to} submitted.`);
136
+ return;
137
+ }
138
+ log.p("Nothing to do.");
105
139
  }
106
140
  })
107
141
  .catch((ex) => {
package/bin/arc-init.js CHANGED
@@ -53,7 +53,7 @@ const handler = function ({ json, quiet }) {
53
53
  return {
54
54
  onBegin() {
55
55
  if (!json && !quiet) {
56
- log.p("Initializing project...");
56
+ log.pnl("Initializing...");
57
57
  }
58
58
  },
59
59
  onInfo() {},
@@ -74,26 +74,25 @@ const start = function (argv) {
74
74
  root,
75
75
  template: argv.template,
76
76
  })
77
- .then((data) => {
78
- const dir = relative(root, data.path);
77
+ .then((project) => {
79
78
  if (json) {
80
- print({ project: data });
79
+ print({ project });
81
80
  return;
82
81
  }
83
82
  if (!quiet) {
84
- log.hsnl("Initialized.");
85
- log.h("Getting started:");
86
- log.pnl(cfgurl.GETTING_STARTED);
83
+ log.hsnl("Done.");
87
84
  log.h("Next steps:");
88
- log.p(
85
+ log.pnl(
89
86
  [
90
- data.path === root ? null : `cd ${dir}`,
87
+ project.path === root ? null : `cd ${relative(root, project.path)}`,
91
88
  "arc login",
92
89
  "arc deploy (or: arc deploy --git)",
93
90
  ]
94
91
  .filter(Boolean)
95
92
  .map((step, i) => `${i + 1}. ${step}`),
96
93
  );
94
+ log.h("Getting started:");
95
+ log.p(cfgurl.GETTING_STARTED);
97
96
  }
98
97
  // degit sometimes hangs
99
98
  process.exit(0);
@@ -153,7 +152,7 @@ const main = function (argv) {
153
152
  .option("yes", {
154
153
  alias: "y",
155
154
  default: conf.yes,
156
- describe: "Accept all prompt defaults",
155
+ describe: "Accept prompt defaults",
157
156
  type: "boolean",
158
157
  })
159
158
  .option("json", {
package/bin/arc-login.js CHANGED
@@ -79,13 +79,13 @@ const handler = function ({ browser }) {
79
79
 
80
80
  const start = function (argv) {
81
81
  const browser = argv.browser;
82
- log.pnl("Starting login...");
82
+ log.pnl("Logging in...");
83
83
  return login({ handler: handler({ browser }) })
84
84
  .then(({ profile }) => {
85
85
  process.stdin.pause();
86
86
  log.p("");
87
- log.p("Logged in as:");
88
- log.hs(profile.display_name);
87
+ log.hs("Done.");
88
+ log.p(profile.display_name);
89
89
  })
90
90
  .catch((ex) => {
91
91
  process.stdin.pause();
@@ -105,7 +105,7 @@ const main = function (argv) {
105
105
  .option("browser", {
106
106
  default: conf.browser,
107
107
  describe:
108
- "Open a web browser automatically (use --no-browser to disable)",
108
+ "Open a web browser automatically (disable with --no-browser)",
109
109
  type: "boolean",
110
110
  })
111
111
  .help("help")
package/bin/arc-pack.js CHANGED
@@ -15,6 +15,7 @@ const yargs = require("yargs/yargs");
15
15
  const log = require("../src/util/log");
16
16
  const output = require("../src/util/output");
17
17
  const pack = require("../src/cli/pack");
18
+ const phrase = require("../src/phrase");
18
19
  const pkg = require("../package.json");
19
20
  const rc = require("../src/util/rc");
20
21
  const validate = require("../src/cli/validate");
@@ -33,6 +34,7 @@ const SUBCMD = "pack";
33
34
  // [define]
34
35
 
35
36
  const conf = {
37
+ input: null,
36
38
  json: false,
37
39
  output: null,
38
40
  quiet: false,
@@ -47,19 +49,24 @@ const start = function (argv) {
47
49
  const root = resolve(process.cwd(), argv.cwd);
48
50
  return validate({ root })
49
51
  .then((issues) => {
50
- const valid = !issues.length;
51
- if (!valid) {
52
- throw new Error("Validate failed. Run `arc validate` for details.");
52
+ const count = issues.length;
53
+ if (count) {
54
+ throw new Error(phrase.issuesFound(count));
53
55
  }
54
- return pack({ output: argv.output, root });
56
+ return pack({ input: argv.input, output: argv.output, root });
55
57
  })
56
- .then((data) => {
58
+ .then((artifact) => {
57
59
  if (json) {
58
- print({ artifact: data });
60
+ print({ artifact });
59
61
  return;
60
62
  }
61
63
  if (!quiet) {
62
- log.hs("Packed.");
64
+ log.hs("Done.");
65
+ if (artifact) {
66
+ print(artifact, { toml: true });
67
+ return;
68
+ }
69
+ log.p("Nothing to do.");
63
70
  }
64
71
  })
65
72
  .catch((ex) => {
@@ -81,10 +88,16 @@ const main = function (argv) {
81
88
  yargs(hideBin(argv))
82
89
  .scriptName(`${CMD} ${SUBCMD}`)
83
90
  .usage("$0 [options]")
91
+ .option("input", {
92
+ alias: "i",
93
+ default: conf.input,
94
+ describe: "Set the input directory",
95
+ type: "string",
96
+ })
84
97
  .option("output", {
85
98
  alias: "o",
86
99
  default: conf.output,
87
- describe: "Set the output path",
100
+ describe: "Set the output file",
88
101
  type: "string",
89
102
  })
90
103
  .option("json", {
@@ -51,7 +51,7 @@ const start = function (argv) {
51
51
  log.hd(`${count} ${count === 1 ? "issue" : "issues"} found.`);
52
52
  log.p(issues.map((error) => `✗ ${error}`));
53
53
  } else if (!quiet) {
54
- log.hs("Validated.");
54
+ log.hs("No issues found.");
55
55
  }
56
56
  process.exit(count ? 1 : 0);
57
57
  })
package/bin/arc-whoami.js CHANGED
@@ -37,7 +37,7 @@ const handler = function ({ json }) {
37
37
  return {
38
38
  onRefresh(refresh) {
39
39
  if (!json) {
40
- log.p("Checking login...");
40
+ // log.p("Checking login...");
41
41
  }
42
42
  return refresh(true);
43
43
  },
@@ -62,8 +62,7 @@ const start = function (argv) {
62
62
  if (json) {
63
63
  print({ profile });
64
64
  } else {
65
- log.p("Logged in as:");
66
- log.hs(profile.display_name);
65
+ log.p(profile.display_name);
67
66
  }
68
67
  })
69
68
  .catch((ex) => {
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "bin": {
3
3
  "arc": "bin/arc.js",
4
+ "arc-build": "bin/arc-build.js",
4
5
  "arc-deploy": "bin/arc-deploy.js",
5
6
  "arc-init": "bin/arc-init.js",
6
7
  "arc-inspect": "bin/arc-inspect.js",
@@ -11,7 +12,6 @@
11
12
  "arc-whoami": "bin/arc-whoami.js"
12
13
  },
13
14
  "dependencies": {
14
- "@iarna/toml": "^2.2.5",
15
15
  "@inquirer/prompts": "^8.5.2",
16
16
  "adm-zip": "^0.5.16",
17
17
  "chalk": "^4.1.2",
@@ -25,6 +25,7 @@
25
25
  "marked": "^17.0.1",
26
26
  "mp4box": "^2.3.0",
27
27
  "open": "^8.4.2",
28
+ "pretty-toml": "^1.0.0",
28
29
  "rc": "^1.2.8",
29
30
  "semver": "^7.7.3",
30
31
  "yargs": "^17.7.2",
@@ -35,6 +36,7 @@
35
36
  "node": ">=24.11.1"
36
37
  },
37
38
  "exports": {
39
+ "./build": "./src/cli/build.js",
38
40
  "./deploy": "./src/cli/deploy.js",
39
41
  "./init": "./src/cli/init.js",
40
42
  "./inspect": "./src/cli/inspect.js",
@@ -51,5 +53,5 @@
51
53
  "license": "Apache-2.0",
52
54
  "main": "./src/cli/index.js",
53
55
  "name": "arcadible",
54
- "version": "0.1.11"
56
+ "version": "0.1.13"
55
57
  }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const cp = require("node:child_process");
6
+ const path = require("node:path");
7
+
8
+ // [import]
9
+
10
+ const fs = require("fs-extra");
11
+
12
+ // [import]
13
+
14
+ const project = require("../../parser/project");
15
+
16
+ // [use]
17
+
18
+ const exec = cp.exec;
19
+ const pathExists = fs.pathExists;
20
+ const remove = fs.remove;
21
+ const resolvePath = path.resolve;
22
+
23
+ // [function]
24
+
25
+ const execBuild = function (command, { cwdPath }) {
26
+ return new Promise((resolve, reject) => {
27
+ exec(command, { cwd: cwdPath }, (err) => {
28
+ if (err) {
29
+ reject(err);
30
+ return;
31
+ }
32
+ resolve();
33
+ });
34
+ });
35
+ };
36
+
37
+ // [function]
38
+
39
+ const build = function (command, { cwdPath, outputPath }) {
40
+ return command
41
+ ? remove(outputPath)
42
+ .then(() => execBuild(command, { cwdPath }))
43
+ .then(() => pathExists(outputPath))
44
+ .then((exists) => (exists ? { path: outputPath } : null))
45
+ : Promise.resolve(null);
46
+ };
47
+
48
+ // [function]
49
+
50
+ const main = function ({ root }) {
51
+ return project(root).then(({ workflow }) => {
52
+ const command = workflow.build;
53
+ return build(
54
+ Array.isArray(command)
55
+ ? command
56
+ .map((cmd) => cmd.trim())
57
+ .filter(Boolean)
58
+ .join(" && ")
59
+ : command.trim(),
60
+ {
61
+ cwdPath: root,
62
+ outputPath: resolvePath(root, workflow.deploy_dir),
63
+ },
64
+ );
65
+ });
66
+ };
67
+
68
+ // [export]
69
+
70
+ module.exports = main;
@@ -8,6 +8,10 @@ const cp = require("node:child_process");
8
8
 
9
9
  const semver = require("semver");
10
10
 
11
+ // [import]
12
+
13
+ const phrase = require("../../phrase");
14
+
11
15
  // [use]
12
16
 
13
17
  const execFile = cp.execFile;
@@ -26,13 +30,9 @@ const git = function (root, args, cb) {
26
30
  // [function]
27
31
 
28
32
  const ensureRepo = function (root, cb) {
29
- const message = [
30
- "Unable to find a Git repository.",
31
- "Ensure you are in a Git repository, or create one before continuing.",
32
- ];
33
33
  git(root, ["rev-parse", "--is-inside-work-tree"], (err) => {
34
34
  if (err) {
35
- cb(new Error(message.join("\n"), { cause: err }));
35
+ cb(new Error(phrase.gitRepoMissing(), { cause: err }));
36
36
  return;
37
37
  }
38
38
  cb();
@@ -42,13 +42,9 @@ const ensureRepo = function (root, cb) {
42
42
  // [function]
43
43
 
44
44
  const ensureRemote = function (root, cb) {
45
- const message = [
46
- "Unable to access Git remote 'origin'.",
47
- "Ensure the remote 'origin' exists and you have permission to access it.",
48
- ];
49
45
  git(root, ["ls-remote", "origin"], (err) => {
50
46
  if (err) {
51
- cb(new Error(message.join("\n"), { cause: err }));
47
+ cb(new Error(phrase.gitRemoteMissing(), { cause: err }));
52
48
  return;
53
49
  }
54
50
  cb();
@@ -58,14 +54,10 @@ const ensureRemote = function (root, cb) {
58
54
  // [function]
59
55
 
60
56
  const ensureClean = function (root, cb) {
61
- const message = [
62
- "Unable to continue with Git changes present.",
63
- "Ensure all changes are staged, committed, or discarded before continuing.",
64
- ];
65
57
  git(root, ["diff", "--quiet"], (err) => {
66
58
  if (err) {
67
59
  if (err.code === 1) {
68
- cb(new Error(message.join("\n"), { cause: err }));
60
+ cb(new Error(phrase.gitChangesPresent(), { cause: err }));
69
61
  return;
70
62
  }
71
63
  cb(err);
@@ -88,10 +80,6 @@ const ensureClean = function (root, cb) {
88
80
  // [function]
89
81
 
90
82
  const createTag = function (root, version, cb) {
91
- const message = [
92
- "Unable to create Git tag.",
93
- "Ensure the release tag does not already exist locally.",
94
- ];
95
83
  git(
96
84
  root,
97
85
  [
@@ -105,7 +93,7 @@ const createTag = function (root, version, cb) {
105
93
  ],
106
94
  (err) => {
107
95
  if (err) {
108
- cb(new Error(message.join("\n"), { cause: err }));
96
+ cb(new Error(phrase.gitTagCreateFailed(), { cause: err }));
109
97
  return;
110
98
  }
111
99
  cb();
@@ -116,13 +104,9 @@ const createTag = function (root, version, cb) {
116
104
  // [function]
117
105
 
118
106
  const pushTag = function (root, version, cb) {
119
- const message = [
120
- "Unable to push Git tag.",
121
- "Ensure the release tag does not already exist on remote 'origin'.",
122
- ];
123
107
  git(root, ["push", "origin", `${PREFIX}${version}`], (err) => {
124
108
  if (err) {
125
- cb(new Error(message.join("\n"), { cause: err }));
109
+ cb(new Error(phrase.gitTagPushFailed(), { cause: err }));
126
110
  return;
127
111
  }
128
112
  cb();
@@ -11,6 +11,7 @@ const fs = require("fs-extra");
11
11
 
12
12
  // [import]
13
13
 
14
+ const phrase = require("../../phrase");
14
15
  const TOML = require("../../util/toml");
15
16
 
16
17
  // [use]
@@ -31,9 +32,7 @@ const parseToml = function (buf) {
31
32
  try {
32
33
  return TOML.parse(buf);
33
34
  } catch (ex) {
34
- throw new Error(`Unable to parse ${MANIFEST}: ${ex.message}`, {
35
- cause: ex,
36
- });
35
+ throw new Error(phrase.unableToParse(MANIFEST, ex.message), { cause: ex });
37
36
  }
38
37
  };
39
38
 
@@ -16,8 +16,8 @@ const resolve = path.resolve;
16
16
 
17
17
  // [function]
18
18
 
19
- const detectVersion = function (root) {
20
- return env(root).then(({ game }) => game?.version ?? null);
19
+ const detectVersion = function (dest) {
20
+ return env(dest).then(({ game }) => game?.version ?? null);
21
21
  };
22
22
 
23
23
  // [function]
@@ -13,6 +13,7 @@ const fs = require("fs-extra");
13
13
 
14
14
  const env = require("../../parser/env");
15
15
  const manifest = require("./manifest");
16
+ const phrase = require("../../phrase");
16
17
 
17
18
  // [use]
18
19
 
@@ -34,8 +35,8 @@ const removeLicense = function (dest) {
34
35
 
35
36
  // [function]
36
37
 
37
- const detectVersion = function (root) {
38
- return env(root).then(({ game }) => game?.version ?? null);
38
+ const detectVersion = function (dest) {
39
+ return env(dest).then(({ game }) => game?.version ?? null);
39
40
  };
40
41
 
41
42
  // [function]
@@ -52,7 +53,7 @@ const scaffold = function (src, dest, { onBegin, onInfo }) {
52
53
  const init = function (dest, { force, template }, handler) {
53
54
  return pathExists(dest).then((exists) => {
54
55
  if (exists && !force) {
55
- throw new Error("Directory already exists. Use `--force` to overwrite.");
56
+ throw new Error(phrase.overwriteWithForce());
56
57
  }
57
58
  return scaffold(template, dest, handler);
58
59
  });
@@ -9,6 +9,7 @@ const crypto = require("node:crypto");
9
9
  const api = require("../../api");
10
10
  const cfgurl = require("../../config/url");
11
11
  const delay = require("../../util/delay");
12
+ const phrase = require("../../phrase");
12
13
  const time = require("../../util/time");
13
14
 
14
15
  // [use]
@@ -16,6 +17,16 @@ const time = require("../../util/time");
16
17
  const poll = delay.poll;
17
18
  const randomUUID = crypto.randomUUID;
18
19
 
20
+ // [declare]
21
+
22
+ const POLL = {
23
+ delay: time.s(3),
24
+ factor: 2,
25
+ interval: time.s(30),
26
+ maxDelay: time.s(15),
27
+ timeoutError: phrase.loginExpired(),
28
+ };
29
+
19
30
  // [function]
20
31
 
21
32
  const postFlow = function ({ expiresAt, token, type }) {
@@ -56,9 +67,9 @@ const pollFlow = function ({ id, token }, onPoll) {
56
67
  case "completed":
57
68
  return data;
58
69
  case "failed":
59
- throw new Error("Login flow failed.");
70
+ throw new Error(phrase.loginFailed());
60
71
  default:
61
- throw new Error("Login flow expired.");
72
+ throw new Error(phrase.loginExpired());
62
73
  }
63
74
  });
64
75
  };
@@ -80,13 +91,7 @@ const main = function ({ onCreated, onPoll }) {
80
91
  return poll(
81
92
  (flow) => pollFlow(flow, onPoll),
82
93
  { id, token },
83
- {
84
- delay: time.s(3),
85
- factor: 2,
86
- interval: time.s(30),
87
- maxDelay: time.s(15),
88
- timeout,
89
- },
94
+ { ...POLL, timeout },
90
95
  );
91
96
  });
92
97
  };
@@ -10,6 +10,7 @@ const fs = require("fs-extra");
10
10
 
11
11
  // [import]
12
12
 
13
+ const phrase = require("../../phrase");
13
14
  const project = require("../../parser/project");
14
15
  const size = require("../../util/size");
15
16
  const Zip = require("../../util/zip");
@@ -36,11 +37,11 @@ const SIZE_TOTAL = size.mb(256);
36
37
 
37
38
  const verifyPartial = function (zip) {
38
39
  if (!zip.has(MAIN)) {
39
- throw new Error(`Package is missing "${MAIN}".`);
40
+ throw new Error(phrase.packageFileMissing(MAIN));
40
41
  }
41
42
  if (zip.stat().size > SIZE_PARTIAL) {
42
43
  const max = SIZE_PARTIAL / 1024 / 1024;
43
- throw new Error(`Package size (excluding store assets) exceeds ${max} MB.`);
44
+ throw new Error(phrase.packageTooLarge(false, max));
44
45
  }
45
46
  return zip;
46
47
  };
@@ -50,19 +51,19 @@ const verifyPartial = function (zip) {
50
51
  const verifyTotal = function (zip) {
51
52
  if (zip.stat().size > SIZE_TOTAL) {
52
53
  const max = SIZE_TOTAL / 1024 / 1024;
53
- throw new Error(`Package size (including store assets) exceeds ${max} MB.`);
54
+ throw new Error(phrase.packageTooLarge(true, max));
54
55
  }
55
56
  return zip;
56
57
  };
57
58
 
58
59
  // [function]
59
60
 
60
- const pack = function (name, game, { deployPath, mediaPath, zipPath }) {
61
+ const pack = function (name, game, { inputPath, mediaPath, outputPath }) {
61
62
  const zip = new Zip(name);
62
- return pathExists(deployPath)
63
+ return pathExists(inputPath)
63
64
  .then((existsDeploy) => {
64
65
  if (existsDeploy) {
65
- zip.addDir(deployPath);
66
+ zip.addDir(inputPath);
66
67
  }
67
68
  return zip;
68
69
  })
@@ -77,21 +78,21 @@ const pack = function (name, game, { deployPath, mediaPath, zipPath }) {
77
78
  })
78
79
  .then(verifyTotal)
79
80
  .then((file) => {
80
- file.write(zipPath);
81
- return { path: zipPath, ...file.stat() };
81
+ file.write(outputPath);
82
+ return { path: outputPath, ...file.stat() };
82
83
  });
83
84
  };
84
85
 
85
86
  // [function]
86
87
 
87
- const main = function ({ output, root }) {
88
+ const main = function ({ input, output, root }) {
88
89
  return project(root).then(({ game, workflow }) => {
89
- const name = game.handle || game.id;
90
- const file = output || `${OUTPUT}/${name}-v${game.version}.zip`;
90
+ const name = game.handle ?? game.id;
91
+ const file = output ?? `${OUTPUT}/${name}-v${game.version}.zip`;
91
92
  return pack(parse(file).name, game, {
92
- deployPath: resolve(root, workflow.deploy_dir),
93
+ inputPath: resolve(root, input ?? workflow.deploy_dir),
93
94
  mediaPath: resolve(root, MEDIA),
94
- zipPath: resolve(root, file),
95
+ outputPath: resolve(root, file),
95
96
  });
96
97
  });
97
98
  };
package/src/api.js CHANGED
@@ -34,6 +34,12 @@ const postFlow = function (form, options) {
34
34
  return request.post(endpoint("/flows"), form, options);
35
35
  };
36
36
 
37
+ // [function]
38
+
39
+ const postUpload = function (form, options) {
40
+ return request.post(endpoint("/uploads"), form, options);
41
+ };
42
+
37
43
  // [export]
38
44
 
39
- module.exports = { authRefresh, getFlow, postFlow };
45
+ module.exports = { authRefresh, getFlow, postFlow, postUpload };
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ // [import]
4
+
5
+ const buildTool = require("../action/build/tool");
6
+
7
+ // [function]
8
+
9
+ const main = function (conf) {
10
+ return buildTool(conf);
11
+ };
12
+
13
+ // [export]
14
+
15
+ module.exports = main;
package/src/cli/deploy.js CHANGED
@@ -7,9 +7,10 @@ const deployToolGit = require("../action/deploy/tool-git");
7
7
 
8
8
  // [function]
9
9
 
10
- const main = function ({ git, handler, ...conf }) {
11
- const tool = git ? deployToolGit : deployTool;
12
- return tool(conf, handler);
10
+ const main = function ({ artifact, handler, session, ...conf }) {
11
+ return artifact
12
+ ? deployTool(session, artifact, conf, handler)
13
+ : deployToolGit(conf, handler);
13
14
  };
14
15
 
15
16
  // [export]
package/src/cli/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // [import]
4
4
 
5
+ const build = require("./build");
5
6
  const deploy = require("./deploy");
6
7
  const init = require("./init");
7
8
  const inspect = require("./inspect");
@@ -14,6 +15,7 @@ const whoami = require("./whoami");
14
15
  // [export]
15
16
 
16
17
  module.exports = {
18
+ build,
17
19
  deploy,
18
20
  init,
19
21
  inspect,
package/src/parser/env.js CHANGED
@@ -11,6 +11,7 @@ const fs = require("fs-extra");
11
11
  // [import]
12
12
 
13
13
  const objs = require("../util/objs");
14
+ const phrase = require("../phrase");
14
15
 
15
16
  // [use]
16
17
 
@@ -29,9 +30,7 @@ const parseJson = function (buf) {
29
30
  try {
30
31
  return JSON.parse(buf);
31
32
  } catch (ex) {
32
- throw new Error(`Unable to parse ${PACKAGE}: ${ex.message}`, {
33
- cause: ex,
34
- });
33
+ throw new Error(phrase.unableToParse(PACKAGE, ex.message), { cause: ex });
35
34
  }
36
35
  };
37
36
 
@@ -11,6 +11,7 @@ const fs = require("fs-extra");
11
11
  // [import]
12
12
 
13
13
  const objs = require("../util/objs");
14
+ const phrase = require("../phrase");
14
15
  const TOML = require("../util/toml");
15
16
 
16
17
  // [use]
@@ -30,9 +31,7 @@ const parseToml = function (buf) {
30
31
  try {
31
32
  return TOML.parse(buf);
32
33
  } catch (ex) {
33
- throw new Error(`Unable to parse ${MANIFEST}: ${ex.message}`, {
34
- cause: ex,
35
- });
34
+ throw new Error(phrase.unableToParse(MANIFEST, ex.message), { cause: ex });
36
35
  }
37
36
  };
38
37
 
@@ -13,6 +13,7 @@ const fs = require("fs-extra");
13
13
  const dom = require("../util/dom");
14
14
  const MD = require("../util/md");
15
15
  const objs = require("../util/objs");
16
+ const phrase = require("../phrase");
16
17
  const strs = require("../util/strs");
17
18
 
18
19
  // [use]
@@ -123,9 +124,7 @@ const parseReadme = function (buf, file) {
123
124
  try {
124
125
  return extract(MD.parse(buf));
125
126
  } catch (ex) {
126
- throw new Error(`Unable to parse ${file}: ${ex.message}`, {
127
- cause: ex,
128
- });
127
+ throw new Error(phrase.unableToParse(file, ex.message), { cause: ex });
129
128
  }
130
129
  };
131
130
 
package/src/phrase.js ADDED
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+
3
+ // [function]
4
+
5
+ const gitChangesPresent = function () {
6
+ return [
7
+ "Unable to continue with Git changes present.",
8
+ "Ensure all changes are staged, committed, or discarded before continuing.",
9
+ ].join("\n");
10
+ };
11
+
12
+ // [function]
13
+
14
+ const gitRemoteMissing = function () {
15
+ return [
16
+ "Unable to access Git remote 'origin'.",
17
+ "Ensure the remote 'origin' exists and you have permission to access it.",
18
+ ].join("\n");
19
+ };
20
+
21
+ // [function]
22
+
23
+ const gitRepoMissing = function () {
24
+ return [
25
+ "Unable to find a Git repository.",
26
+ "Ensure you are in a Git repository, or create one before continuing.",
27
+ ].join("\n");
28
+ };
29
+
30
+ // [function]
31
+
32
+ const gitTagCreateFailed = function () {
33
+ return [
34
+ "Unable to create Git tag.",
35
+ "Ensure the version tag does not already exist locally.",
36
+ ].join("\n");
37
+ };
38
+
39
+ // [function]
40
+
41
+ const gitTagPushFailed = function () {
42
+ return [
43
+ "Unable to push Git tag.",
44
+ "Ensure the version tag does not already exist on remote 'origin'.",
45
+ ].join("\n");
46
+ };
47
+
48
+ // [function]
49
+
50
+ const issuesFound = function (count) {
51
+ return `${count} ${count === 1 ? "issue" : "issues"} found. Run \`arc validate\` for details.`;
52
+ };
53
+
54
+ // [function]
55
+
56
+ const loginExpired = function () {
57
+ return "Login request expired.";
58
+ };
59
+
60
+ // [function]
61
+
62
+ const loginFailed = function () {
63
+ return "Login request failed.";
64
+ };
65
+
66
+ // [function]
67
+
68
+ const notLoggedIn = function () {
69
+ return "Not logged in. Run `arc login` to log in.";
70
+ };
71
+
72
+ // [function]
73
+
74
+ const noVideoMetadata = function () {
75
+ return "No video metadata found.";
76
+ };
77
+
78
+ // [function]
79
+
80
+ const noVideoTrack = function () {
81
+ return "No video track found.";
82
+ };
83
+
84
+ // [function]
85
+
86
+ const overwriteWithForce = function () {
87
+ return "Directory already exists (overwrite with `--force`).";
88
+ };
89
+
90
+ // [function]
91
+
92
+ const packageFileMissing = function (file) {
93
+ return `Package is missing "${file}".`;
94
+ };
95
+
96
+ // [function]
97
+
98
+ const packageTooLarge = function (size, assets) {
99
+ return `Package size (${assets ? "including" : "excluding"} store assets) exceeds ${size} MB.`;
100
+ };
101
+
102
+ // [function]
103
+
104
+ const timedOut = function () {
105
+ return "Timed out.";
106
+ };
107
+
108
+ // [function]
109
+
110
+ const unableToParse = function (file, message) {
111
+ return `Unable to parse ${file}: ${message}`;
112
+ };
113
+
114
+ // [export]
115
+
116
+ module.exports = {
117
+ gitChangesPresent,
118
+ gitRemoteMissing,
119
+ gitRepoMissing,
120
+ gitTagCreateFailed,
121
+ gitTagPushFailed,
122
+ issuesFound,
123
+ loginExpired,
124
+ loginFailed,
125
+ noVideoMetadata,
126
+ noVideoTrack,
127
+ notLoggedIn,
128
+ overwriteWithForce,
129
+ packageFileMissing,
130
+ packageTooLarge,
131
+ timedOut,
132
+ unableToParse,
133
+ };
@@ -22,6 +22,7 @@ const strs = require("../util/strs");
22
22
 
23
23
  // [use]
24
24
 
25
+ const cleanSemver = semver.clean;
25
26
  const existsCheck = check.exists;
26
27
  const imageCheck = check.image;
27
28
  const indentLines = strs.indentLines;
@@ -303,14 +304,14 @@ const zId = function () {
303
304
  return z
304
305
  .string(
305
306
  message(field, [
306
- "Must be a valid UUID.",
307
- "Run `arc init` to generate a new game ID.",
307
+ "Must be defined.",
308
+ "Run `arc init` to generate a new ID.",
308
309
  ]),
309
310
  )
310
311
  .uuid(message(field, ["Must be a valid UUID."]))
311
312
  .refine(
312
313
  (value) => value === value.toLowerCase(),
313
- message(field, ["Must use lowercase characters."]),
314
+ message(field, ["Must be lowercase."]),
314
315
  );
315
316
  };
316
317
 
@@ -406,13 +407,17 @@ const zVersion = function () {
406
407
  return z
407
408
  .string(
408
409
  message(field, [
409
- "Must be valid semver.",
410
+ "Must be defined.",
410
411
  "A version is required unless one can be derived.",
411
412
  ]),
412
413
  )
413
414
  .refine(
414
415
  (value) => validSemver(value) !== null,
415
416
  message(field, ["Must be valid semver."]),
417
+ )
418
+ .refine(
419
+ (value) => cleanSemver(value) === value,
420
+ message(field, ["Must be normalized semver."]),
416
421
  );
417
422
  };
418
423
 
@@ -54,7 +54,7 @@ const zDeployDir = function (root, { build }) {
54
54
  const field = "deploy_dir";
55
55
  return z
56
56
  .string()
57
- .min(1)
57
+ .min(1, message(field, ["Must be defined."]))
58
58
  .refine(
59
59
  (file) => isRelativeCheck(root, file),
60
60
  message(field, ["Must be a relative path."]),
@@ -70,8 +70,8 @@ const zDeployDir = function (root, { build }) {
70
70
  .refine(
71
71
  (file) => build || existsCheck(root, file),
72
72
  message(field, [
73
- "Must exist when there is no build step.",
74
- "Run `arc inspect workflow.deploy_dir` for details.",
73
+ "Must exist when no build step is defined.",
74
+ "Run `arc inspect workflow` for details.",
75
75
  ]),
76
76
  );
77
77
  };
package/src/util/delay.js CHANGED
@@ -2,8 +2,20 @@
2
2
 
3
3
  // [import]
4
4
 
5
+ const phrase = require("../phrase");
5
6
  const time = require("./time");
6
7
 
8
+ // [declare]
9
+
10
+ const POLL = {
11
+ delay: time.s(1),
12
+ factor: 1,
13
+ interval: time.m(1),
14
+ maxDelay: time.s(1),
15
+ timeout: Infinity,
16
+ timeoutError: phrase.timedOut(),
17
+ };
18
+
7
19
  // [function]
8
20
 
9
21
  const backoff = function (elapsed, delay, factor, interval, maxDelay) {
@@ -30,13 +42,7 @@ const wait = function (delay, next) {
30
42
  const poll = function (
31
43
  fn,
32
44
  value,
33
- {
34
- delay = time.s(1),
35
- factor = 1,
36
- interval = time.m(1),
37
- maxDelay = delay,
38
- timeout = Infinity,
39
- } = {},
45
+ { delay, factor, interval, maxDelay, timeout, timeoutError } = POLL,
40
46
  ) {
41
47
  const started = Date.now();
42
48
  const next = function () {
@@ -46,7 +52,7 @@ const poll = function (
46
52
  }
47
53
  const elapsed = Date.now() - started;
48
54
  if (elapsed >= timeout) {
49
- throw new Error("Operation timed out.");
55
+ throw new Error(timeoutError);
50
56
  }
51
57
  return wait(backoff(elapsed, delay, factor, interval, maxDelay), next);
52
58
  });
package/src/util/s3.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";
package/src/util/toml.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // [import]
4
4
 
5
- const TOML = require("@iarna/toml");
5
+ const TOML = require("pretty-toml");
6
6
 
7
7
  // [use]
8
8
 
package/src/util/video.js CHANGED
@@ -5,6 +5,10 @@
5
5
  const createFile = require("mp4box").createFile;
6
6
  const fs = require("fs-extra");
7
7
 
8
+ // [import]
9
+
10
+ const phrase = require("../phrase");
11
+
8
12
  // [use]
9
13
 
10
14
  const readFileSync = fs.readFileSync; // eslint-disable-line no-sync
@@ -47,7 +51,7 @@ const readMp4 = function (file) {
47
51
  mp4.onReady = function (info) {
48
52
  const track = info.tracks.find((t) => t.video);
49
53
  if (!track || !track.video) {
50
- error = new Error("No video track found.");
54
+ error = new Error(phrase.noVideoTrack());
51
55
  return;
52
56
  }
53
57
  video = {
@@ -70,7 +74,7 @@ const readMp4 = function (file) {
70
74
  throw error;
71
75
  }
72
76
  if (!video) {
73
- throw new Error("No video metadata found.");
77
+ throw new Error(phrase.noVideoMetadata());
74
78
  }
75
79
  return video;
76
80
  };