arcadible 0.1.44 → 0.1.46
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 +9 -17
- package/bin/arc-deploy.js +20 -36
- package/bin/arc-init.js +6 -16
- package/bin/arc-inspect.js +3 -7
- package/bin/arc-login.js +5 -5
- package/bin/arc-logout.js +4 -8
- package/bin/arc-open.js +7 -20
- package/bin/arc-pack.js +4 -14
- package/bin/arc-play.js +6 -20
- package/bin/arc-validate.js +8 -17
- package/bin/arc-whoami.js +5 -9
- package/bin/arc.js +7 -2
- package/package.json +1 -1
- package/src/account.js +5 -5
- package/src/action/build/tool.js +8 -15
- package/src/action/deploy/tool-git.js +1 -6
- package/src/action/deploy/wait.js +5 -11
- package/src/action/init/manifest.js +9 -37
- package/src/action/login/flow.js +3 -10
- package/src/action/open/tool.js +4 -3
- package/src/action/pack/limits.js +4 -6
- package/src/action/pack/tool.js +9 -5
- package/src/action/play/tool.js +6 -6
- package/src/action/predeploy/ac.js +34 -31
- package/src/action/predeploy/git.js +4 -4
- package/src/action/predeploy/pm.js +1 -1
- package/src/action/predeploy/published.js +4 -4
- package/src/action/predeploy/tool-git.js +10 -10
- package/src/action/validate/unknown.js +11 -10
- package/src/config/code.js +1 -1
- package/src/config/file.js +23 -0
- package/src/config/limit.js +21 -0
- package/src/parser/env.js +0 -2
- package/src/parser/manifest.js +15 -13
- package/src/parser/media.js +9 -11
- package/src/parser/project.js +31 -9
- package/src/parser/self.js +27 -0
- package/src/phrase.js +175 -43
- package/src/schema/game.js +65 -42
- package/src/schema/workflow.js +7 -8
- package/src/util/argv.js +27 -1
- package/src/util/command.js +18 -0
- package/src/util/delay.js +5 -21
- package/src/util/doc.js +4 -13
- package/src/util/links.js +17 -2
- package/src/util/media.js +2 -2
- package/src/util/objs.js +8 -1
- package/src/util/output.js +24 -2
- package/src/util/rc.js +2 -2
- package/src/util/request.js +1 -1
- package/src/util/s3.js +1 -1
- package/src/util/schema.js +9 -3
- package/src/util/uris.js +1 -1
- package/src/util/zip.js +2 -2
- package/src/config/doc.js +0 -7
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
|
-
const semver = require("semver");
|
|
6
|
-
|
|
7
|
-
// [import]
|
|
8
|
-
|
|
9
5
|
const cfgcode = require("../../config/code");
|
|
10
6
|
const error = require("../../util/error");
|
|
11
7
|
const given = require("../../parser/given");
|
|
@@ -18,14 +14,13 @@ const repo = require("../predeploy/git");
|
|
|
18
14
|
const ArcError = error.ArcError;
|
|
19
15
|
const failWith = repo.failWith;
|
|
20
16
|
const git = repo.git;
|
|
21
|
-
const prereleaseSemver = semver.prerelease;
|
|
22
17
|
const tagOf = repo.tagOf;
|
|
23
18
|
|
|
24
19
|
// [function]
|
|
25
20
|
|
|
26
21
|
const createTag = function (root, version) {
|
|
27
22
|
const message =
|
|
28
|
-
|
|
23
|
+
links.channel(version) === links.PRODUCTION
|
|
29
24
|
? phrase.versionRelease(version)
|
|
30
25
|
: phrase.versionPrerelease(version);
|
|
31
26
|
return git(root, ["tag", "-a", tagOf(version), "-m", message]).then(
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
5
|
const api = require("../../api");
|
|
6
|
-
const cfguri = require("../../config/uri");
|
|
7
6
|
const delay = require("../../util/delay");
|
|
7
|
+
const links = require("../../util/links");
|
|
8
8
|
const repo = require("../predeploy/git");
|
|
9
9
|
const time = require("../../util/time");
|
|
10
10
|
|
|
@@ -12,6 +12,7 @@ const time = require("../../util/time");
|
|
|
12
12
|
|
|
13
13
|
const git = repo.git;
|
|
14
14
|
const sleep = delay.sleep;
|
|
15
|
+
const studioUrl = links.studioUrl;
|
|
15
16
|
|
|
16
17
|
// [declare]
|
|
17
18
|
|
|
@@ -34,13 +35,6 @@ const transient = function (err) {
|
|
|
34
35
|
|
|
35
36
|
// [function]
|
|
36
37
|
|
|
37
|
-
// where a developer sees the release in Studio.
|
|
38
|
-
const studioUrl = function (game) {
|
|
39
|
-
return `${cfguri.HOST_HTTP_STUDIO}/games/${game}/builds`;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// [function]
|
|
43
|
-
|
|
44
38
|
// the repository's GitHub Actions page, from origin's URL, or null when
|
|
45
39
|
// origin isn't on GitHub.
|
|
46
40
|
const actionsUrl = function (root) {
|
|
@@ -95,7 +89,7 @@ const findGit = function ({ account, game, headers, since, version }) {
|
|
|
95
89
|
// checks the release until it's published or failed, or `timeout` seconds
|
|
96
90
|
// pass. Resolves { release, timedOut }; `release` is null when it was never
|
|
97
91
|
// found. `onRelease` hears each release it finds.
|
|
98
|
-
const
|
|
92
|
+
const pollRelease = function (find, timeout, onRelease) {
|
|
99
93
|
const deadline = Date.now() + timeout * 1000;
|
|
100
94
|
// one check, then the next, each after the last: `last` is the newest
|
|
101
95
|
// release found, kept through a check that fails for a moment.
|
|
@@ -150,12 +144,12 @@ const main = function (
|
|
|
150
144
|
})
|
|
151
145
|
: findDirect(deployed.release, headers);
|
|
152
146
|
return Promise.all([
|
|
153
|
-
|
|
147
|
+
pollRelease(find, timeout, onRelease),
|
|
154
148
|
isGit ? actionsUrl(root) : null,
|
|
155
149
|
]).then(([{ release, timedOut }, actions]) => ({
|
|
156
150
|
actions,
|
|
157
151
|
release,
|
|
158
|
-
studio: owns ? studioUrl(game) : null,
|
|
152
|
+
studio: owns ? studioUrl(game, "builds") : null,
|
|
159
153
|
timedOut,
|
|
160
154
|
}));
|
|
161
155
|
};
|
|
@@ -11,40 +11,20 @@ const fs = require("fs-extra");
|
|
|
11
11
|
|
|
12
12
|
// [import]
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const cfgproduct = require("../../config/product");
|
|
14
|
+
const cfgfile = require("../../config/file");
|
|
16
15
|
const env = require("../../parser/env");
|
|
17
|
-
const
|
|
18
|
-
const phrase = require("../../phrase");
|
|
16
|
+
const manifest = require("../../parser/manifest");
|
|
19
17
|
const TOML = require("../../util/toml");
|
|
20
18
|
|
|
21
19
|
// [use]
|
|
22
20
|
|
|
23
|
-
const ArcError = error.ArcError;
|
|
24
21
|
const join = path.join;
|
|
25
|
-
const pathExists = fs.pathExists;
|
|
26
22
|
const randomUUID = crypto.randomUUID;
|
|
27
|
-
const readFile = fs.promises.readFile;
|
|
28
23
|
const writeFile = fs.promises.writeFile;
|
|
29
24
|
|
|
30
25
|
// [declare]
|
|
31
26
|
|
|
32
|
-
const
|
|
33
|
-
const MANIFEST = `${HANDLE}.toml`;
|
|
34
|
-
|
|
35
|
-
// [function]
|
|
36
|
-
|
|
37
|
-
const parseToml = function (buf) {
|
|
38
|
-
try {
|
|
39
|
-
return TOML.parse(buf);
|
|
40
|
-
} catch (ex) {
|
|
41
|
-
throw new ArcError(
|
|
42
|
-
cfgcode.PARSE_FAILED,
|
|
43
|
-
phrase.unableToParse(MANIFEST, ex.message),
|
|
44
|
-
{ cause: ex },
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
27
|
+
const MANIFEST = cfgfile.MANIFEST;
|
|
48
28
|
|
|
49
29
|
// [function]
|
|
50
30
|
|
|
@@ -64,14 +44,6 @@ const pickVersion = function (version, envVersion) {
|
|
|
64
44
|
|
|
65
45
|
// [function]
|
|
66
46
|
|
|
67
|
-
const readManifest = function (file) {
|
|
68
|
-
return pathExists(file)
|
|
69
|
-
.then((exists) => (exists ? readFile(file, "utf8") : null))
|
|
70
|
-
.then((buf) => (buf === null ? {} : parseToml(buf)));
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// [function]
|
|
74
|
-
|
|
75
47
|
// a new project always gets its own id: a template's, such as an existing
|
|
76
48
|
// game's, belongs to that game.
|
|
77
49
|
const patchGame = function (envVersion, game = {}, { fresh = false } = {}) {
|
|
@@ -85,10 +57,10 @@ const patchGame = function (envVersion, game = {}, { fresh = false } = {}) {
|
|
|
85
57
|
|
|
86
58
|
// [function]
|
|
87
59
|
|
|
88
|
-
const shouldWrite = function (
|
|
60
|
+
const shouldWrite = function (written, next) {
|
|
89
61
|
return (
|
|
90
|
-
|
|
91
|
-
|
|
62
|
+
written.game?.id !== next.game?.id ||
|
|
63
|
+
written.game?.version !== next.game?.version
|
|
92
64
|
);
|
|
93
65
|
};
|
|
94
66
|
|
|
@@ -96,10 +68,10 @@ const shouldWrite = function (manifest, next) {
|
|
|
96
68
|
|
|
97
69
|
const prepare = function (dest, envVersion, opts) {
|
|
98
70
|
const file = join(dest, MANIFEST);
|
|
99
|
-
return
|
|
100
|
-
const { game, ...rest } =
|
|
71
|
+
return manifest.read(dest).then((written) => {
|
|
72
|
+
const { game, ...rest } = written;
|
|
101
73
|
const next = { game: patchGame(envVersion, game, opts), ...rest };
|
|
102
|
-
if (shouldWrite(
|
|
74
|
+
if (shouldWrite(written, next)) {
|
|
103
75
|
return writeFile(file, TOML.stringify(next), "utf8");
|
|
104
76
|
}
|
|
105
77
|
return null;
|
package/src/action/login/flow.js
CHANGED
|
@@ -60,11 +60,8 @@ const createFlow = function ({ expiresAt, secret, type }, onLoginCreated) {
|
|
|
60
60
|
|
|
61
61
|
// [function]
|
|
62
62
|
|
|
63
|
-
const pollFlow = function (value
|
|
63
|
+
const pollFlow = function (value) {
|
|
64
64
|
return flow(value).then(({ data, status }) => {
|
|
65
|
-
if (onLoginPoll) {
|
|
66
|
-
onLoginPoll({ status });
|
|
67
|
-
}
|
|
68
65
|
switch (status) {
|
|
69
66
|
case "pending":
|
|
70
67
|
return null;
|
|
@@ -83,7 +80,7 @@ const pollFlow = function (value, onLoginPoll) {
|
|
|
83
80
|
|
|
84
81
|
// [function]
|
|
85
82
|
|
|
86
|
-
const main = function ({ onLoginCreated
|
|
83
|
+
const main = function ({ onLoginCreated }) {
|
|
87
84
|
const expiresAt = new Date(Date.now() + time.m(10));
|
|
88
85
|
const secret = randomUUID();
|
|
89
86
|
return createFlow(
|
|
@@ -95,11 +92,7 @@ const main = function ({ onLoginCreated, onLoginPoll }) {
|
|
|
95
92
|
onLoginCreated,
|
|
96
93
|
).then(({ id }) => {
|
|
97
94
|
const timeout = Math.max(0, expiresAt.getTime() - Date.now() - time.s(15));
|
|
98
|
-
return poll(
|
|
99
|
-
(value) => pollFlow(value, onLoginPoll),
|
|
100
|
-
{ id, secret },
|
|
101
|
-
{ ...POLL, timeout },
|
|
102
|
-
);
|
|
95
|
+
return poll(pollFlow, { id, secret }, { ...POLL, timeout });
|
|
103
96
|
});
|
|
104
97
|
};
|
|
105
98
|
|
package/src/action/open/tool.js
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
5
|
const links = require("../../util/links");
|
|
6
|
-
const
|
|
6
|
+
const self = require("../../parser/self");
|
|
7
7
|
|
|
8
8
|
// [function]
|
|
9
9
|
|
|
10
|
+
// the store page's path, which the app opens, and its URL on the web.
|
|
10
11
|
const openUrl = function (id) {
|
|
11
|
-
return id ? { page: links.storePage(id) } : null;
|
|
12
|
+
return id ? { page: links.storePage(id), url: links.storeUrl(id) } : null;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
// [function]
|
|
@@ -20,7 +21,7 @@ const open = function (id) {
|
|
|
20
21
|
// [function]
|
|
21
22
|
|
|
22
23
|
const openSelf = function (root) {
|
|
23
|
-
return
|
|
24
|
+
return self(root).then((game) => openUrl(game?.handle ?? game?.id));
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
// [function]
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
5
|
const cfgcode = require("../../config/code");
|
|
6
|
+
const cfglimit = require("../../config/limit");
|
|
6
7
|
const error = require("../../util/error");
|
|
7
8
|
const phrase = require("../../phrase");
|
|
8
9
|
const size = require("../../util/size");
|
|
@@ -14,12 +15,9 @@ const toMb = size.toMb;
|
|
|
14
15
|
|
|
15
16
|
// [declare]
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const MAX_FILES = 10000;
|
|
21
|
-
const MAX_FILE_SIZE = size.mb(64);
|
|
22
|
-
const MAX_TOTAL_SIZE = size.mb(256);
|
|
18
|
+
const MAX_FILES = cfglimit.FILES;
|
|
19
|
+
const MAX_FILE_SIZE = cfglimit.FILE_SIZE;
|
|
20
|
+
const MAX_TOTAL_SIZE = cfglimit.TOTAL_SIZE;
|
|
23
21
|
|
|
24
22
|
// [function]
|
|
25
23
|
|
package/src/action/pack/tool.js
CHANGED
|
@@ -11,6 +11,8 @@ const fs = require("fs-extra");
|
|
|
11
11
|
// [import]
|
|
12
12
|
|
|
13
13
|
const cfgcode = require("../../config/code");
|
|
14
|
+
const cfgfile = require("../../config/file");
|
|
15
|
+
const cfglimit = require("../../config/limit");
|
|
14
16
|
const cfgproduct = require("../../config/product");
|
|
15
17
|
const error = require("../../util/error");
|
|
16
18
|
const given = require("../../parser/given");
|
|
@@ -28,29 +30,31 @@ const resolve = path.resolve;
|
|
|
28
30
|
|
|
29
31
|
// [declare]
|
|
30
32
|
|
|
31
|
-
const HANDLE = cfgproduct.handle;
|
|
32
33
|
const TOOL = cfgproduct.tool;
|
|
33
34
|
|
|
34
35
|
// [declare]
|
|
35
36
|
|
|
36
37
|
const MAIN = "index.html";
|
|
37
|
-
const MANIFEST =
|
|
38
|
-
const MEDIA =
|
|
38
|
+
const MANIFEST = cfgfile.PACKAGED;
|
|
39
|
+
const MEDIA = cfgfile.MEDIA;
|
|
39
40
|
const OUTPUT = `.${TOOL}/package`;
|
|
40
41
|
|
|
41
42
|
// [declare]
|
|
42
43
|
|
|
43
|
-
const SIZE_PARTIAL =
|
|
44
|
+
const SIZE_PARTIAL = cfglimit.ZIP_SIZE;
|
|
44
45
|
|
|
45
46
|
// [function]
|
|
46
47
|
|
|
48
|
+
// the files' own limits come first, since they name what to fix, such as
|
|
49
|
+
// the one file over 64 MB that makes the zip too large.
|
|
47
50
|
const verifyPartial = function (zip) {
|
|
48
51
|
if (!zip.has(MAIN)) {
|
|
49
52
|
throw new ArcError(
|
|
50
53
|
cfgcode.PACKAGE_INDEX_MISSING,
|
|
51
|
-
phrase.
|
|
54
|
+
phrase.packageIndexMissing(MAIN),
|
|
52
55
|
);
|
|
53
56
|
}
|
|
57
|
+
limits.check(zip.entries());
|
|
54
58
|
if (zip.stat().size > SIZE_PARTIAL) {
|
|
55
59
|
throw new ArcError(
|
|
56
60
|
cfgcode.PACKAGE_TOO_LARGE,
|
package/src/action/play/tool.js
CHANGED
|
@@ -7,7 +7,7 @@ const cfgcode = require("../../config/code");
|
|
|
7
7
|
const error = require("../../util/error");
|
|
8
8
|
const links = require("../../util/links");
|
|
9
9
|
const phrase = require("../../phrase");
|
|
10
|
-
const
|
|
10
|
+
const self = require("../../parser/self");
|
|
11
11
|
|
|
12
12
|
// [use]
|
|
13
13
|
|
|
@@ -18,7 +18,7 @@ const ArcError = error.ArcError;
|
|
|
18
18
|
// the game, `select`ed, or null when the API can't be reached. It rejects
|
|
19
19
|
// when the game isn't on Arcadible: its builds can't say, since a game that
|
|
20
20
|
// doesn't exist has none.
|
|
21
|
-
const
|
|
21
|
+
const findGame = function (id, select) {
|
|
22
22
|
return api.discoverGame(id, { select }).catch((err) => {
|
|
23
23
|
if (err?.status === 404) {
|
|
24
24
|
throw new ArcError(cfgcode.GAME_NOT_FOUND, phrase.gameNotFound(id));
|
|
@@ -68,7 +68,7 @@ const play = function (id, release) {
|
|
|
68
68
|
if (!id) {
|
|
69
69
|
return Promise.resolve(null);
|
|
70
70
|
}
|
|
71
|
-
return
|
|
71
|
+
return findGame(id, "orientation").then((game) =>
|
|
72
72
|
target(id, game?.orientation, release),
|
|
73
73
|
);
|
|
74
74
|
};
|
|
@@ -78,14 +78,14 @@ const play = function (id, release) {
|
|
|
78
78
|
// this project's game: its orientation comes from the manifest. With a
|
|
79
79
|
// chosen release, it makes no API call.
|
|
80
80
|
const playSelf = function (root, release) {
|
|
81
|
-
return
|
|
82
|
-
.then((
|
|
81
|
+
return self(root)
|
|
82
|
+
.then((game) => {
|
|
83
83
|
const id = game?.handle ?? game?.id;
|
|
84
84
|
if (!id) {
|
|
85
85
|
return [null, null];
|
|
86
86
|
}
|
|
87
87
|
return Promise.all([
|
|
88
|
-
release ? null :
|
|
88
|
+
release ? null : findGame(id, "id"),
|
|
89
89
|
target(id, game.orientation, release),
|
|
90
90
|
]);
|
|
91
91
|
})
|
|
@@ -14,34 +14,44 @@ const ArcError = error.ArcError;
|
|
|
14
14
|
|
|
15
15
|
// [function]
|
|
16
16
|
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
// a lookup's failure, passed over when it's a 404: nothing there to refuse.
|
|
18
|
+
const ignoreMissing = function (err) {
|
|
19
|
+
if (err.status !== 404) {
|
|
20
|
+
throw err;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// [function]
|
|
25
|
+
|
|
26
|
+
// fails when the game is another account's. A game the API doesn't find
|
|
27
|
+
// passes: it isn't on Arcadible yet, or it was deleted, which the upload
|
|
28
|
+
// refuses.
|
|
29
|
+
const ensureOwned = function (current, id) {
|
|
30
|
+
return api.discoverGame(id).then((game) => {
|
|
31
|
+
// a token's account isn't known yet; the server checks ownership when
|
|
32
|
+
// it authorizes the upload.
|
|
33
|
+
if (!current?.token && game.account !== current?.user?.id) {
|
|
34
|
+
throw new ArcError(cfgcode.GAME_UNAVAILABLE, phrase.gameUnavailable(id));
|
|
35
|
+
}
|
|
36
|
+
}, ignoreMissing);
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
// [function]
|
|
38
40
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
+
// fails when the handle is another game's, whoever owns it. A game without a
|
|
42
|
+
// handle has none to check.
|
|
43
|
+
const ensureHandle = function ({ handle, id }) {
|
|
41
44
|
if ((handle ?? null) === null) {
|
|
42
45
|
return Promise.resolve(null);
|
|
43
46
|
}
|
|
44
|
-
return
|
|
47
|
+
return api.discoverGame(handle).then((game) => {
|
|
48
|
+
if (game.id !== id) {
|
|
49
|
+
throw new ArcError(
|
|
50
|
+
cfgcode.GAME_HANDLE_TAKEN,
|
|
51
|
+
phrase.gameHandleTaken(handle),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}, ignoreMissing);
|
|
45
55
|
};
|
|
46
56
|
|
|
47
57
|
// [function]
|
|
@@ -49,8 +59,8 @@ const ensureHandle = function (current, { handle, id }) {
|
|
|
49
59
|
// resolves with the game once its id, and its handle if it has one, are
|
|
50
60
|
// free or already its own.
|
|
51
61
|
const ensureId = function (current, game) {
|
|
52
|
-
return
|
|
53
|
-
.then(() => ensureHandle(
|
|
62
|
+
return ensureOwned(current, game.id)
|
|
63
|
+
.then(() => ensureHandle(game))
|
|
54
64
|
.then(() => game);
|
|
55
65
|
};
|
|
56
66
|
|
|
@@ -61,14 +71,7 @@ const ensureUnpublished = function (current, game) {
|
|
|
61
71
|
const { id, version } = game;
|
|
62
72
|
return api
|
|
63
73
|
.gameBuild(id, version, null, api.auth(current?.session?.access_token))
|
|
64
|
-
.then(
|
|
65
|
-
() => published.refuse(id, version),
|
|
66
|
-
(err) => {
|
|
67
|
-
if (err.status !== 404) {
|
|
68
|
-
throw err;
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
)
|
|
74
|
+
.then(() => published.refuse(id, version), ignoreMissing)
|
|
72
75
|
.then(() => game);
|
|
73
76
|
};
|
|
74
77
|
|
|
@@ -7,6 +7,7 @@ const cp = require("node:child_process");
|
|
|
7
7
|
// [import]
|
|
8
8
|
|
|
9
9
|
const cfgcode = require("../../config/code");
|
|
10
|
+
const cfgfile = require("../../config/file");
|
|
10
11
|
const cfgproduct = require("../../config/product");
|
|
11
12
|
const error = require("../../util/error");
|
|
12
13
|
const phrase = require("../../phrase");
|
|
@@ -20,8 +21,7 @@ const execFile = cp.execFile;
|
|
|
20
21
|
|
|
21
22
|
const HANDLE = cfgproduct.handle;
|
|
22
23
|
const PREFIX = `${HANDLE}/release/v`;
|
|
23
|
-
|
|
24
|
-
const WORKFLOW = ".github/workflows/arcadible-release.yml";
|
|
24
|
+
const WORKFLOW = cfgfile.WORKFLOW;
|
|
25
25
|
// a remote's URL: `scheme://[user@]host/path`, or scp-like
|
|
26
26
|
// `[user@]host:path`. A path on this machine is neither.
|
|
27
27
|
const URL_REMOTE = /^[a-z][a-z0-9+.-]*:\/\/(?:[^@/\s]+@)?[^/\s]+\/(\S+)$/iu;
|
|
@@ -70,7 +70,7 @@ const tagOf = function (version) {
|
|
|
70
70
|
const ensureClean = function (root) {
|
|
71
71
|
return git(root, ["status", "--porcelain"]).then((stdout) => {
|
|
72
72
|
if (stdout.trim()) {
|
|
73
|
-
throw new ArcError(cfgcode.GIT_UNCOMMITTED, phrase.
|
|
73
|
+
throw new ArcError(cfgcode.GIT_UNCOMMITTED, phrase.gitUncommitted());
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
};
|
|
@@ -200,7 +200,7 @@ const ensureTagFree = function (root, version) {
|
|
|
200
200
|
if (exists) {
|
|
201
201
|
throw new ArcError(
|
|
202
202
|
cfgcode.GIT_TAG_EXISTS,
|
|
203
|
-
phrase.
|
|
203
|
+
phrase.gitTagExists(version),
|
|
204
204
|
);
|
|
205
205
|
}
|
|
206
206
|
});
|
|
@@ -37,7 +37,7 @@ const ensureLockFile = function (root) {
|
|
|
37
37
|
const has = (file) => existsSync(join(root, file));
|
|
38
38
|
if (has("package.json") && !LOCK_FILES.some(has)) {
|
|
39
39
|
return Promise.reject(
|
|
40
|
-
new ArcError(cfgcode.LOCKFILE_MISSING, phrase.
|
|
40
|
+
new ArcError(cfgcode.LOCKFILE_MISSING, phrase.lockfileMissing()),
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
return Promise.resolve();
|
|
@@ -52,7 +52,7 @@ const next = function (version, { production, staging }) {
|
|
|
52
52
|
|
|
53
53
|
// the error for a version that's already published, naming what's newest on
|
|
54
54
|
// each channel and a version to bump to.
|
|
55
|
-
const
|
|
55
|
+
const versionExists = function (id, version) {
|
|
56
56
|
return Promise.all([
|
|
57
57
|
latest(id, links.PRODUCTION),
|
|
58
58
|
latest(id, links.STAGING),
|
|
@@ -60,7 +60,7 @@ const versionPublished = function (id, version) {
|
|
|
60
60
|
const found = { production, staging };
|
|
61
61
|
return new ArcError(
|
|
62
62
|
cfgcode.VERSION_EXISTS,
|
|
63
|
-
phrase.
|
|
63
|
+
phrase.versionExists(version, {
|
|
64
64
|
...found,
|
|
65
65
|
next: next(version, found),
|
|
66
66
|
}),
|
|
@@ -70,9 +70,9 @@ const versionPublished = function (id, version) {
|
|
|
70
70
|
|
|
71
71
|
// [function]
|
|
72
72
|
|
|
73
|
-
// rejects with
|
|
73
|
+
// rejects with versionExists's error.
|
|
74
74
|
const refuse = function (id, version) {
|
|
75
|
-
return
|
|
75
|
+
return versionExists(id, version).then((err) => {
|
|
76
76
|
throw err;
|
|
77
77
|
});
|
|
78
78
|
};
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
const api = require("../../api");
|
|
6
6
|
const cfgcode = require("../../config/code");
|
|
7
|
-
const cfguri = require("../../config/uri");
|
|
8
7
|
const error = require("../../util/error");
|
|
9
8
|
const git = require("./git");
|
|
10
9
|
const given = require("../../parser/given");
|
|
10
|
+
const links = require("../../util/links");
|
|
11
11
|
const phrase = require("../../phrase");
|
|
12
12
|
const pm = require("./pm");
|
|
13
13
|
const published = require("./published");
|
|
@@ -23,12 +23,13 @@ const ensureRoot = git.ensureRoot;
|
|
|
23
23
|
const ensureTagFree = git.ensureTagFree;
|
|
24
24
|
const ensureWorkflow = git.ensureWorkflow;
|
|
25
25
|
const originRepo = git.originRepo;
|
|
26
|
+
const studioUrl = links.studioUrl;
|
|
26
27
|
|
|
27
28
|
// [function]
|
|
28
29
|
|
|
29
30
|
// a public lookup's answer: found (200, with its data), missing (404), or
|
|
30
31
|
// null for any other answer, such as when the API can't be reached.
|
|
31
|
-
const
|
|
32
|
+
const answerOf = function (request) {
|
|
32
33
|
return request.then(
|
|
33
34
|
(data) => ({ data, status: 200 }),
|
|
34
35
|
(err) => (err.status === 404 ? { status: 404 } : null),
|
|
@@ -45,8 +46,8 @@ const lookup = function (request) {
|
|
|
45
46
|
// resolves with the game's owner, or null when Arcadible can't be reached.
|
|
46
47
|
const ensurePublishable = function ({ handle, id, version }, handler) {
|
|
47
48
|
return Promise.all([
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
answerOf(api.discoverGame(id)),
|
|
50
|
+
answerOf(api.discoverGameBuild(id, version)),
|
|
50
51
|
]).then(([game, build]) => {
|
|
51
52
|
if (game?.status === 404) {
|
|
52
53
|
throw new ArcError(
|
|
@@ -55,10 +56,9 @@ const ensurePublishable = function ({ handle, id, version }, handler) {
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
if (game === null || build === null) {
|
|
58
|
-
if (handler.
|
|
59
|
-
handler.
|
|
59
|
+
if (handler.onWarning) {
|
|
60
|
+
handler.onWarning({
|
|
60
61
|
code: cfgcode.WARNING.VERSION_UNCHECKED,
|
|
61
|
-
level: "warn",
|
|
62
62
|
message: phrase.versionUnchecked(version),
|
|
63
63
|
});
|
|
64
64
|
}
|
|
@@ -96,17 +96,17 @@ const ensureConnected = function (root, { handle, id }) {
|
|
|
96
96
|
.then((found) => {
|
|
97
97
|
repo = found;
|
|
98
98
|
return repo
|
|
99
|
-
?
|
|
99
|
+
? answerOf(api.discoverGameRepo(id, repo.owner, repo.name))
|
|
100
100
|
: null;
|
|
101
101
|
})
|
|
102
102
|
.then((connected) => {
|
|
103
103
|
if (connected?.status === 404) {
|
|
104
104
|
throw new ArcError(
|
|
105
105
|
cfgcode.REPO_NOT_CONNECTED,
|
|
106
|
-
phrase.
|
|
106
|
+
phrase.repoNotConnected(
|
|
107
107
|
handle ?? id,
|
|
108
108
|
`${repo.owner}/${repo.name}`,
|
|
109
|
-
|
|
109
|
+
studioUrl(id, "repository"),
|
|
110
110
|
),
|
|
111
111
|
);
|
|
112
112
|
}
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
const cfgcode = require("../../config/code");
|
|
6
6
|
const game = require("../../schema/game");
|
|
7
7
|
const manifest = require("../../parser/manifest");
|
|
8
|
+
const objs = require("../../util/objs");
|
|
9
|
+
const phrase = require("../../phrase");
|
|
8
10
|
const strs = require("../../util/strs");
|
|
9
11
|
const workflow = require("../../schema/workflow");
|
|
10
12
|
|
|
11
13
|
// [use]
|
|
12
14
|
|
|
13
15
|
const closest = strs.closest;
|
|
16
|
+
const isTable = objs.isTable;
|
|
14
17
|
const WARNING = cfgcode.WARNING;
|
|
15
18
|
|
|
16
19
|
// [function]
|
|
@@ -30,7 +33,7 @@ const known = function (root) {
|
|
|
30
33
|
const warning = function (path, message, near) {
|
|
31
34
|
return {
|
|
32
35
|
code: WARNING.FIELD_UNKNOWN,
|
|
33
|
-
details: near ? [
|
|
36
|
+
details: near ? [phrase.didYouMean(near)] : [],
|
|
34
37
|
link: null,
|
|
35
38
|
message,
|
|
36
39
|
path,
|
|
@@ -43,13 +46,11 @@ const warning = function (path, message, near) {
|
|
|
43
46
|
// it is, in the table that takes it, if any.
|
|
44
47
|
const untabled = function (fields, name) {
|
|
45
48
|
const table = Object.keys(fields).find((each) => fields[each].includes(name));
|
|
46
|
-
return warning(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const isTable = function (value) {
|
|
52
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
49
|
+
return warning(
|
|
50
|
+
name,
|
|
51
|
+
phrase.unknownField(),
|
|
52
|
+
table ? `${table}.${name}` : null,
|
|
53
|
+
);
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
// [function]
|
|
@@ -65,14 +66,14 @@ const main = function (root) {
|
|
|
65
66
|
return Object.hasOwn(fields, table) ? [] : [untabled(fields, table)];
|
|
66
67
|
}
|
|
67
68
|
if (!Object.hasOwn(fields, table)) {
|
|
68
|
-
return [warning(table,
|
|
69
|
+
return [warning(table, phrase.unknownTable(), closest(table, tables))];
|
|
69
70
|
}
|
|
70
71
|
return Object.keys(values)
|
|
71
72
|
.filter((field) => !fields[table].includes(field))
|
|
72
73
|
.map((field) =>
|
|
73
74
|
warning(
|
|
74
75
|
`${table}.${field}`,
|
|
75
|
-
|
|
76
|
+
phrase.unknownField(),
|
|
76
77
|
closest(field, fields[table]),
|
|
77
78
|
),
|
|
78
79
|
);
|
package/src/config/code.js
CHANGED
|
@@ -13,7 +13,7 @@ const CLI = {
|
|
|
13
13
|
ARGUMENT_INVALID: "argument_invalid",
|
|
14
14
|
BUILD_FAILED: "build_failed",
|
|
15
15
|
DEPLOY_DIR_MISSING: "deploy_dir_missing",
|
|
16
|
-
|
|
16
|
+
GAME_HANDLE_TAKEN: "game_handle_taken",
|
|
17
17
|
GAME_ID_MISSING: "game_id_missing",
|
|
18
18
|
GAME_NOT_DEPLOYED: "game_not_deployed",
|
|
19
19
|
GAME_NOT_FOUND: "game_not_found",
|