@tapestry-mud/cli 0.5.0 → 0.6.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 +15 -2
- package/package.json +1 -1
- package/src/commands/install.js +1 -1
- package/src/commands/link.js +64 -7
- package/src/commands/preset.js +9 -2
- package/src/lib/links.js +28 -1
- package/src/lib/registry-client.js +13 -1
package/bin/tapestry.js
CHANGED
|
@@ -27,7 +27,7 @@ const { stopCmd } = require('../src/commands/stop');
|
|
|
27
27
|
const { changePassword } = require('../src/commands/change-password');
|
|
28
28
|
const { unpublish } = require('../src/commands/unpublish');
|
|
29
29
|
const { distTagSet, distTagList } = require('../src/commands/dist-tag');
|
|
30
|
-
const { presetSet } = require('../src/commands/preset');
|
|
30
|
+
const { presetSet, presetDelete } = require('../src/commands/preset');
|
|
31
31
|
|
|
32
32
|
const program = new Command();
|
|
33
33
|
|
|
@@ -169,6 +169,18 @@ presetCmd
|
|
|
169
169
|
}
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
+
presetCmd
|
|
173
|
+
.command('delete <name>')
|
|
174
|
+
.description('Delete a preset from the registry')
|
|
175
|
+
.action(async (name) => {
|
|
176
|
+
try {
|
|
177
|
+
await presetDelete(name);
|
|
178
|
+
} catch (e) {
|
|
179
|
+
console.error(`error: ${e.message}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
172
184
|
program
|
|
173
185
|
.command('install [package]')
|
|
174
186
|
.description('Install a package or all dependencies from tapestry.yaml')
|
|
@@ -342,12 +354,13 @@ program
|
|
|
342
354
|
.command('link [path]')
|
|
343
355
|
.description('Attach a local pack working copy to this project (use --list to show links)')
|
|
344
356
|
.option('--list', 'List active links instead of creating one')
|
|
357
|
+
.option('--skip-install', 'Skip dependency resolution; warn about missing deps instead')
|
|
345
358
|
.action(async (linkPath, options) => {
|
|
346
359
|
try {
|
|
347
360
|
if (options.list || !linkPath) {
|
|
348
361
|
await linkList();
|
|
349
362
|
} else {
|
|
350
|
-
await link(linkPath);
|
|
363
|
+
await link(linkPath, { noInstall: !!options.skipInstall });
|
|
351
364
|
}
|
|
352
365
|
} catch (e) {
|
|
353
366
|
console.error(`error: ${e.message}`);
|
package/package.json
CHANGED
package/src/commands/install.js
CHANGED
package/src/commands/link.js
CHANGED
|
@@ -4,9 +4,14 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const {
|
|
6
6
|
readLinks, addLink, removeLink, readPackManifest,
|
|
7
|
-
removeMaterializedLink, checkMissingDeps,
|
|
7
|
+
removeMaterializedLink, checkMissingDeps, partitionDeps,
|
|
8
8
|
} = require('../lib/links');
|
|
9
9
|
const { addPackageToBoot, removePackageFromBoot } = require('../lib/boot');
|
|
10
|
+
const { resolve } = require('../lib/semver-resolver');
|
|
11
|
+
const { installResolved, packInstallPath } = require('./install');
|
|
12
|
+
const { readLock, writeLock } = require('../lib/lock-file');
|
|
13
|
+
const { loadToken } = require('../lib/auth');
|
|
14
|
+
const { DEFAULT_REGISTRY } = require('../lib/registry-client');
|
|
10
15
|
|
|
11
16
|
function requireProject(cwd) {
|
|
12
17
|
if (!fs.existsSync(path.join(cwd, 'tapestry.yaml'))) {
|
|
@@ -25,7 +30,7 @@ function ensureGitignore(cwd) {
|
|
|
25
30
|
}
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
async function link(targetPath, { cwd = process.cwd() } = {}) {
|
|
33
|
+
async function link(targetPath, { cwd = process.cwd(), noInstall = false, registryUrl = DEFAULT_REGISTRY } = {}) {
|
|
29
34
|
requireProject(cwd);
|
|
30
35
|
const absPath = path.resolve(cwd, targetPath);
|
|
31
36
|
if (!fs.existsSync(absPath)) {
|
|
@@ -41,14 +46,66 @@ async function link(targetPath, { cwd = process.cwd() } = {}) {
|
|
|
41
46
|
addPackageToBoot(cwd, name, manifest);
|
|
42
47
|
ensureGitignore(cwd);
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
// Warn if the pack is marked inactive — fires on all paths
|
|
46
50
|
if (manifest.active === false) {
|
|
47
51
|
console.warn(` warning: ${name} is marked active: false; it will not load until activated`);
|
|
48
52
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.
|
|
53
|
+
|
|
54
|
+
if (noInstall) {
|
|
55
|
+
console.log(`linked ${name} -> ${absPath}`);
|
|
56
|
+
for (const dep of checkMissingDeps(cwd, manifest)) {
|
|
57
|
+
const range = manifest.dependencies[dep];
|
|
58
|
+
console.warn(` warning: missing dependency ${dep} (${range}) -- run: tapestry install ${dep}`);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { needsInstall } = partitionDeps(cwd, manifest);
|
|
64
|
+
|
|
65
|
+
if (Object.keys(needsInstall).length === 0) {
|
|
66
|
+
console.log(`linked ${name} -> ${absPath}`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let toRollback = [];
|
|
71
|
+
try {
|
|
72
|
+
const token = loadToken();
|
|
73
|
+
const resolved = await resolve(needsInstall, registryUrl, token);
|
|
74
|
+
|
|
75
|
+
// New installs: not on disk yet. Upgrade targets: in needsInstall AND on disk
|
|
76
|
+
// (installResolved deletes the old dir before downloading; track so rollback removes the boot entry)
|
|
77
|
+
toRollback = [
|
|
78
|
+
...Object.keys(resolved).filter((n) => !fs.existsSync(packInstallPath(cwd, n))),
|
|
79
|
+
...Object.keys(needsInstall).filter((n) => fs.existsSync(packInstallPath(cwd, n))),
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
await installResolved(cwd, resolved, token);
|
|
83
|
+
|
|
84
|
+
const existingLock = readLock(cwd);
|
|
85
|
+
const mergedResolved = Object.assign({}, (existingLock && existingLock.resolved) || {}, resolved);
|
|
86
|
+
writeLock(cwd, {
|
|
87
|
+
lockfile_version: 1,
|
|
88
|
+
...(existingLock && existingLock.deps_hash ? { deps_hash: existingLock.deps_hash } : {}),
|
|
89
|
+
resolved: mergedResolved,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log(`linked ${name} -> ${absPath}`);
|
|
93
|
+
for (const [pkgName, info] of Object.entries(resolved)) {
|
|
94
|
+
console.log(` installed ${pkgName}@${info.version} (dependency of ${name})`);
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
removeLink(cwd, name);
|
|
98
|
+
removePackageFromBoot(cwd, name);
|
|
99
|
+
for (const pkgName of toRollback) {
|
|
100
|
+
const installPath = packInstallPath(cwd, pkgName);
|
|
101
|
+
if (fs.existsSync(installPath)) {
|
|
102
|
+
fs.rmSync(installPath, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
removePackageFromBoot(cwd, pkgName);
|
|
105
|
+
}
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Cannot resolve dependencies for ${name} — ${err.message}. Use --skip-install to link without dependency resolution.`
|
|
108
|
+
);
|
|
52
109
|
}
|
|
53
110
|
}
|
|
54
111
|
|
package/src/commands/preset.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { requireToken } = require('../lib/auth');
|
|
4
|
-
const { patchPreset, DEFAULT_REGISTRY } = require('../lib/registry-client');
|
|
4
|
+
const { patchPreset, deletePreset, DEFAULT_REGISTRY } = require('../lib/registry-client');
|
|
5
5
|
|
|
6
6
|
async function presetSet(name, version, engineChannel, packs, { registryUrl = DEFAULT_REGISTRY } = {}) {
|
|
7
7
|
const token = requireToken();
|
|
@@ -10,4 +10,11 @@ async function presetSet(name, version, engineChannel, packs, { registryUrl = DE
|
|
|
10
10
|
console.log('Done.');
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
async function presetDelete(name, { registryUrl = DEFAULT_REGISTRY } = {}) {
|
|
14
|
+
const token = requireToken();
|
|
15
|
+
await deletePreset(name, token, registryUrl);
|
|
16
|
+
console.log(` Deleted preset '${name}'`);
|
|
17
|
+
console.log('Done.');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { presetSet, presetDelete };
|
package/src/lib/links.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
+
const semver = require('semver');
|
|
5
6
|
const { readYaml, writeYaml } = require('../util/yaml');
|
|
6
7
|
const { PACK_MANIFEST } = require('./manifest');
|
|
7
8
|
|
|
@@ -113,8 +114,34 @@ function checkMissingDeps(cwd, manifest) {
|
|
|
113
114
|
return missing;
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
function partitionDeps(cwd, manifest) {
|
|
118
|
+
const deps = (manifest && manifest.dependencies) || {};
|
|
119
|
+
const needsInstall = {};
|
|
120
|
+
if (Object.keys(deps).length === 0) {
|
|
121
|
+
return { needsInstall };
|
|
122
|
+
}
|
|
123
|
+
const { links } = readLinks(cwd);
|
|
124
|
+
for (const [depName, range] of Object.entries(deps)) {
|
|
125
|
+
if (depName in links) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const installPath = packLinkPath(cwd, depName);
|
|
129
|
+
if (fs.existsSync(installPath)) {
|
|
130
|
+
const manifestPath = path.join(installPath, PACK_MANIFEST);
|
|
131
|
+
if (fs.existsSync(manifestPath)) {
|
|
132
|
+
const installed = readYaml(manifestPath) || {};
|
|
133
|
+
if (installed.version && semver.satisfies(installed.version, range)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
needsInstall[depName] = range;
|
|
139
|
+
}
|
|
140
|
+
return { needsInstall };
|
|
141
|
+
}
|
|
142
|
+
|
|
116
143
|
module.exports = {
|
|
117
144
|
LINKS_FILE, readLinks, writeLinks, addLink, removeLink,
|
|
118
145
|
readPackManifest, packLinkPath, containerPackTarget, dockerLinkMounts,
|
|
119
|
-
materializeLinks, removeMaterializedLink, checkMissingDeps,
|
|
146
|
+
materializeLinks, removeMaterializedLink, checkMissingDeps, partitionDeps,
|
|
120
147
|
};
|
|
@@ -116,7 +116,19 @@ async function patchPreset(name, payload, token, registryUrl = DEFAULT_REGISTRY)
|
|
|
116
116
|
return res.json();
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
async function deletePreset(name, token, registryUrl = DEFAULT_REGISTRY) {
|
|
120
|
+
const url = `${registryUrl.replace(/\/$/, '')}/v1/admin/presets/${name}`;
|
|
121
|
+
const res = await fetch(url, {
|
|
122
|
+
method: 'DELETE',
|
|
123
|
+
headers: {
|
|
124
|
+
Authorization: `Bearer ${token}`,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
await throwIfError(res, `Failed to delete preset '${name}'`);
|
|
128
|
+
return res.json();
|
|
129
|
+
}
|
|
130
|
+
|
|
119
131
|
module.exports = {
|
|
120
132
|
fetchPackageMetadata, fetchTarball, throwIfError, DEFAULT_REGISTRY,
|
|
121
|
-
fetchPreset, fetchPresetList, patchDistTag, listDistTags, patchPreset,
|
|
133
|
+
fetchPreset, fetchPresetList, patchDistTag, listDistTags, patchPreset, deletePreset,
|
|
122
134
|
};
|