@tapestry-mud/cli 0.3.10 → 0.4.0
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/tapestry.js +1 -1
- package/package.json +1 -1
- package/src/commands/create-pack.js +2 -1
- package/src/commands/init.js +24 -1
- package/src/commands/install.js +20 -5
- package/src/commands/list.js +2 -1
- package/src/commands/pack.js +2 -1
- package/src/commands/publish.js +2 -1
- package/src/commands/update.js +2 -1
- package/src/commands/validate.js +16 -3
- package/src/lib/lock-file.js +7 -1
- package/src/lib/manifest.js +5 -0
- package/src/scaffold/templates.js +1 -1
package/bin/tapestry.js
CHANGED
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { generatePackFiles } = require('../scaffold/templates');
|
|
6
|
+
const { PACK_MANIFEST } = require('../lib/manifest');
|
|
6
7
|
|
|
7
8
|
function parseName(name) {
|
|
8
9
|
const scopedMatch = name.match(/^@([a-z0-9-]+)\/([a-z0-9-]+)$/);
|
|
@@ -59,7 +60,7 @@ function createPack(name, cwd) {
|
|
|
59
60
|
console.log(` ${file.path}`);
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
|
-
console.log(
|
|
63
|
+
console.log(`\nEdit ${PACK_MANIFEST}, then run: tapestry validate`);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
package/src/commands/init.js
CHANGED
|
@@ -28,7 +28,7 @@ function buildManifest(name, deps, engineVersion, engineChannel) {
|
|
|
28
28
|
].join('\n');
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function buildServerYaml({ serverName, adminHandle, adminPassword, telemetry }) {
|
|
31
|
+
function buildServerYaml({ serverName, adminHandle, adminEmail, adminPassword, telemetry }) {
|
|
32
32
|
const telemetryBlock = telemetry
|
|
33
33
|
? [
|
|
34
34
|
`telemetry:`,
|
|
@@ -59,6 +59,7 @@ function buildServerYaml({ serverName, adminHandle, adminPassword, telemetry })
|
|
|
59
59
|
``,
|
|
60
60
|
`admin:`,
|
|
61
61
|
` handle: ${adminHandle}`,
|
|
62
|
+
` email: ${adminEmail}`,
|
|
62
63
|
` password: ${adminPassword}`,
|
|
63
64
|
``,
|
|
64
65
|
`# --- Telemetry (OpenTelemetry) ---`,
|
|
@@ -105,6 +106,15 @@ function buildServerYaml({ serverName, adminHandle, adminPassword, telemetry })
|
|
|
105
106
|
`# pre_auth:`,
|
|
106
107
|
`# enabled: false`,
|
|
107
108
|
`# token_expiry_seconds: 60`,
|
|
109
|
+
``,
|
|
110
|
+
`# --- Accounts ---`,
|
|
111
|
+
`# accounts:`,
|
|
112
|
+
`# max_concurrent_characters: 1`,
|
|
113
|
+
``,
|
|
114
|
+
`# --- Link-Dead (player disconnect grace period) ---`,
|
|
115
|
+
`# link_dead:`,
|
|
116
|
+
`# enabled: true`,
|
|
117
|
+
`# timeout_seconds: 120`,
|
|
108
118
|
].join('\n');
|
|
109
119
|
}
|
|
110
120
|
|
|
@@ -157,6 +167,7 @@ async function init(cwd, { registryUrl = DEFAULT_REGISTRY, yes = false, prompter
|
|
|
157
167
|
answers = {
|
|
158
168
|
gameName: dirName,
|
|
159
169
|
adminHandle: 'admin',
|
|
170
|
+
adminEmail: 'admin@localhost',
|
|
160
171
|
adminPassword: 'changeme',
|
|
161
172
|
telemetry: false,
|
|
162
173
|
};
|
|
@@ -177,6 +188,17 @@ async function init(cwd, { registryUrl = DEFAULT_REGISTRY, yes = false, prompter
|
|
|
177
188
|
message: 'Admin handle:',
|
|
178
189
|
validate: (v) => (v.trim().length > 0 && !/\s/.test(v)) || 'Required, no spaces',
|
|
179
190
|
},
|
|
191
|
+
{
|
|
192
|
+
type: 'input',
|
|
193
|
+
name: 'adminEmail',
|
|
194
|
+
message: 'Admin email:',
|
|
195
|
+
validate: (v) => {
|
|
196
|
+
v = v.trim();
|
|
197
|
+
if (!v) { return 'Required'; }
|
|
198
|
+
if (!v.includes('@') || !v.includes('.')) { return 'Must be a valid email'; }
|
|
199
|
+
return true;
|
|
200
|
+
},
|
|
201
|
+
},
|
|
180
202
|
{
|
|
181
203
|
type: 'password',
|
|
182
204
|
name: 'adminPassword',
|
|
@@ -208,6 +230,7 @@ async function init(cwd, { registryUrl = DEFAULT_REGISTRY, yes = false, prompter
|
|
|
208
230
|
buildServerYaml({
|
|
209
231
|
serverName: answers.gameName,
|
|
210
232
|
adminHandle: answers.adminHandle,
|
|
233
|
+
adminEmail: answers.adminEmail,
|
|
211
234
|
adminPassword: answers.adminPassword,
|
|
212
235
|
telemetry: answers.telemetry,
|
|
213
236
|
})
|
package/src/commands/install.js
CHANGED
|
@@ -5,11 +5,12 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { readYaml, writeYaml } = require('../util/yaml');
|
|
7
7
|
const { resolve } = require('../lib/semver-resolver');
|
|
8
|
-
const { readLock, writeLock } = require('../lib/lock-file');
|
|
8
|
+
const { readLock, writeLock, hashDeps } = require('../lib/lock-file');
|
|
9
9
|
const { fetchTarball, DEFAULT_REGISTRY } = require('../lib/registry-client');
|
|
10
10
|
const { verifyIntegrity, saveTarball, extractTarball } = require('../lib/tarball');
|
|
11
11
|
const { addPackageToBoot } = require('../lib/boot');
|
|
12
12
|
const { loadToken } = require('../lib/auth');
|
|
13
|
+
const { PACK_MANIFEST } = require('../lib/manifest');
|
|
13
14
|
|
|
14
15
|
function packInstallPath(cwd, packageName) {
|
|
15
16
|
const parts = packageName.split('/');
|
|
@@ -25,6 +26,9 @@ function parsePackageArg(arg) {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
function isLockCurrent(manifestDeps, lock) {
|
|
29
|
+
if (!lock.deps_hash || lock.deps_hash !== hashDeps(manifestDeps)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
28
32
|
const lockResolved = lock.resolved || {};
|
|
29
33
|
return Object.keys(manifestDeps).every((name) => lockResolved[name]);
|
|
30
34
|
}
|
|
@@ -34,8 +38,18 @@ async function installResolved(cwd, resolved, token) {
|
|
|
34
38
|
const destDir = packInstallPath(cwd, packageName);
|
|
35
39
|
|
|
36
40
|
if (fs.existsSync(destDir)) {
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
const installedManifestPath = path.join(destDir, PACK_MANIFEST);
|
|
42
|
+
if (fs.existsSync(installedManifestPath)) {
|
|
43
|
+
const installed = readYaml(installedManifestPath);
|
|
44
|
+
if (installed.version === info.version) {
|
|
45
|
+
console.log(` already installed ${packageName}@${info.version}`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
console.log(` upgrading ${packageName} ${installed.version} -> ${info.version}`);
|
|
49
|
+
} else {
|
|
50
|
+
console.log(` reinstalling ${packageName}@${info.version} (missing manifest)`);
|
|
51
|
+
}
|
|
52
|
+
fs.rmSync(destDir, { recursive: true });
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
console.log(` installing ${packageName}@${info.version}`);
|
|
@@ -54,7 +68,7 @@ async function installResolved(cwd, resolved, token) {
|
|
|
54
68
|
}
|
|
55
69
|
}
|
|
56
70
|
|
|
57
|
-
const packManifest = readYaml(path.join(destDir,
|
|
71
|
+
const packManifest = readYaml(path.join(destDir, PACK_MANIFEST));
|
|
58
72
|
addPackageToBoot(cwd, packageName, packManifest);
|
|
59
73
|
}
|
|
60
74
|
}
|
|
@@ -95,7 +109,8 @@ async function install(packageArg, { cwd = process.cwd(), registryUrl = DEFAULT_
|
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
await installResolved(cwd, resolved, token);
|
|
98
|
-
|
|
112
|
+
const deps = manifest.dependencies || {};
|
|
113
|
+
writeLock(cwd, { lockfile_version: 1, deps_hash: hashDeps(deps), resolved });
|
|
99
114
|
console.log('Done.');
|
|
100
115
|
}
|
|
101
116
|
|
package/src/commands/list.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const { readLock } = require('../lib/lock-file');
|
|
6
6
|
const { readBoot } = require('../lib/boot');
|
|
7
7
|
const { readYaml } = require('../util/yaml');
|
|
8
|
+
const { PACK_MANIFEST } = require('../lib/manifest');
|
|
8
9
|
|
|
9
10
|
function packInstallPath(cwd, packageName) {
|
|
10
11
|
const parts = packageName.split('/');
|
|
@@ -32,7 +33,7 @@ async function list({ cwd = process.cwd() } = {}) {
|
|
|
32
33
|
const enabled = boot.packs[pkgName]?.enabled !== false ? 'enabled' : 'disabled';
|
|
33
34
|
|
|
34
35
|
let type = '';
|
|
35
|
-
const packManifestPath = path.join(packInstallPath(cwd, pkgName),
|
|
36
|
+
const packManifestPath = path.join(packInstallPath(cwd, pkgName), PACK_MANIFEST);
|
|
36
37
|
if (fs.existsSync(packManifestPath)) {
|
|
37
38
|
try {
|
|
38
39
|
type = readYaml(packManifestPath).type || '';
|
package/src/commands/pack.js
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { readYaml } = require('../util/yaml');
|
|
5
5
|
const { buildTarball, computeIntegrity } = require('../lib/tarball-builder');
|
|
6
|
+
const { PACK_MANIFEST } = require('../lib/manifest');
|
|
6
7
|
const { validate } = require('./validate');
|
|
7
8
|
|
|
8
9
|
async function pack({ cwd = process.cwd() } = {}) {
|
|
9
10
|
validate({ cwd });
|
|
10
11
|
|
|
11
|
-
const manifest = readYaml(path.join(cwd,
|
|
12
|
+
const manifest = readYaml(path.join(cwd, PACK_MANIFEST));
|
|
12
13
|
const shortName = manifest.name.split('/')[1];
|
|
13
14
|
const outputPath = path.join(cwd, `${shortName}-${manifest.version}.tgz`);
|
|
14
15
|
|
package/src/commands/publish.js
CHANGED
|
@@ -8,13 +8,14 @@ const FormData = require('form-data');
|
|
|
8
8
|
const { readYaml } = require('../util/yaml');
|
|
9
9
|
const { validate } = require('./validate');
|
|
10
10
|
const { buildTarball, computeIntegrity } = require('../lib/tarball-builder');
|
|
11
|
+
const { PACK_MANIFEST } = require('../lib/manifest');
|
|
11
12
|
const { requireToken } = require('../lib/auth');
|
|
12
13
|
const { DEFAULT_REGISTRY, throwIfError } = require('../lib/registry-client');
|
|
13
14
|
|
|
14
15
|
async function publish({ cwd = process.cwd(), registryUrl = DEFAULT_REGISTRY } = {}) {
|
|
15
16
|
validate({ cwd });
|
|
16
17
|
|
|
17
|
-
const manifest = readYaml(path.join(cwd,
|
|
18
|
+
const manifest = readYaml(path.join(cwd, PACK_MANIFEST));
|
|
18
19
|
const token = requireToken();
|
|
19
20
|
|
|
20
21
|
const shortName = manifest.name.split('/')[1];
|
package/src/commands/update.js
CHANGED
|
@@ -9,6 +9,7 @@ const { readLock, writeLock } = require('../lib/lock-file');
|
|
|
9
9
|
const { fetchTarball, DEFAULT_REGISTRY } = require('../lib/registry-client');
|
|
10
10
|
const { verifyIntegrity, saveTarball, extractTarball } = require('../lib/tarball');
|
|
11
11
|
const { addPackageToBoot } = require('../lib/boot');
|
|
12
|
+
const { PACK_MANIFEST } = require('../lib/manifest');
|
|
12
13
|
|
|
13
14
|
function packInstallPath(cwd, packageName) {
|
|
14
15
|
const parts = packageName.split('/');
|
|
@@ -69,7 +70,7 @@ async function update(packageArg, { cwd = process.cwd(), registryUrl = DEFAULT_R
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
const packManifest = readYaml(path.join(destDir,
|
|
73
|
+
const packManifest = readYaml(path.join(destDir, PACK_MANIFEST));
|
|
73
74
|
addPackageToBoot(cwd, packageName, packManifest);
|
|
74
75
|
}
|
|
75
76
|
|
package/src/commands/validate.js
CHANGED
|
@@ -4,11 +4,20 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { readYaml } = require('../util/yaml');
|
|
6
6
|
const { validatePackageManifest } = require('../schema/manifest');
|
|
7
|
+
const { PACK_MANIFEST } = require('../lib/manifest');
|
|
7
8
|
|
|
8
9
|
function validate({ cwd = process.cwd() } = {}) {
|
|
9
|
-
const manifestPath = path.join(cwd,
|
|
10
|
+
const manifestPath = path.join(cwd, PACK_MANIFEST);
|
|
10
11
|
if (!fs.existsSync(manifestPath)) {
|
|
11
|
-
|
|
12
|
+
const serverPath = path.join(cwd, 'tapestry.yaml');
|
|
13
|
+
if (fs.existsSync(serverPath)) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`No ${PACK_MANIFEST} found in current directory. ` +
|
|
16
|
+
`The tapestry.yaml here is a server manifest. ` +
|
|
17
|
+
`Pack validation requires ${PACK_MANIFEST}.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`No ${PACK_MANIFEST} found in current directory`);
|
|
12
21
|
}
|
|
13
22
|
|
|
14
23
|
const data = readYaml(manifestPath);
|
|
@@ -18,7 +27,11 @@ function validate({ cwd = process.cwd() } = {}) {
|
|
|
18
27
|
if (!result.success) {
|
|
19
28
|
for (const issue of result.error.issues) {
|
|
20
29
|
const fieldPath = issue.path.join('.') || 'root';
|
|
21
|
-
|
|
30
|
+
let message = issue.message;
|
|
31
|
+
if (fieldPath === 'engine' && data.engine && typeof data.engine === 'object') {
|
|
32
|
+
message += `. engine must be a version constraint string in pack manifests (e.g. '>=0.0.1'). Object format is for server manifests (tapestry.yaml).`;
|
|
33
|
+
}
|
|
34
|
+
console.log(` error: ${fieldPath} - ${message}`);
|
|
22
35
|
}
|
|
23
36
|
throw new Error(`${result.error.issues.length} validation error(s)`);
|
|
24
37
|
}
|
package/src/lib/lock-file.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const crypto = require('crypto');
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
const { readYaml, writeYaml } = require('../util/yaml');
|
|
6
7
|
|
|
7
8
|
const LOCK_FILE = 'tapestry-lock.yaml';
|
|
8
9
|
|
|
10
|
+
function hashDeps(deps) {
|
|
11
|
+
const sorted = Object.keys(deps).sort().map((k) => `${k}@${deps[k]}`).join('\n');
|
|
12
|
+
return crypto.createHash('sha256').update(sorted).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
function readLock(cwd) {
|
|
10
16
|
const lockPath = path.join(cwd, LOCK_FILE);
|
|
11
17
|
if (!fs.existsSync(lockPath)) {
|
|
@@ -18,4 +24,4 @@ function writeLock(cwd, lock) {
|
|
|
18
24
|
writeYaml(path.join(cwd, LOCK_FILE), lock);
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
module.exports = { readLock, writeLock };
|
|
27
|
+
module.exports = { readLock, writeLock, hashDeps };
|
|
@@ -404,7 +404,7 @@ see_also: [help, commands]
|
|
|
404
404
|
|
|
405
405
|
function generatePackFiles({ scopedName, shortName }) {
|
|
406
406
|
return [
|
|
407
|
-
{ path: '
|
|
407
|
+
{ path: 'pack.yaml', content: manifestTemplate(scopedName) },
|
|
408
408
|
{ path: 'tags.yml', content: tagsTemplate() },
|
|
409
409
|
{ path: 'areas/example-area/area.yaml', content: areaTemplate() },
|
|
410
410
|
{ path: 'areas/example-area/rooms/town-square.yaml', content: roomTemplate(shortName) },
|