arcadible 0.1.32 → 0.1.34
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 +4 -4
- package/bin/arc-build.js +23 -5
- package/bin/arc-deploy.js +78 -13
- package/bin/arc-init.js +6 -1
- package/bin/arc-inspect.js +1 -0
- package/bin/arc-login.js +4 -1
- package/bin/arc-open.js +4 -1
- package/bin/arc-pack.js +23 -3
- package/bin/arc-play.js +4 -1
- package/bin/arc-validate.js +1 -0
- package/bin/arc-whoami.js +1 -0
- package/package.json +3 -3
- package/src/account.js +7 -4
- package/src/action/build/tool.js +8 -4
- package/src/action/deploy/tool-git.js +49 -61
- package/src/action/deploy/tool.js +50 -44
- package/src/action/deploy/wait.js +37 -22
- package/src/action/init/manifest.js +23 -6
- package/src/action/init/tool-patch.js +1 -7
- package/src/action/init/tool.js +12 -9
- package/src/action/login/flow.js +7 -3
- package/src/action/pack/limits.js +13 -3
- package/src/action/pack/tool.js +19 -6
- package/src/action/play/tool.js +7 -1
- package/src/action/predeploy/ac.js +39 -22
- package/src/action/predeploy/git.js +69 -58
- package/src/action/predeploy/pm.js +24 -17
- package/src/action/predeploy/published.js +15 -2
- package/src/action/predeploy/tool-git.js +74 -86
- package/src/action/predeploy/tool.js +7 -33
- package/src/action/validate/tool.js +5 -4
- package/src/api.js +14 -1
- package/src/cli/predeploy.js +1 -1
- package/src/config/code.js +112 -0
- package/src/config/url.js +2 -1
- package/src/parser/env.js +19 -3
- package/src/parser/given.js +17 -0
- package/src/parser/manifest.js +8 -1
- package/src/parser/media.js +5 -9
- package/src/parser/project.js +4 -1
- package/src/parser/readme-file.js +8 -1
- package/src/phrase.js +75 -7
- package/src/schema/check/game.js +5 -26
- package/src/schema/game.js +117 -210
- package/src/schema/message.js +46 -7
- package/src/schema/readme.js +8 -3
- package/src/schema/workflow.js +35 -15
- package/src/util/argv.js +33 -1
- package/src/util/delay.js +4 -1
- package/src/util/error.js +49 -0
- package/src/util/image.js +4 -30
- package/src/util/log.js +8 -0
- package/src/util/media.js +40 -0
- package/src/util/output.js +34 -4
- package/src/util/request.js +11 -2
- package/src/util/s3.js +37 -20
- package/src/util/schema.js +93 -30
- package/src/util/video.js +4 -27
package/src/action/play/tool.js
CHANGED
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
5
|
const api = require("../../api");
|
|
6
|
+
const cfgcode = require("../../config/code");
|
|
7
|
+
const error = require("../../util/error");
|
|
6
8
|
const links = require("../../util/links");
|
|
7
9
|
const phrase = require("../../phrase");
|
|
8
10
|
const project = require("../../parser/project");
|
|
9
11
|
|
|
12
|
+
// [use]
|
|
13
|
+
|
|
14
|
+
const ArcError = error.ArcError;
|
|
15
|
+
|
|
10
16
|
// [function]
|
|
11
17
|
|
|
12
18
|
// the game, `select`ed, or null when the API can't be reached. It rejects
|
|
@@ -15,7 +21,7 @@ const project = require("../../parser/project");
|
|
|
15
21
|
const lookup = function (id, select) {
|
|
16
22
|
return api.discoverGame(id, { select }).catch((err) => {
|
|
17
23
|
if (err?.status === 404) {
|
|
18
|
-
throw new
|
|
24
|
+
throw new ArcError(cfgcode.GAME_NOT_FOUND, phrase.gameNotFound(id));
|
|
19
25
|
}
|
|
20
26
|
return null;
|
|
21
27
|
});
|
|
@@ -3,56 +3,73 @@
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
5
|
const api = require("../../api");
|
|
6
|
+
const cfgcode = require("../../config/code");
|
|
7
|
+
const error = require("../../util/error");
|
|
6
8
|
const phrase = require("../../phrase");
|
|
7
9
|
const published = require("./published");
|
|
8
10
|
|
|
11
|
+
// [use]
|
|
12
|
+
|
|
13
|
+
const ArcError = error.ArcError;
|
|
14
|
+
|
|
9
15
|
// [function]
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
// fails when `idOrHandle` is another game's, or this game's on another
|
|
18
|
+
// account. A game that isn't on Arcadible yet passes.
|
|
19
|
+
const ensureGame = function (current, idOrHandle, id) {
|
|
12
20
|
return api.discoverGame(idOrHandle).then(
|
|
13
21
|
(game) => {
|
|
14
22
|
// a token's account isn't known yet, so only the id is compared; the
|
|
15
23
|
// server checks ownership when it authorizes the upload.
|
|
16
24
|
const owned = current?.token || game.account === current?.user?.id;
|
|
17
|
-
if (owned
|
|
18
|
-
|
|
19
|
-
return;
|
|
25
|
+
if (!owned || game.id !== id) {
|
|
26
|
+
throw new ArcError(cfgcode.GAME_EXISTS, phrase.gameExists(idOrHandle));
|
|
20
27
|
}
|
|
21
|
-
cb(new Error(phrase.gameExists(idOrHandle)));
|
|
22
28
|
},
|
|
23
29
|
(err) => {
|
|
24
|
-
if (err.status
|
|
25
|
-
|
|
26
|
-
return;
|
|
30
|
+
if (err.status !== 404) {
|
|
31
|
+
throw err;
|
|
27
32
|
}
|
|
28
|
-
cb(err);
|
|
29
33
|
},
|
|
30
34
|
);
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
// [function]
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
// a game without a handle has none to check.
|
|
40
|
+
const ensureHandle = function (current, { handle, id }) {
|
|
41
|
+
if ((handle ?? null) === null) {
|
|
42
|
+
return Promise.resolve(null);
|
|
43
|
+
}
|
|
44
|
+
return ensureGame(current, handle, id);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// [function]
|
|
48
|
+
|
|
49
|
+
// resolves with the game once its id, and its handle if it has one, are
|
|
50
|
+
// free or already its own.
|
|
51
|
+
const ensureId = function (current, game) {
|
|
52
|
+
return ensureGame(current, game.id, game.id)
|
|
53
|
+
.then(() => ensureHandle(current, game))
|
|
54
|
+
.then(() => game);
|
|
43
55
|
};
|
|
44
56
|
|
|
45
57
|
// [function]
|
|
46
58
|
|
|
47
|
-
// fails when
|
|
48
|
-
const ensureUnpublished = function (current,
|
|
59
|
+
// resolves with the game, or fails when its version is already published.
|
|
60
|
+
const ensureUnpublished = function (current, game) {
|
|
61
|
+
const { id, version } = game;
|
|
49
62
|
return api
|
|
50
63
|
.gameBuild(id, version, null, api.auth(current?.session?.access_token))
|
|
51
64
|
.then(
|
|
52
|
-
() => published.
|
|
53
|
-
(err) =>
|
|
65
|
+
() => published.refuse(id, version),
|
|
66
|
+
(err) => {
|
|
67
|
+
if (err.status !== 404) {
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
54
71
|
)
|
|
55
|
-
.then(
|
|
72
|
+
.then(() => game);
|
|
56
73
|
};
|
|
57
74
|
|
|
58
75
|
// [export]
|
|
@@ -6,22 +6,46 @@ const cp = require("node:child_process");
|
|
|
6
6
|
|
|
7
7
|
// [import]
|
|
8
8
|
|
|
9
|
+
const cfgcode = require("../../config/code");
|
|
9
10
|
const cfgproduct = require("../../config/product");
|
|
11
|
+
const error = require("../../util/error");
|
|
10
12
|
const phrase = require("../../phrase");
|
|
11
13
|
|
|
12
14
|
// [use]
|
|
13
15
|
|
|
16
|
+
const ArcError = error.ArcError;
|
|
14
17
|
const execFile = cp.execFile;
|
|
15
18
|
|
|
16
19
|
// [declare]
|
|
17
20
|
|
|
18
21
|
const HANDLE = cfgproduct.handle;
|
|
19
22
|
const PREFIX = `${HANDLE}/release/v`;
|
|
23
|
+
// the release workflow, which builds the release on GitHub Actions.
|
|
24
|
+
const WORKFLOW = ".github/workflows/arcadible-release.yml";
|
|
20
25
|
|
|
21
26
|
// [function]
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
// runs git in `root`, and resolves with its stdout.
|
|
29
|
+
const git = function (root, args) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
execFile("git", ["-C", root, ...args], (err, stdout) => {
|
|
32
|
+
if (err) {
|
|
33
|
+
reject(err);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
resolve(stdout);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// [function]
|
|
42
|
+
|
|
43
|
+
// a rejection handler that replaces git's error with `code` and `message`,
|
|
44
|
+
// keeping it as the cause.
|
|
45
|
+
const failWith = function (code, message) {
|
|
46
|
+
return (err) => {
|
|
47
|
+
throw new ArcError(code, message, { cause: err });
|
|
48
|
+
};
|
|
25
49
|
};
|
|
26
50
|
|
|
27
51
|
// [function]
|
|
@@ -36,90 +60,75 @@ const tagOf = function (version) {
|
|
|
36
60
|
// fails on any change that isn't committed: staged, unstaged, or untracked.
|
|
37
61
|
// ignored files don't count. The release is built from HEAD, so anything
|
|
38
62
|
// uncommitted wouldn't be part of it.
|
|
39
|
-
const ensureClean = function (root
|
|
40
|
-
git(root, ["status", "--porcelain"]
|
|
41
|
-
if (err) {
|
|
42
|
-
cb(err);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
63
|
+
const ensureClean = function (root) {
|
|
64
|
+
return git(root, ["status", "--porcelain"]).then((stdout) => {
|
|
45
65
|
if (stdout.trim()) {
|
|
46
|
-
|
|
47
|
-
return;
|
|
66
|
+
throw new ArcError(cfgcode.GIT_UNCOMMITTED, phrase.gitChangesPresent());
|
|
48
67
|
}
|
|
49
|
-
cb(null);
|
|
50
68
|
});
|
|
51
69
|
};
|
|
52
70
|
|
|
53
71
|
// [function]
|
|
54
72
|
|
|
55
|
-
const ensureRemote = function (root
|
|
56
|
-
git(root, ["ls-remote", "origin"]
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
cb(null);
|
|
62
|
-
});
|
|
73
|
+
const ensureRemote = function (root) {
|
|
74
|
+
return git(root, ["ls-remote", "origin"]).then(
|
|
75
|
+
() => null,
|
|
76
|
+
failWith(cfgcode.GIT_REMOTE_UNAVAILABLE, phrase.gitRemoteMissing()),
|
|
77
|
+
);
|
|
63
78
|
};
|
|
64
79
|
|
|
65
80
|
// [function]
|
|
66
81
|
|
|
67
|
-
const ensureRepo = function (root
|
|
68
|
-
git(root, ["rev-parse", "--is-inside-work-tree"]
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
cb(null);
|
|
74
|
-
});
|
|
82
|
+
const ensureRepo = function (root) {
|
|
83
|
+
return git(root, ["rev-parse", "--is-inside-work-tree"]).then(
|
|
84
|
+
() => null,
|
|
85
|
+
failWith(cfgcode.GIT_REPO_MISSING, phrase.gitRepoMissing()),
|
|
86
|
+
);
|
|
75
87
|
};
|
|
76
88
|
|
|
77
89
|
// [function]
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
91
|
+
// fails when HEAD has no release workflow, which Arcadible checks for when the
|
|
92
|
+
// tag arrives (workflow_missing).
|
|
93
|
+
const ensureWorkflow = function (root) {
|
|
94
|
+
return git(root, ["cat-file", "-e", `HEAD:${WORKFLOW}`]).then(
|
|
95
|
+
() => null,
|
|
96
|
+
failWith(cfgcode.WORKFLOW_MISSING, phrase.workflowMissing(WORKFLOW)),
|
|
97
|
+
);
|
|
87
98
|
};
|
|
88
99
|
|
|
89
100
|
// [function]
|
|
90
101
|
|
|
91
|
-
const
|
|
92
|
-
git(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
const hasTagLocal = function (root, tag) {
|
|
103
|
+
return git(root, ["tag", "--list", tag]).then((stdout) =>
|
|
104
|
+
Boolean(stdout.trim()),
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// [function]
|
|
109
|
+
|
|
110
|
+
const hasTagRemote = function (root, tag) {
|
|
111
|
+
return git(root, ["ls-remote", "--tags", "origin", `refs/tags/${tag}`]).then(
|
|
112
|
+
(stdout) => Boolean(stdout.trim()),
|
|
113
|
+
failWith(cfgcode.GIT_REMOTE_UNAVAILABLE, phrase.gitRemoteMissing()),
|
|
102
114
|
);
|
|
103
115
|
};
|
|
104
116
|
|
|
105
117
|
// [function]
|
|
106
118
|
|
|
107
119
|
// fails when the version's tag exists, locally or on origin.
|
|
108
|
-
const ensureTagFree = function (root, version
|
|
120
|
+
const ensureTagFree = function (root, version) {
|
|
109
121
|
const tag = tagOf(version);
|
|
110
|
-
hasTagLocal(root, tag
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return;
|
|
122
|
+
return hasTagLocal(root, tag)
|
|
123
|
+
.then((local) => local || hasTagRemote(root, tag))
|
|
124
|
+
.then((exists) => {
|
|
125
|
+
if (exists) {
|
|
126
|
+
throw new ArcError(
|
|
127
|
+
cfgcode.GIT_TAG_EXISTS,
|
|
128
|
+
phrase.gitVersionExists(version),
|
|
129
|
+
);
|
|
119
130
|
}
|
|
120
|
-
cb(null);
|
|
121
131
|
});
|
|
122
|
-
});
|
|
123
132
|
};
|
|
124
133
|
|
|
125
134
|
// [export]
|
|
@@ -129,6 +138,8 @@ module.exports = {
|
|
|
129
138
|
ensureRemote,
|
|
130
139
|
ensureRepo,
|
|
131
140
|
ensureTagFree,
|
|
141
|
+
ensureWorkflow,
|
|
142
|
+
failWith,
|
|
132
143
|
git,
|
|
133
144
|
tagOf,
|
|
134
145
|
};
|
|
@@ -7,33 +7,40 @@ const path = require("node:path");
|
|
|
7
7
|
|
|
8
8
|
// [import]
|
|
9
9
|
|
|
10
|
+
const cfgcode = require("../../config/code");
|
|
11
|
+
const error = require("../../util/error");
|
|
10
12
|
const phrase = require("../../phrase");
|
|
11
13
|
|
|
12
14
|
// [use]
|
|
13
15
|
|
|
16
|
+
const ArcError = error.ArcError;
|
|
14
17
|
const existsSync = fs.existsSync; // eslint-disable-line no-sync
|
|
15
18
|
const join = path.join;
|
|
16
19
|
|
|
20
|
+
// [declare]
|
|
21
|
+
|
|
22
|
+
const LOCK_FILES = [
|
|
23
|
+
// npm
|
|
24
|
+
"package-lock.json",
|
|
25
|
+
"npm-shrinkwrap.json",
|
|
26
|
+
// pnpm
|
|
27
|
+
"pnpm-lock.yaml",
|
|
28
|
+
// yarn
|
|
29
|
+
"yarn.lock",
|
|
30
|
+
];
|
|
31
|
+
|
|
17
32
|
// [function]
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
existsSync(join(root, "npm-shrinkwrap.json")) ||
|
|
28
|
-
// pnpm
|
|
29
|
-
existsSync(join(root, "pnpm-lock.yaml")) ||
|
|
30
|
-
// yarn
|
|
31
|
-
existsSync(join(root, "yarn.lock"))
|
|
32
|
-
) {
|
|
33
|
-
cb(null);
|
|
34
|
-
return;
|
|
34
|
+
// fails when there's a package.json without a lockfile: the release workflow
|
|
35
|
+
// installs exactly what the lockfile pins.
|
|
36
|
+
const ensureLockFile = function (root) {
|
|
37
|
+
const has = (file) => existsSync(join(root, file));
|
|
38
|
+
if (has("package.json") && !LOCK_FILES.some(has)) {
|
|
39
|
+
return Promise.reject(
|
|
40
|
+
new ArcError(cfgcode.LOCKFILE_MISSING, phrase.noLockFile()),
|
|
41
|
+
);
|
|
35
42
|
}
|
|
36
|
-
|
|
43
|
+
return Promise.resolve();
|
|
37
44
|
};
|
|
38
45
|
|
|
39
46
|
// [export]
|
|
@@ -7,11 +7,14 @@ const semver = require("semver");
|
|
|
7
7
|
// [import]
|
|
8
8
|
|
|
9
9
|
const api = require("../../api");
|
|
10
|
+
const cfgcode = require("../../config/code");
|
|
11
|
+
const error = require("../../util/error");
|
|
10
12
|
const links = require("../../util/links");
|
|
11
13
|
const phrase = require("../../phrase");
|
|
12
14
|
|
|
13
15
|
// [use]
|
|
14
16
|
|
|
17
|
+
const ArcError = error.ArcError;
|
|
15
18
|
const inc = semver.inc;
|
|
16
19
|
const prerelease = semver.prerelease;
|
|
17
20
|
const rcompare = semver.rcompare;
|
|
@@ -55,7 +58,8 @@ const versionPublished = function (id, version) {
|
|
|
55
58
|
latest(id, links.STAGING),
|
|
56
59
|
]).then(([production, staging]) => {
|
|
57
60
|
const found = { production, staging };
|
|
58
|
-
return new
|
|
61
|
+
return new ArcError(
|
|
62
|
+
cfgcode.VERSION_EXISTS,
|
|
59
63
|
phrase.versionPublished(version, {
|
|
60
64
|
...found,
|
|
61
65
|
next: next(version, found),
|
|
@@ -64,6 +68,15 @@ const versionPublished = function (id, version) {
|
|
|
64
68
|
});
|
|
65
69
|
};
|
|
66
70
|
|
|
71
|
+
// [function]
|
|
72
|
+
|
|
73
|
+
// rejects with versionPublished's error.
|
|
74
|
+
const refuse = function (id, version) {
|
|
75
|
+
return versionPublished(id, version).then((err) => {
|
|
76
|
+
throw err;
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
67
80
|
// [export]
|
|
68
81
|
|
|
69
|
-
module.exports = { next,
|
|
82
|
+
module.exports = { next, refuse };
|
|
@@ -3,28 +3,33 @@
|
|
|
3
3
|
// [import]
|
|
4
4
|
|
|
5
5
|
const api = require("../../api");
|
|
6
|
+
const cfgcode = require("../../config/code");
|
|
7
|
+
const cfguri = require("../../config/uri");
|
|
8
|
+
const error = require("../../util/error");
|
|
6
9
|
const git = require("./git");
|
|
10
|
+
const given = require("../../parser/given");
|
|
7
11
|
const phrase = require("../../phrase");
|
|
8
12
|
const pm = require("./pm");
|
|
9
|
-
const project = require("../../parser/project");
|
|
10
13
|
const published = require("./published");
|
|
11
14
|
|
|
12
15
|
// [use]
|
|
13
16
|
|
|
17
|
+
const ArcError = error.ArcError;
|
|
14
18
|
const ensureClean = git.ensureClean;
|
|
15
19
|
const ensureLockFile = pm.ensureLockFile;
|
|
16
20
|
const ensureRemote = git.ensureRemote;
|
|
17
21
|
const ensureRepo = git.ensureRepo;
|
|
18
22
|
const ensureTagFree = git.ensureTagFree;
|
|
23
|
+
const ensureWorkflow = git.ensureWorkflow;
|
|
19
24
|
|
|
20
25
|
// [function]
|
|
21
26
|
|
|
22
|
-
// a public lookup's answer: found (200), missing (404), or
|
|
23
|
-
// other answer, such as when the API can't be reached.
|
|
27
|
+
// a public lookup's answer: found (200, with its data), missing (404), or
|
|
28
|
+
// null for any other answer, such as when the API can't be reached.
|
|
24
29
|
const lookup = function (request) {
|
|
25
30
|
return request.then(
|
|
26
|
-
() => 200,
|
|
27
|
-
(err) => (err.status === 404 ? 404 : null),
|
|
31
|
+
(data) => ({ data, status: 200 }),
|
|
32
|
+
(err) => (err.status === 404 ? { status: 404 } : null),
|
|
28
33
|
);
|
|
29
34
|
};
|
|
30
35
|
|
|
@@ -35,113 +40,96 @@ const lookup = function (request) {
|
|
|
35
40
|
// that's already published. The lookups are public, so this needs no login.
|
|
36
41
|
// when Arcadible can't be reached, it warns and pushes anyway: a release
|
|
37
42
|
// shouldn't depend on the API, and Arcadible still checks.
|
|
38
|
-
|
|
43
|
+
// resolves with the game's owner, or null when Arcadible can't be reached.
|
|
44
|
+
const ensurePublishable = function ({ handle, id, version }, handler) {
|
|
39
45
|
return Promise.all([
|
|
40
46
|
lookup(api.discoverGame(id)),
|
|
41
47
|
lookup(api.discoverGameBuild(id, version)),
|
|
42
|
-
])
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (handler.onLogInfo) {
|
|
49
|
-
handler.onLogInfo({
|
|
50
|
-
level: "warn",
|
|
51
|
-
message: phrase.versionUnchecked(version),
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
return build === 200 ? published.versionPublished(id, version) : null;
|
|
57
|
-
})
|
|
58
|
-
.then(cb);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// [function]
|
|
62
|
-
|
|
63
|
-
const deployRelease = function (game, { root }, handler, cb) {
|
|
64
|
-
ensureTagFree(root, game.version, (err) => {
|
|
65
|
-
if (err) {
|
|
66
|
-
cb(err);
|
|
67
|
-
return;
|
|
48
|
+
]).then(([game, build]) => {
|
|
49
|
+
if (game?.status === 404) {
|
|
50
|
+
throw new ArcError(
|
|
51
|
+
cfgcode.GAME_NOT_DEPLOYED,
|
|
52
|
+
phrase.gameNotDeployed(handle ?? id),
|
|
53
|
+
);
|
|
68
54
|
}
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
55
|
+
if (game === null || build === null) {
|
|
56
|
+
if (handler.onLogInfo) {
|
|
57
|
+
handler.onLogInfo({
|
|
58
|
+
level: "warn",
|
|
59
|
+
message: phrase.versionUnchecked(version),
|
|
60
|
+
});
|
|
73
61
|
}
|
|
74
|
-
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// [function]
|
|
80
|
-
|
|
81
|
-
const deployEnsureGit = function ({ root }, handler, cb) {
|
|
82
|
-
ensureRepo(root, (err) => {
|
|
83
|
-
if (err) {
|
|
84
|
-
cb(err);
|
|
85
|
-
return;
|
|
62
|
+
return null;
|
|
86
63
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
ensureRemote(root, cb);
|
|
93
|
-
});
|
|
64
|
+
return build.status === 200
|
|
65
|
+
? published.refuse(id, version)
|
|
66
|
+
: (game.data?.account ?? null);
|
|
94
67
|
});
|
|
95
68
|
};
|
|
96
69
|
|
|
97
70
|
// [function]
|
|
98
71
|
|
|
99
|
-
|
|
100
|
-
|
|
72
|
+
// the project's checks: a lockfile when there's a package.json, and a clean
|
|
73
|
+
// repository with the release workflow and a reachable origin.
|
|
74
|
+
const ensureProject = function (root) {
|
|
75
|
+
return ensureLockFile(root)
|
|
76
|
+
.then(() => ensureRepo(root))
|
|
77
|
+
.then(() => ensureClean(root))
|
|
78
|
+
.then(() => ensureWorkflow(root))
|
|
79
|
+
.then(() => ensureRemote(root));
|
|
101
80
|
};
|
|
102
81
|
|
|
103
82
|
// [function]
|
|
104
83
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
84
|
+
// fails when the game has no repository connected, which Arcadible would
|
|
85
|
+
// refuse (repo_not_connected) after GitHub Actions had built it. Only the
|
|
86
|
+
// game's owner can see its repository, so it's checked only when they're
|
|
87
|
+
// logged in, and skipped when Arcadible can't be reached.
|
|
88
|
+
const ensureConnected = function (current, { handle, id }, owner) {
|
|
89
|
+
if (!owner || current?.token || current?.user?.id !== owner) {
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
const headers = api.auth(current.session?.access_token);
|
|
93
|
+
return api.gameRepos(id, { select: "id" }, headers).then(
|
|
94
|
+
(repos) => {
|
|
95
|
+
if (!repos?.length) {
|
|
96
|
+
throw new ArcError(
|
|
97
|
+
cfgcode.REPO_NOT_CONNECTED,
|
|
98
|
+
phrase.gameRepoMissing(
|
|
99
|
+
handle ?? id,
|
|
100
|
+
`${cfguri.HOST_HTTP_STUDIO}/games/${id}/repository`,
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
() => null,
|
|
106
|
+
);
|
|
113
107
|
};
|
|
114
108
|
|
|
115
109
|
// [function]
|
|
116
110
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
111
|
+
// the release's checks: its tag is free, Arcadible would publish it, and its
|
|
112
|
+
// repository is connected. resolves with the version, and the game's owner,
|
|
113
|
+
// who alone can wait for the release.
|
|
114
|
+
const ensureRelease = function (root, current, game, handler) {
|
|
115
|
+
return ensureTagFree(root, game.version)
|
|
116
|
+
.then(() => ensurePublishable(game, handler))
|
|
117
|
+
.then((owner) =>
|
|
118
|
+
Promise.all([owner, ensureConnected(current, game, owner)]),
|
|
119
|
+
)
|
|
120
|
+
.then(([owner]) => ({ owner, to: game.version }));
|
|
125
121
|
};
|
|
126
122
|
|
|
127
123
|
// [function]
|
|
128
124
|
|
|
129
|
-
const main = function (
|
|
125
|
+
const main = function (current, conf, { onPredeployBegin, ...handler }) {
|
|
126
|
+
const root = conf.root;
|
|
130
127
|
if (onPredeployBegin) {
|
|
131
128
|
onPredeployBegin();
|
|
132
129
|
}
|
|
133
|
-
return
|
|
134
|
-
({ game }) =>
|
|
135
|
-
|
|
136
|
-
deploy(game, { root }, handler, (err, data) => {
|
|
137
|
-
if (err) {
|
|
138
|
-
reject(err);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
resolve(data);
|
|
142
|
-
});
|
|
143
|
-
}),
|
|
144
|
-
);
|
|
130
|
+
return given(conf)
|
|
131
|
+
.then(({ game }) => Promise.all([game, ensureProject(root)]))
|
|
132
|
+
.then(([game]) => ensureRelease(root, current, game, handler));
|
|
145
133
|
};
|
|
146
134
|
|
|
147
135
|
// [export]
|