@glash/cli 0.2.45 → 0.2.47
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/glash.mjs +1 -1
- package/commands/pull.mjs +63 -12
- package/package.json +1 -1
package/bin/glash.mjs
CHANGED
|
@@ -88,7 +88,7 @@ ${color.bold('DATABASE')}
|
|
|
88
88
|
database upload Restore the latest/local dump into linked glashDB database
|
|
89
89
|
|
|
90
90
|
${color.bold('PULL')}
|
|
91
|
-
pull [<slug>] [--out dir] [--no-env]
|
|
91
|
+
pull [<slug>] [--out dir] [--no-env] Unpack a project (asks which, if no slug)
|
|
92
92
|
|
|
93
93
|
${color.bold('SYSTEM')}
|
|
94
94
|
update Update @glash/cli to the latest version
|
package/commands/pull.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// glash pull
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// Unpacks a glashDB project's active deployed image to the current directory,
|
|
4
|
+
// plus its .env. Works however long ago the project was deployed.
|
|
5
5
|
//
|
|
6
|
-
// glash pull →
|
|
6
|
+
// glash pull → pick a project from your glashDB account
|
|
7
7
|
// glash pull <slug> → pulls a specific project by slug
|
|
8
8
|
// glash pull --out ../somewhere → write to a different directory
|
|
9
9
|
// glash pull --no-env → skip writing .env
|
|
@@ -16,7 +16,8 @@ import { createWriteStream } from 'node:fs';
|
|
|
16
16
|
import { Readable } from 'node:stream';
|
|
17
17
|
import { argv, cwd, exit, stdout, stderr } from 'node:process';
|
|
18
18
|
import { API_URL, getToken, readProjectLink } from '../lib/config.mjs';
|
|
19
|
-
import {
|
|
19
|
+
import { apiGet } from '../lib/api.mjs';
|
|
20
|
+
import { color, info, fail, prompt } from '../lib/ui.mjs';
|
|
20
21
|
|
|
21
22
|
export async function pull(parsed) {
|
|
22
23
|
const flags = parsed?.flags ?? {};
|
|
@@ -25,15 +26,12 @@ export async function pull(parsed) {
|
|
|
25
26
|
const token = await getToken();
|
|
26
27
|
if (!token) return fail('Not signed in. Run `glash login` first.');
|
|
27
28
|
|
|
28
|
-
// Resolve which project to pull
|
|
29
|
+
// Resolve which project to pull. With no argument, pick from the projects on
|
|
30
|
+
// this glashDB account rather than guessing.
|
|
29
31
|
let slugOrId = positional[0] ?? flags.project;
|
|
30
32
|
if (!slugOrId) {
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
else if (link?.projectId) slugOrId = link.projectId;
|
|
34
|
-
}
|
|
35
|
-
if (!slugOrId) {
|
|
36
|
-
return fail('No project specified. Pass `glash pull <slug>` or run `glash link <slug>` first.');
|
|
33
|
+
slugOrId = await chooseProject();
|
|
34
|
+
if (!slugOrId) return;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
const outDir = resolve(flags.out ? String(flags.out) : cwd());
|
|
@@ -52,6 +50,51 @@ export async function pull(parsed) {
|
|
|
52
50
|
stdout.write(color.dim(`\n next: cd ${outDir} && glash deploy\n`));
|
|
53
51
|
}
|
|
54
52
|
|
|
53
|
+
// Lists this account's projects and asks which one to pull. The project linked
|
|
54
|
+
// to the current directory, if any, is the default on Enter.
|
|
55
|
+
async function chooseProject() {
|
|
56
|
+
let projects;
|
|
57
|
+
try {
|
|
58
|
+
projects = await apiGet('/projects');
|
|
59
|
+
} catch (e) {
|
|
60
|
+
fail(`Could not list your projects: ${e.message}`);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (!projects?.length) {
|
|
64
|
+
fail('This account has no projects yet. Run `glash projects:new` first.');
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const link = await readProjectLink().catch(() => null);
|
|
69
|
+
const linkedIndex = projects.findIndex(
|
|
70
|
+
(p) => (link?.projectId && p.id === link.projectId) || (link?.slug && p.slug === link.slug),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
stdout.write('\n Your projects:\n\n');
|
|
74
|
+
projects.forEach((p, i) => {
|
|
75
|
+
const marker = i === linkedIndex ? color.dim(' (linked here)') : '';
|
|
76
|
+
stdout.write(` ${String(i + 1).padStart(2)}. ${p.slug}${p.name && p.name !== p.slug ? color.dim(` - ${p.name}`) : ''}${marker}\n`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const fallback = linkedIndex >= 0 ? projects[linkedIndex] : null;
|
|
80
|
+
const suffix = fallback ? ` [${fallback.slug}]` : '';
|
|
81
|
+
const answer = (await prompt(`\n Which project do you want to pull?${suffix} `)).trim();
|
|
82
|
+
|
|
83
|
+
if (!answer) {
|
|
84
|
+
if (fallback) return fallback.slug;
|
|
85
|
+
fail('No project selected.');
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const byNumber = Number(answer);
|
|
89
|
+
if (Number.isInteger(byNumber) && byNumber >= 1 && byNumber <= projects.length) {
|
|
90
|
+
return projects[byNumber - 1].slug;
|
|
91
|
+
}
|
|
92
|
+
const byName = projects.find((p) => p.slug === answer || p.id === answer);
|
|
93
|
+
if (byName) return byName.slug;
|
|
94
|
+
fail(`No project named "${answer}" on this account.`);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
55
98
|
async function downloadAndExtract(token, slugOrId, outDir) {
|
|
56
99
|
const url = `${API_URL}/projects/${encodeURIComponent(slugOrId)}/source.tar.gz`;
|
|
57
100
|
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
@@ -61,12 +104,20 @@ async function downloadAndExtract(token, slugOrId, outDir) {
|
|
|
61
104
|
}
|
|
62
105
|
if (!res.body) throw new Error('empty response from server');
|
|
63
106
|
|
|
107
|
+
// The server says which of the two sources it could serve.
|
|
108
|
+
if (res.headers.get('x-glash-pull-mode') === 'build-directory') {
|
|
109
|
+
stderr.write(color.dim(
|
|
110
|
+
' ! this host runs an older glash agent, so the live image could not be unpacked\n' +
|
|
111
|
+
' you are getting the build directory instead, which may hold only a Dockerfile and env files\n',
|
|
112
|
+
));
|
|
113
|
+
}
|
|
114
|
+
|
|
64
115
|
// Stream-pipe into `tar -xz -C outDir` via stdin
|
|
65
116
|
const child = spawnSync('tar', ['-xz', '-C', outDir], { input: Buffer.from(await res.arrayBuffer()) });
|
|
66
117
|
if (child.status !== 0) {
|
|
67
118
|
throw new Error(`tar extract failed: ${child.stderr?.toString() || 'unknown'}`);
|
|
68
119
|
}
|
|
69
|
-
info(color.dim(` ✓
|
|
120
|
+
info(color.dim(` ✓ project files unpacked to ${outDir}`));
|
|
70
121
|
}
|
|
71
122
|
|
|
72
123
|
async function downloadEnv(token, slugOrId, outDir, force) {
|
package/package.json
CHANGED