arcadible 1.0.2 → 1.0.5
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/bin/arc-build.js +7 -22
- package/bin/arc-default.js +8 -8
- package/bin/arc-deploy.js +20 -22
- package/bin/arc-init.js +4 -3
- package/bin/arc-login.js +2 -2
- package/bin/arc-pack.js +8 -22
- package/bin/arc-play.js +1 -1
- package/bin/arc-serve.js +4 -3
- package/bin/arc-validate.js +2 -1
- package/bin/arc-whoami.js +1 -1
- package/package.json +1 -1
- package/src/account.js +3 -1
- package/src/action/build/tool.js +7 -3
- package/src/action/deploy/tool-git.js +8 -3
- package/src/action/deploy/tool.js +5 -4
- package/src/action/init/tool.js +11 -3
- package/src/action/pack/tool.js +11 -4
- package/src/action/predeploy/ac.js +4 -1
- package/src/action/predeploy/git.js +2 -2
- package/src/action/serve/tool.js +19 -3
- package/src/action/validate/checked.js +37 -0
- package/src/api.js +28 -13
- package/src/config/code.js +2 -2
- package/src/parser/readme.js +1 -1
- package/src/phrase.js +123 -62
- package/src/schema/game.js +32 -20
- package/src/schema/readme.js +2 -1
- package/src/schema/workflow.js +8 -10
- package/src/serve/game.js +7 -1
- package/src/serve/page.js +13 -13
- package/src/serve/reload.js +5 -0
- package/src/util/log.js +9 -2
- package/src/util/md.js +1 -1
- package/src/util/output.js +17 -2
- package/src/util/rc.js +23 -1
- package/src/util/s3.js +22 -3
- package/src/util/schema.js +53 -1
package/bin/arc-build.js
CHANGED
|
@@ -17,19 +17,16 @@ const build = require("../src/cli/build");
|
|
|
17
17
|
const cfgcode = require("../src/config/code");
|
|
18
18
|
const cfgpackage = require("../src/config/package");
|
|
19
19
|
const cfgproduct = require("../src/config/product");
|
|
20
|
-
const
|
|
20
|
+
const checked = require("../src/action/validate/checked");
|
|
21
21
|
const log = require("../src/util/log");
|
|
22
22
|
const output = require("../src/util/output");
|
|
23
23
|
const phrase = require("../src/phrase");
|
|
24
|
-
const project = require("../src/parser/project");
|
|
25
24
|
const rc = require("../src/util/rc");
|
|
26
25
|
const spinner = require("../src/util/spinner");
|
|
27
|
-
const validate = require("../src/cli/validate");
|
|
28
26
|
|
|
29
27
|
// [use]
|
|
30
28
|
|
|
31
29
|
const abort = output.abort;
|
|
32
|
-
const ArcError = error.ArcError;
|
|
33
30
|
const findRc = rc.findRc;
|
|
34
31
|
const print = output.print;
|
|
35
32
|
const relative = path.relative;
|
|
@@ -62,26 +59,16 @@ const start = function (argv) {
|
|
|
62
59
|
let parsed = null;
|
|
63
60
|
// what --json lists under `warnings`.
|
|
64
61
|
let warnings = [];
|
|
65
|
-
let sw = new SpinnerWheel("Validating
|
|
62
|
+
let sw = new SpinnerWheel("Validating… %s");
|
|
66
63
|
sw.start(json || quiet);
|
|
67
|
-
return
|
|
64
|
+
return checked(root, (found) => {
|
|
65
|
+
warnings = found;
|
|
66
|
+
})
|
|
68
67
|
.then((proj) => {
|
|
69
68
|
parsed = proj;
|
|
70
|
-
return validate({ project: parsed, root });
|
|
71
|
-
})
|
|
72
|
-
.then((validated) => {
|
|
73
|
-
const count = validated.issues.length;
|
|
74
|
-
warnings = validated.warnings;
|
|
75
|
-
if (count) {
|
|
76
|
-
throw new ArcError(
|
|
77
|
-
cfgcode.VALIDATION_FAILED,
|
|
78
|
-
phrase.issuesFound(count),
|
|
79
|
-
{ data: { issues: validated.issues } },
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
69
|
sw.stop(true);
|
|
83
70
|
warn(warnings, { json, quiet });
|
|
84
|
-
sw = new SpinnerWheel("Building
|
|
71
|
+
sw = new SpinnerWheel("Building… %s");
|
|
85
72
|
sw.start(json || quiet);
|
|
86
73
|
return build({ project: parsed, root });
|
|
87
74
|
})
|
|
@@ -110,9 +97,7 @@ const start = function (argv) {
|
|
|
110
97
|
}
|
|
111
98
|
if (artifact?.missing) {
|
|
112
99
|
log.result(phrase.buildSucceeded(), [], { color: "yellow" });
|
|
113
|
-
log.nlpw(
|
|
114
|
-
`⚠ ${phrase.buildOutputMissing(relative(root, artifact.missing))}`,
|
|
115
|
-
);
|
|
100
|
+
log.nlpw(phrase.buildOutputMissing(relative(root, artifact.missing)));
|
|
116
101
|
return;
|
|
117
102
|
}
|
|
118
103
|
log.hs(phrase.nothingToDo());
|
package/bin/arc-default.js
CHANGED
|
@@ -34,17 +34,17 @@ const main = function (argv) {
|
|
|
34
34
|
yargs(args.normalize(hideBin(argv)))
|
|
35
35
|
.scriptName(TOOL)
|
|
36
36
|
.usage("$0 <command> [options]")
|
|
37
|
-
.command("init", "Initialize project")
|
|
38
|
-
.command("inspect", "
|
|
39
|
-
.command("validate", "
|
|
40
|
-
.command("build", "
|
|
41
|
-
.command("pack", "
|
|
42
|
-
.command("serve", "Serve
|
|
43
|
-
.command("deploy", "Deploy
|
|
37
|
+
.command("init", "Initialize a project")
|
|
38
|
+
.command("inspect", "Print the resolved manifest")
|
|
39
|
+
.command("validate", "Check the project for issues")
|
|
40
|
+
.command("build", "Run the project's build")
|
|
41
|
+
.command("pack", "Pack the game into a zip")
|
|
42
|
+
.command("serve", "Serve the game locally")
|
|
43
|
+
.command("deploy", "Deploy the game")
|
|
44
44
|
.command("open", "Open a game's store page")
|
|
45
45
|
.command("play", "Play a game")
|
|
46
46
|
.command("login", `Log in to ${TITLE}`)
|
|
47
|
-
.command("whoami",
|
|
47
|
+
.command("whoami", "Show the logged-in account")
|
|
48
48
|
.command("logout", `Log out of ${TITLE}`)
|
|
49
49
|
.help("help")
|
|
50
50
|
.alias("help", "h")
|
package/bin/arc-deploy.js
CHANGED
|
@@ -17,6 +17,7 @@ const build = require("../src/cli/build");
|
|
|
17
17
|
const cfgcode = require("../src/config/code");
|
|
18
18
|
const cfgpackage = require("../src/config/package");
|
|
19
19
|
const cfgproduct = require("../src/config/product");
|
|
20
|
+
const checked = require("../src/action/validate/checked");
|
|
20
21
|
const deploy = require("../src/cli/deploy");
|
|
21
22
|
const error = require("../src/util/error");
|
|
22
23
|
const links = require("../src/util/links");
|
|
@@ -97,7 +98,7 @@ const handler = function ({ json, pb, quiet, sw, warnings }) {
|
|
|
97
98
|
if (pb) {
|
|
98
99
|
pb.stop();
|
|
99
100
|
}
|
|
100
|
-
log.pw(
|
|
101
|
+
log.pw(message);
|
|
101
102
|
sw.start(false);
|
|
102
103
|
if (pb) {
|
|
103
104
|
pb.start();
|
|
@@ -126,7 +127,11 @@ const report = function (
|
|
|
126
127
|
heading = phrase.releasePublished(version.to, channel, built);
|
|
127
128
|
next = null;
|
|
128
129
|
}
|
|
129
|
-
log.result(heading, [
|
|
130
|
+
log.result(heading, [
|
|
131
|
+
store ? [phrase.storeLabel(), store] : null,
|
|
132
|
+
[phrase.playLabel(), url],
|
|
133
|
+
next,
|
|
134
|
+
]);
|
|
130
135
|
};
|
|
131
136
|
|
|
132
137
|
// [function]
|
|
@@ -259,27 +264,19 @@ const start = function (argv) {
|
|
|
259
264
|
let parsed = null;
|
|
260
265
|
// what --json lists under `warnings`: arc validate's, then the deploy's.
|
|
261
266
|
const warnings = [];
|
|
262
|
-
let sw = new SpinnerWheel("Validating
|
|
267
|
+
let sw = new SpinnerWheel("Validating… %s");
|
|
263
268
|
sw.start(json || quiet);
|
|
264
269
|
return checkTimeout(timeout)
|
|
265
|
-
.then(() =>
|
|
270
|
+
.then(() =>
|
|
271
|
+
checked(root, (found) => {
|
|
272
|
+
warnings.push(...found);
|
|
273
|
+
}),
|
|
274
|
+
)
|
|
266
275
|
.then((proj) => {
|
|
267
276
|
parsed = proj;
|
|
268
|
-
return validate({ project: parsed, root });
|
|
269
|
-
})
|
|
270
|
-
.then((validated) => {
|
|
271
|
-
const count = validated.issues.length;
|
|
272
|
-
warnings.push(...validated.warnings);
|
|
273
|
-
if (count) {
|
|
274
|
-
throw new ArcError(
|
|
275
|
-
cfgcode.VALIDATION_FAILED,
|
|
276
|
-
phrase.issuesFound(count),
|
|
277
|
-
{ data: { issues: validated.issues } },
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
277
|
sw.stop(true);
|
|
281
|
-
warn(
|
|
282
|
-
sw = new SpinnerWheel("Checking
|
|
278
|
+
warn(warnings, { json, quiet });
|
|
279
|
+
sw = new SpinnerWheel("Checking… %s");
|
|
283
280
|
// git deploys need no login: anyone who can push to the repository can
|
|
284
281
|
// release, and the Arcadible GitHub App publishes the release. Anyone
|
|
285
282
|
// can wait for it too; the game's owner, logged in, waits among their
|
|
@@ -296,7 +293,7 @@ const start = function (argv) {
|
|
|
296
293
|
throw new ArcError(cfgcode.NOT_LOGGED_IN, phrase.notLoggedIn());
|
|
297
294
|
}
|
|
298
295
|
sw.stop(true);
|
|
299
|
-
sw = new SpinnerWheel("Preparing
|
|
296
|
+
sw = new SpinnerWheel("Preparing… %s");
|
|
300
297
|
return Promise.all([
|
|
301
298
|
// whoami
|
|
302
299
|
current,
|
|
@@ -314,7 +311,7 @@ const start = function (argv) {
|
|
|
314
311
|
sw.stop(true);
|
|
315
312
|
owner = predeployed.owner ?? null;
|
|
316
313
|
held = predeployed.held ?? false;
|
|
317
|
-
sw = new SpinnerWheel("Building
|
|
314
|
+
sw = new SpinnerWheel("Building… %s");
|
|
318
315
|
sw.start(git || json || quiet);
|
|
319
316
|
return Promise.all([
|
|
320
317
|
// whoami
|
|
@@ -345,7 +342,7 @@ const start = function (argv) {
|
|
|
345
342
|
}
|
|
346
343
|
parsed = proj;
|
|
347
344
|
sw.stop(true);
|
|
348
|
-
sw = new SpinnerWheel("Packing
|
|
345
|
+
sw = new SpinnerWheel("Packing… %s");
|
|
349
346
|
sw.start(git || json || quiet);
|
|
350
347
|
return Promise.all([
|
|
351
348
|
// whoami
|
|
@@ -441,7 +438,8 @@ const main = function (argv) {
|
|
|
441
438
|
})
|
|
442
439
|
.option("timeout", {
|
|
443
440
|
default: conf.timeout,
|
|
444
|
-
|
|
441
|
+
defaultDescription: "300, or 900 for git",
|
|
442
|
+
describe: "Seconds to wait",
|
|
445
443
|
type: "number",
|
|
446
444
|
})
|
|
447
445
|
.option("cwd", args.CWD)
|
package/bin/arc-init.js
CHANGED
|
@@ -31,6 +31,7 @@ const spinner = require("../src/util/spinner");
|
|
|
31
31
|
|
|
32
32
|
const abort = output.abort;
|
|
33
33
|
const ArcError = error.ArcError;
|
|
34
|
+
const exit = output.exit;
|
|
34
35
|
const findRc = rc.findRc;
|
|
35
36
|
const pathExists = fs.pathExists;
|
|
36
37
|
const print = output.print;
|
|
@@ -69,7 +70,7 @@ const handler = function ({ json, quiet, sw }) {
|
|
|
69
70
|
const start = function (argv) {
|
|
70
71
|
const json = argv.json;
|
|
71
72
|
const quiet = argv.quiet;
|
|
72
|
-
const sw = new SpinnerWheel("Initializing
|
|
73
|
+
const sw = new SpinnerWheel("Initializing… %s");
|
|
73
74
|
return init({
|
|
74
75
|
handler: handler({ json, quiet, sw }),
|
|
75
76
|
patch: argv.patch,
|
|
@@ -86,7 +87,7 @@ const start = function (argv) {
|
|
|
86
87
|
print(project, { nl: true, toml: true });
|
|
87
88
|
}
|
|
88
89
|
// degit sometimes hangs, so exit once stdout is written.
|
|
89
|
-
|
|
90
|
+
return exit(0);
|
|
90
91
|
})
|
|
91
92
|
.catch((ex) => abort(phrase.aborted(COMMAND), ex, { json, sw }));
|
|
92
93
|
};
|
|
@@ -149,7 +150,7 @@ const main = function (argv) {
|
|
|
149
150
|
default: conf.template,
|
|
150
151
|
describe: [
|
|
151
152
|
"Project template",
|
|
152
|
-
"Public GitHub repository, or a folder in one (user/repo[/folder])
|
|
153
|
+
"Public GitHub repository, or a folder in one (user/repo[/folder])",
|
|
153
154
|
].join("\n"),
|
|
154
155
|
type: "string",
|
|
155
156
|
})
|
package/bin/arc-login.js
CHANGED
|
@@ -137,12 +137,12 @@ const main = function (argv) {
|
|
|
137
137
|
.usage("$0 [options]")
|
|
138
138
|
.option("browser", {
|
|
139
139
|
default: conf.browser,
|
|
140
|
-
describe: "Open a web browser on Enter
|
|
140
|
+
describe: "Open a web browser on Enter",
|
|
141
141
|
type: "boolean",
|
|
142
142
|
})
|
|
143
143
|
.option("qrcode", {
|
|
144
144
|
default: conf.qrcode,
|
|
145
|
-
describe: "
|
|
145
|
+
describe: "Show a QR code",
|
|
146
146
|
type: "boolean",
|
|
147
147
|
})
|
|
148
148
|
.help("help")
|
package/bin/arc-pack.js
CHANGED
|
@@ -13,23 +13,19 @@ const yargs = require("yargs/yargs");
|
|
|
13
13
|
// [import]
|
|
14
14
|
|
|
15
15
|
const args = require("../src/util/argv");
|
|
16
|
-
const cfgcode = require("../src/config/code");
|
|
17
16
|
const cfgpackage = require("../src/config/package");
|
|
18
17
|
const cfgproduct = require("../src/config/product");
|
|
19
|
-
const
|
|
18
|
+
const checked = require("../src/action/validate/checked");
|
|
20
19
|
const log = require("../src/util/log");
|
|
21
20
|
const output = require("../src/util/output");
|
|
22
21
|
const pack = require("../src/cli/pack");
|
|
23
22
|
const phrase = require("../src/phrase");
|
|
24
|
-
const project = require("../src/parser/project");
|
|
25
23
|
const rc = require("../src/util/rc");
|
|
26
24
|
const spinner = require("../src/util/spinner");
|
|
27
|
-
const validate = require("../src/cli/validate");
|
|
28
25
|
|
|
29
26
|
// [use]
|
|
30
27
|
|
|
31
28
|
const abort = output.abort;
|
|
32
|
-
const ArcError = error.ArcError;
|
|
33
29
|
const findRc = rc.findRc;
|
|
34
30
|
const print = output.print;
|
|
35
31
|
const resolve = path.resolve;
|
|
@@ -62,26 +58,16 @@ const start = function (argv) {
|
|
|
62
58
|
let parsed = null;
|
|
63
59
|
// what --json lists under `warnings`.
|
|
64
60
|
let warnings = [];
|
|
65
|
-
let sw = new SpinnerWheel("Validating
|
|
61
|
+
let sw = new SpinnerWheel("Validating… %s");
|
|
66
62
|
sw.start(json || quiet);
|
|
67
|
-
return
|
|
63
|
+
return checked(root, (found) => {
|
|
64
|
+
warnings = found;
|
|
65
|
+
})
|
|
68
66
|
.then((proj) => {
|
|
69
67
|
parsed = proj;
|
|
70
|
-
return validate({ project: parsed, root });
|
|
71
|
-
})
|
|
72
|
-
.then((validated) => {
|
|
73
|
-
const count = validated.issues.length;
|
|
74
|
-
warnings = validated.warnings;
|
|
75
|
-
if (count) {
|
|
76
|
-
throw new ArcError(
|
|
77
|
-
cfgcode.VALIDATION_FAILED,
|
|
78
|
-
phrase.issuesFound(count),
|
|
79
|
-
{ data: { issues: validated.issues } },
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
68
|
sw.stop(true);
|
|
83
69
|
warn(warnings, { json, quiet });
|
|
84
|
-
sw = new SpinnerWheel("Packing
|
|
70
|
+
sw = new SpinnerWheel("Packing… %s");
|
|
85
71
|
sw.start(json || quiet);
|
|
86
72
|
return pack({
|
|
87
73
|
input: argv.input,
|
|
@@ -116,13 +102,13 @@ const main = function (argv) {
|
|
|
116
102
|
.option("input", {
|
|
117
103
|
alias: "i",
|
|
118
104
|
default: conf.input,
|
|
119
|
-
describe: "
|
|
105
|
+
describe: "Pack this directory instead of deploy_dir",
|
|
120
106
|
type: "string",
|
|
121
107
|
})
|
|
122
108
|
.option("output", {
|
|
123
109
|
alias: "o",
|
|
124
110
|
default: conf.output,
|
|
125
|
-
describe: "
|
|
111
|
+
describe: "Write the zip to this path",
|
|
126
112
|
type: "string",
|
|
127
113
|
})
|
|
128
114
|
.option("json", { ...args.JSON_OUTPUT, default: conf.json })
|
package/bin/arc-play.js
CHANGED
|
@@ -64,7 +64,7 @@ const handler = function ({ quiet, sw }) {
|
|
|
64
64
|
// [function]
|
|
65
65
|
|
|
66
66
|
const start = function (argv) {
|
|
67
|
-
const sw = new SpinnerWheel("Opening
|
|
67
|
+
const sw = new SpinnerWheel("Opening… %s");
|
|
68
68
|
return play({
|
|
69
69
|
game: argv.game,
|
|
70
70
|
handler: handler({ quiet: argv.quiet, sw }),
|
package/bin/arc-serve.js
CHANGED
|
@@ -111,7 +111,7 @@ const start = function (argv) {
|
|
|
111
111
|
// `.arcrc` values are strings, and a flag that isn't a number is NaN.
|
|
112
112
|
const port = Number(argv.port);
|
|
113
113
|
const root = resolve(process.cwd(), argv.cwd);
|
|
114
|
-
const sw = new SpinnerWheel("Building
|
|
114
|
+
const sw = new SpinnerWheel("Building… %s");
|
|
115
115
|
return checkPort(port)
|
|
116
116
|
.then(() => checkUrl(argv.url))
|
|
117
117
|
.then((url) =>
|
|
@@ -141,7 +141,7 @@ const start = function (argv) {
|
|
|
141
141
|
process.once("SIGTERM", stop);
|
|
142
142
|
if (!quiet) {
|
|
143
143
|
log.result(phrase.served(served), [
|
|
144
|
-
[
|
|
144
|
+
[phrase.playLabel(), `${served.page}/`],
|
|
145
145
|
[phrase.serveAlone(), served.game],
|
|
146
146
|
served.url
|
|
147
147
|
? phrase.serveNextUrl()
|
|
@@ -173,7 +173,7 @@ const main = function (argv) {
|
|
|
173
173
|
.option("port", {
|
|
174
174
|
alias: "p",
|
|
175
175
|
default: conf.port,
|
|
176
|
-
describe: "
|
|
176
|
+
describe: "Serve on this port",
|
|
177
177
|
type: "number",
|
|
178
178
|
})
|
|
179
179
|
.option("build", {
|
|
@@ -182,6 +182,7 @@ const main = function (argv) {
|
|
|
182
182
|
type: "boolean",
|
|
183
183
|
})
|
|
184
184
|
.option("url", {
|
|
185
|
+
default: conf.url,
|
|
185
186
|
describe: "Frame another server's page instead of deploy_dir",
|
|
186
187
|
type: "string",
|
|
187
188
|
})
|
package/bin/arc-validate.js
CHANGED
|
@@ -26,6 +26,7 @@ const validate = require("../src/cli/validate");
|
|
|
26
26
|
|
|
27
27
|
const abort = output.abort;
|
|
28
28
|
const dereference = schema.dereference;
|
|
29
|
+
const exit = output.exit;
|
|
29
30
|
const findRc = rc.findRc;
|
|
30
31
|
const print = output.print;
|
|
31
32
|
const resolve = path.resolve;
|
|
@@ -75,7 +76,7 @@ const start = function (argv) {
|
|
|
75
76
|
log.hs(phrase.noIssuesFound());
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
return exit(count ? 1 : 0);
|
|
79
80
|
})
|
|
80
81
|
.catch((ex) => abort(phrase.aborted(COMMAND), ex, { json }));
|
|
81
82
|
};
|
package/bin/arc-whoami.js
CHANGED
|
@@ -54,7 +54,7 @@ const handler = function ({ json, sw }) {
|
|
|
54
54
|
|
|
55
55
|
const start = function (argv) {
|
|
56
56
|
const json = argv.json;
|
|
57
|
-
const sw = new SpinnerWheel("Checking
|
|
57
|
+
const sw = new SpinnerWheel("Checking… %s");
|
|
58
58
|
return whoami({ handler: handler({ json, sw }) })
|
|
59
59
|
.then((current) => {
|
|
60
60
|
const user = current?.user;
|
package/package.json
CHANGED
package/src/account.js
CHANGED
|
@@ -102,7 +102,9 @@ const getSaved = function (onRefresh) {
|
|
|
102
102
|
return readFile(file, "utf8")
|
|
103
103
|
.then(JSON.parse)
|
|
104
104
|
.catch((ex) => {
|
|
105
|
-
|
|
105
|
+
// a session file cut short (a full disk, a crash while saving) is no
|
|
106
|
+
// session: `arc login` writes a new one
|
|
107
|
+
if (ex.code === "ENOENT" || ex instanceof SyntaxError) {
|
|
106
108
|
return null;
|
|
107
109
|
}
|
|
108
110
|
throw ex;
|
package/src/action/build/tool.js
CHANGED
|
@@ -35,9 +35,13 @@ const execBuild = function (command, { cwdPath }) {
|
|
|
35
35
|
if (err) {
|
|
36
36
|
const { code, signal } = err;
|
|
37
37
|
reject(
|
|
38
|
-
new ArcError(
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
new ArcError(
|
|
39
|
+
cfgcode.BUILD_FAILED,
|
|
40
|
+
phrase.buildFailed({ code, signal, stderr, stdout }),
|
|
41
|
+
{
|
|
42
|
+
cause: err,
|
|
43
|
+
},
|
|
44
|
+
),
|
|
41
45
|
);
|
|
42
46
|
return;
|
|
43
47
|
}
|
|
@@ -12,7 +12,6 @@ const repo = require("../predeploy/git");
|
|
|
12
12
|
// [use]
|
|
13
13
|
|
|
14
14
|
const ArcError = error.ArcError;
|
|
15
|
-
const failWith = repo.failWith;
|
|
16
15
|
const git = repo.git;
|
|
17
16
|
const tagOf = repo.tagOf;
|
|
18
17
|
|
|
@@ -25,7 +24,13 @@ const createTag = function (root, version) {
|
|
|
25
24
|
: phrase.versionPrerelease(version);
|
|
26
25
|
return git(root, ["tag", "-a", tagOf(version), "-m", message]).then(
|
|
27
26
|
() => null,
|
|
28
|
-
|
|
27
|
+
(err) => {
|
|
28
|
+
throw new ArcError(
|
|
29
|
+
cfgcode.GIT_TAG_CREATE_FAILED,
|
|
30
|
+
phrase.gitTagCreateFailed(version, err.stderr),
|
|
31
|
+
{ cause: err },
|
|
32
|
+
);
|
|
33
|
+
},
|
|
29
34
|
);
|
|
30
35
|
};
|
|
31
36
|
|
|
@@ -52,7 +57,7 @@ const pushTag = function (root, version) {
|
|
|
52
57
|
version,
|
|
53
58
|
new ArcError(
|
|
54
59
|
cfgcode.GIT_TAG_PUSH_FAILED,
|
|
55
|
-
phrase.gitTagPushFailed(version),
|
|
60
|
+
phrase.gitTagPushFailed(version, err.stderr),
|
|
56
61
|
{
|
|
57
62
|
cause: err,
|
|
58
63
|
},
|
|
@@ -33,18 +33,19 @@ const uploadBuild = function (current, artifact, { id, version }, onProgress) {
|
|
|
33
33
|
s3.upload(upload.upload_url, artifact, onProgress),
|
|
34
34
|
]),
|
|
35
35
|
(err) => {
|
|
36
|
-
// the API refuses a game that was deleted or is another account's
|
|
37
|
-
// and the same version
|
|
36
|
+
// the API refuses a game that was deleted or is another account's
|
|
37
|
+
// (403), and the same version deployed again within 5 minutes (429).
|
|
38
38
|
if (err.status === 403) {
|
|
39
39
|
throw new ArcError(
|
|
40
40
|
cfgcode.GAME_UNAVAILABLE,
|
|
41
41
|
phrase.gameUnavailable(id),
|
|
42
|
+
{ cause: err },
|
|
42
43
|
);
|
|
43
44
|
}
|
|
44
45
|
if (err.status === 429) {
|
|
45
46
|
throw new ArcError(
|
|
46
|
-
cfgcode.
|
|
47
|
-
phrase.
|
|
47
|
+
cfgcode.DEPLOY_RATE_LIMITED,
|
|
48
|
+
phrase.deployRateLimited(version),
|
|
48
49
|
{ cause: err },
|
|
49
50
|
);
|
|
50
51
|
}
|
package/src/action/init/tool.js
CHANGED
|
@@ -15,6 +15,7 @@ const cfgcode = require("../../config/code");
|
|
|
15
15
|
const error = require("../../util/error");
|
|
16
16
|
const manifest = require("./manifest");
|
|
17
17
|
const phrase = require("../../phrase");
|
|
18
|
+
const templates = require("../../config/template");
|
|
18
19
|
|
|
19
20
|
// [use]
|
|
20
21
|
|
|
@@ -38,10 +39,14 @@ const removeLicense = function (dest) {
|
|
|
38
39
|
|
|
39
40
|
// [function]
|
|
40
41
|
|
|
42
|
+
// a starter's name on its own, such as `blank`, suggests the starter.
|
|
41
43
|
const unavailable = function (src, cause) {
|
|
44
|
+
const starter = templates.choices.find(
|
|
45
|
+
({ value }) => value.split("/").pop() === src,
|
|
46
|
+
);
|
|
42
47
|
return new ArcError(
|
|
43
48
|
cfgcode.TEMPLATE_UNAVAILABLE,
|
|
44
|
-
phrase.templateUnavailable(src),
|
|
49
|
+
phrase.templateUnavailable(src, starter?.value),
|
|
45
50
|
{ cause },
|
|
46
51
|
);
|
|
47
52
|
};
|
|
@@ -64,8 +69,11 @@ const scaffold = function (src, dest, { onInitBegin }) {
|
|
|
64
69
|
if (onInitBegin) {
|
|
65
70
|
onInitBegin({ dest, src });
|
|
66
71
|
}
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
// degit throws as it's made when it can't parse `src`.
|
|
73
|
+
return Promise.resolve()
|
|
74
|
+
.then(() =>
|
|
75
|
+
degit(src, { cache: false, force: true, verbose: false }).clone(dest),
|
|
76
|
+
)
|
|
69
77
|
.catch((ex) => {
|
|
70
78
|
throw unavailable(src, ex);
|
|
71
79
|
})
|
package/src/action/pack/tool.js
CHANGED
|
@@ -48,13 +48,20 @@ const SIZE_PARTIAL = cfglimit.ZIP_SIZE;
|
|
|
48
48
|
|
|
49
49
|
// [function]
|
|
50
50
|
|
|
51
|
-
// the build's
|
|
51
|
+
// the first path at the build's root that's Arcadible's, if it has one: a
|
|
52
|
+
// folder named like the reserved one, or the manifest or media that packing
|
|
53
|
+
// adds, as `arc validate` checks deploy_dir. A folder ends with "/".
|
|
52
54
|
const findReserved = function (zip) {
|
|
53
|
-
|
|
55
|
+
return zip
|
|
54
56
|
.entries()
|
|
55
57
|
.map((entry) => entry.name.split("/"))
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
+
.map((parts) => (parts.length > 1 ? `${parts[0]}/` : parts[0]))
|
|
59
|
+
.find(
|
|
60
|
+
(name) =>
|
|
61
|
+
name.toLowerCase() === `${RESERVED}/` ||
|
|
62
|
+
name === MANIFEST ||
|
|
63
|
+
name === `${MEDIA}/`,
|
|
64
|
+
);
|
|
58
65
|
};
|
|
59
66
|
|
|
60
67
|
// [function]
|
|
@@ -68,7 +68,10 @@ const ensureHandle = function (current, { handle, id, version }, handler) {
|
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
if (status === "limit") {
|
|
71
|
-
warn(
|
|
71
|
+
warn(
|
|
72
|
+
cfgcode.WARNING.GAME_HANDLE_LIMIT_REACHED,
|
|
73
|
+
phrase.gameHandleLimit(handle),
|
|
74
|
+
);
|
|
72
75
|
return true;
|
|
73
76
|
}
|
|
74
77
|
if (status === "lapsed") {
|
|
@@ -35,9 +35,9 @@ const NAME_REPO = /^[A-Za-z0-9._-]{1,100}$/u;
|
|
|
35
35
|
// runs git in `root`, and resolves with its stdout.
|
|
36
36
|
const git = function (root, args) {
|
|
37
37
|
return new Promise((resolve, reject) => {
|
|
38
|
-
execFile("git", ["-C", root, ...args], (err, stdout) => {
|
|
38
|
+
execFile("git", ["-C", root, ...args], (err, stdout, stderr) => {
|
|
39
39
|
if (err) {
|
|
40
|
-
reject(err);
|
|
40
|
+
reject(Object.assign(err, { stderr }));
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
resolve(stdout);
|
package/src/action/serve/tool.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// [import]
|
|
7
7
|
|
|
8
8
|
const path = require("node:path");
|
|
9
|
+
const timers = require("node:timers/promises");
|
|
9
10
|
|
|
10
11
|
// [import]
|
|
11
12
|
|
|
@@ -39,6 +40,7 @@ const relative = path.relative;
|
|
|
39
40
|
const remoteFiles = remote.remoteFiles;
|
|
40
41
|
const resolvePath = path.resolve;
|
|
41
42
|
const serve = server.serve;
|
|
43
|
+
const sleep = timers.setTimeout;
|
|
42
44
|
const watchDir = reload.watchDir;
|
|
43
45
|
|
|
44
46
|
// [declare]
|
|
@@ -48,7 +50,7 @@ const CHANNEL = "local";
|
|
|
48
50
|
// the saves of a game with no id yet, kept apart from any other's.
|
|
49
51
|
const UNNAMED = "local";
|
|
50
52
|
|
|
51
|
-
// [
|
|
53
|
+
// [declare]
|
|
52
54
|
|
|
53
55
|
// the headers a page needs for the play page to frame it: the play page is
|
|
54
56
|
// cross-origin isolated, so a cross-origin frame must be too, and allow it.
|
|
@@ -57,19 +59,33 @@ const FRAMEABLE = {
|
|
|
57
59
|
"cross-origin-resource-policy": ["cross-origin"],
|
|
58
60
|
};
|
|
59
61
|
|
|
62
|
+
// [declare]
|
|
63
|
+
|
|
64
|
+
// how long `url` has to answer before it counts as unreachable: a server
|
|
65
|
+
// that takes the connection and never answers would otherwise hold `arc
|
|
66
|
+
// serve` for minutes.
|
|
67
|
+
const PROBE_MS = 10000;
|
|
68
|
+
|
|
60
69
|
// [function]
|
|
61
70
|
|
|
62
71
|
// what `url` says when asked: `{ reachable, frameable }`, where `frameable`
|
|
63
72
|
// says whether it sends the headers the play page needs to frame it.
|
|
64
73
|
const probe = async function (url) {
|
|
65
74
|
try {
|
|
66
|
-
|
|
75
|
+
// null when it hasn't answered in time
|
|
76
|
+
const res = await Promise.race([
|
|
77
|
+
fetch(url, { method: "GET" }),
|
|
78
|
+
sleep(PROBE_MS, null, { ref: false }),
|
|
79
|
+
]);
|
|
80
|
+
if (!res) {
|
|
81
|
+
return { frameable: false, reachable: false };
|
|
82
|
+
}
|
|
67
83
|
await res.body?.cancel();
|
|
68
84
|
const frameable = Object.entries(FRAMEABLE).every(([name, values]) =>
|
|
69
85
|
values.includes(res.headers.get(name)?.trim().toLowerCase()),
|
|
70
86
|
);
|
|
71
87
|
return { frameable, reachable: true };
|
|
72
|
-
} catch
|
|
88
|
+
} catch {
|
|
73
89
|
return { frameable: false, reachable: false };
|
|
74
90
|
}
|
|
75
91
|
};
|