@glash/cli 0.1.1 → 0.2.1
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 +6 -3
- package/commands/deploy.mjs +117 -12
- package/commands/deployments.mjs +1 -1
- package/commands/projects.mjs +12 -3
- package/commands/pull.mjs +109 -10
- package/lib/zip.mjs +28 -1
- package/package.json +2 -2
package/bin/glash.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { argv, exit } from 'node:process';
|
|
|
3
3
|
import { parseArgs } from '../lib/args.mjs';
|
|
4
4
|
import { info, fail, color } from '../lib/ui.mjs';
|
|
5
5
|
|
|
6
|
-
const VERSION = '0.
|
|
6
|
+
const VERSION = '0.2.1';
|
|
7
7
|
|
|
8
8
|
const sub = argv[2];
|
|
9
9
|
const rest = argv.slice(3);
|
|
@@ -55,7 +55,7 @@ ${color.bold('PROJECTS')}
|
|
|
55
55
|
open Open the project's primary domain in a browser
|
|
56
56
|
|
|
57
57
|
${color.bold('DEPLOY')}
|
|
58
|
-
deploy [--prod] [--detach] Deploy the linked project
|
|
58
|
+
deploy [--prod] [--detach] Deploy the linked project, syncing .env files first
|
|
59
59
|
deployments List recent deployments
|
|
60
60
|
status <id> Show deployment pipeline + events
|
|
61
61
|
logs <id> Stream deployment events (alias of status)
|
|
@@ -69,12 +69,15 @@ ${color.bold('ENV VARS')}
|
|
|
69
69
|
env:pull [--out .env.local] Write env vars to a local .env file
|
|
70
70
|
env:push [<file>] Bulk upload from a .env file
|
|
71
71
|
|
|
72
|
-
${color.bold('
|
|
72
|
+
${color.bold('PULL')}
|
|
73
|
+
pull [<slug>] [--out dir] [--no-env] Pull source + .env from a glashDB project
|
|
73
74
|
pull --from vercel --project <id> Pull source + env from a Vercel project
|
|
74
75
|
|
|
75
76
|
${color.bold('FLAGS')}
|
|
76
77
|
--project <slug|id> Override the linked project for this command
|
|
77
78
|
--environment <name> Env-var environment (default: production)
|
|
79
|
+
--env-file <file[,file]> Env file(s) to sync before deploy
|
|
80
|
+
--no-env Skip automatic env sync during deploy
|
|
78
81
|
--help, -h Show this help
|
|
79
82
|
--version, -v Show CLI version
|
|
80
83
|
|
package/commands/deploy.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { apiGet, apiPost } from '../lib/api.mjs';
|
|
2
|
-
import { readProjectLink } from '../lib/config.mjs';
|
|
3
|
-
import { ok, fail, info, warn, color } from '../lib/ui.mjs';
|
|
2
|
+
import { readProjectLink, writeProjectLink } from '../lib/config.mjs';
|
|
3
|
+
import { ok, fail, info, warn, color, prompt } from '../lib/ui.mjs';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
5
5
|
import { zipDirectory } from '../lib/zip.mjs';
|
|
6
|
-
import { join } from 'node:path';
|
|
6
|
+
import { join, basename } from 'node:path';
|
|
7
7
|
import { tmpdir } from 'node:os';
|
|
8
|
-
import { readFile, rm } from 'node:fs/promises';
|
|
8
|
+
import { access, readFile, rm } from 'node:fs/promises';
|
|
9
9
|
|
|
10
10
|
function currentCommit() {
|
|
11
11
|
try {
|
|
@@ -28,10 +28,43 @@ async function resolveProjectId(flags) {
|
|
|
28
28
|
return found.id;
|
|
29
29
|
}
|
|
30
30
|
const link = await readProjectLink();
|
|
31
|
-
if (
|
|
32
|
-
|
|
31
|
+
if (link?.projectId) {
|
|
32
|
+
return link.projectId;
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
// INTERACTIVE PROJECT SETUP (Vercel-style)
|
|
36
|
+
const dirName = basename(process.cwd());
|
|
37
|
+
const setup = await prompt(`Set up and deploy "${dirName}"? [Y/n] `);
|
|
38
|
+
if (setup.toLowerCase() === 'n') {
|
|
39
|
+
throw new Error('Deployment aborted.');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const existing = await prompt(`Link to existing project? [y/N] `);
|
|
43
|
+
if (existing.toLowerCase() === 'y') {
|
|
44
|
+
const list = await apiGet('/projects');
|
|
45
|
+
if (list.length === 0) {
|
|
46
|
+
info('No existing projects found. Proceeding to create a new one.');
|
|
47
|
+
} else {
|
|
48
|
+
info('\nYour existing projects:');
|
|
49
|
+
list.forEach((p, i) => info(` ${color.dim(i + 1 + '.')} ${color.bold(p.name)} ${color.dim('(' + p.slug + ')')}`));
|
|
50
|
+
const choice = await prompt('\nWhich project do you want to link to? (enter number or slug): ');
|
|
51
|
+
const p = list.find((x, i) => x.slug === choice || String(i + 1) === choice);
|
|
52
|
+
if (!p) throw new Error(`Project "${choice}" not found.`);
|
|
53
|
+
await writeProjectLink({ projectId: p.id, slug: p.slug });
|
|
54
|
+
ok(`Linked to ${p.name} (${p.slug})`);
|
|
55
|
+
return p.id;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// CREATE NEW PROJECT
|
|
60
|
+
const name = (await prompt(`What's your project's name? [${dirName}] `)) || dirName;
|
|
61
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
62
|
+
|
|
63
|
+
info(color.dim(`Creating project "${name}"...`));
|
|
64
|
+
const p = await apiPost('/projects', { name, slug });
|
|
65
|
+
await writeProjectLink({ projectId: p.id, slug: p.slug });
|
|
66
|
+
ok(`Created and linked to ${p.name} (${p.slug})`);
|
|
67
|
+
return p.id;
|
|
35
68
|
}
|
|
36
69
|
|
|
37
70
|
export async function deploy({ flags }) {
|
|
@@ -43,9 +76,17 @@ export async function deploy({ flags }) {
|
|
|
43
76
|
const target = flags.prod || flags.production ? 'production' : (flags.target || 'production');
|
|
44
77
|
|
|
45
78
|
let dep;
|
|
79
|
+
|
|
80
|
+
if (!flags['no-env']) {
|
|
81
|
+
try {
|
|
82
|
+
await syncEnvBeforeDeploy(projectId, target, flags);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
return fail(`Env sync failed: ${e.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
46
87
|
|
|
47
|
-
if (!
|
|
48
|
-
info(color.dim(`→
|
|
88
|
+
if (!flags.git && !flags.repo && !flags.remote) {
|
|
89
|
+
info(color.dim(`→ uploading local source for project ${projectId} (${target})`));
|
|
49
90
|
const zipPath = join(tmpdir(), `glash-deploy-${Date.now()}.zip`);
|
|
50
91
|
try {
|
|
51
92
|
await zipDirectory(process.cwd(), zipPath);
|
|
@@ -55,6 +96,10 @@ export async function deploy({ flags }) {
|
|
|
55
96
|
// We wrap the buffer in a Blob so fetch treats it as a file
|
|
56
97
|
formData.append('file', new Blob([zipBuffer]), 'project.zip');
|
|
57
98
|
|
|
99
|
+
formData.append('commitHash', commitHash || 'local-upload');
|
|
100
|
+
formData.append('gitBranch', gitBranch || '');
|
|
101
|
+
formData.append('target', target);
|
|
102
|
+
|
|
58
103
|
dep = await apiPost('/deployments/upload', formData);
|
|
59
104
|
await rm(zipPath, { force: true }).catch(() => {});
|
|
60
105
|
} catch (e) {
|
|
@@ -62,7 +107,7 @@ export async function deploy({ flags }) {
|
|
|
62
107
|
return fail(`Packaging failed: ${e.message}`);
|
|
63
108
|
}
|
|
64
109
|
} else {
|
|
65
|
-
info(color.dim(`→ deploying project ${projectId}${commitHash ? ` @ ${commitHash.slice(0, 7)}` : ''} (${target})`));
|
|
110
|
+
info(color.dim(`→ deploying connected repo for project ${projectId}${commitHash ? ` @ ${commitHash.slice(0, 7)}` : ''} (${target})`));
|
|
66
111
|
try {
|
|
67
112
|
dep = await apiPost('/deployments', { projectId, commitHash, gitBranch, target });
|
|
68
113
|
} catch (e) {
|
|
@@ -94,10 +139,10 @@ export async function deploy({ flags }) {
|
|
|
94
139
|
info(` ${tag} ${color.dim(ev.step.padEnd(8))} ${ev.message ?? ''}`);
|
|
95
140
|
}
|
|
96
141
|
|
|
97
|
-
const status = pipe.
|
|
142
|
+
const status = pipe.deployStatus ?? pipe.buildStatus ?? '';
|
|
98
143
|
if (status && status !== lastStatus) lastStatus = status;
|
|
99
144
|
if (['ready', 'live', 'success'].includes(status)) {
|
|
100
|
-
const url = pipe.
|
|
145
|
+
const url = pipe.hostings?.[0]?.domains?.[0]?.domain;
|
|
101
146
|
ok(`Live${url ? ` → ${color.cyan(`https://${url.replace(/^https?:\/\//, '')}`)}` : ''}`);
|
|
102
147
|
return;
|
|
103
148
|
}
|
|
@@ -109,6 +154,66 @@ export async function deploy({ flags }) {
|
|
|
109
154
|
warn('Timed out waiting for deployment. Check `glash status <id>`.');
|
|
110
155
|
}
|
|
111
156
|
|
|
157
|
+
async function syncEnvBeforeDeploy(projectId, target, flags) {
|
|
158
|
+
const files = await resolveEnvFiles(target, flags);
|
|
159
|
+
if (!files.length) return;
|
|
160
|
+
|
|
161
|
+
const merged = new Map();
|
|
162
|
+
for (const file of files) {
|
|
163
|
+
const text = await readFile(file, 'utf8');
|
|
164
|
+
for (const entry of parseDotenv(text)) merged.set(entry.key, entry.value);
|
|
165
|
+
}
|
|
166
|
+
const vars = [...merged.entries()]
|
|
167
|
+
.filter(([key]) => !key.startsWith('GLASH_'))
|
|
168
|
+
.map(([key, value]) => ({ key, value, environment: target }));
|
|
169
|
+
|
|
170
|
+
if (!vars.length) return;
|
|
171
|
+
await apiPost(`/projects/${projectId}/env-vars/bulk`, { vars });
|
|
172
|
+
info(color.dim(`→ synced ${vars.length} env vars from ${files.join(', ')}`));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function resolveEnvFiles(target, flags) {
|
|
176
|
+
if (flags['env-file']) {
|
|
177
|
+
const files = String(flags['env-file']).split(',').map((f) => f.trim()).filter(Boolean);
|
|
178
|
+
for (const file of files) await access(file);
|
|
179
|
+
return files;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const candidates = [
|
|
183
|
+
'.env',
|
|
184
|
+
'.env.local',
|
|
185
|
+
`.env.${target}`,
|
|
186
|
+
`.env.${target}.local`,
|
|
187
|
+
];
|
|
188
|
+
const out = [];
|
|
189
|
+
for (const file of candidates) {
|
|
190
|
+
try {
|
|
191
|
+
await access(file);
|
|
192
|
+
out.push(file);
|
|
193
|
+
} catch {}
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function parseDotenv(text) {
|
|
199
|
+
const out = [];
|
|
200
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
201
|
+
let line = raw.trim();
|
|
202
|
+
if (!line || line.startsWith('#')) continue;
|
|
203
|
+
if (line.startsWith('export ')) line = line.slice('export '.length).trim();
|
|
204
|
+
const eq = line.indexOf('=');
|
|
205
|
+
if (eq === -1) continue;
|
|
206
|
+
const key = line.slice(0, eq).trim();
|
|
207
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
|
|
208
|
+
let value = line.slice(eq + 1).trim();
|
|
209
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
210
|
+
value = value.slice(1, -1);
|
|
211
|
+
}
|
|
212
|
+
out.push({ key, value });
|
|
213
|
+
}
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
|
|
112
217
|
function stepBadge(status) {
|
|
113
218
|
if (status === 'success' || status === 'issued' || status === 'active' || status === 'ready') return color.green('✓');
|
|
114
219
|
if (status === 'error' || status === 'failed') return color.red('✗');
|
package/commands/deployments.mjs
CHANGED
|
@@ -34,7 +34,7 @@ export async function status({ positional, flags }) {
|
|
|
34
34
|
try {
|
|
35
35
|
const pipe = await apiGet(`/deployments/${id}/pipeline`);
|
|
36
36
|
info(color.bold(`Deployment ${id}`));
|
|
37
|
-
info(color.dim(`status: ${pipe.
|
|
37
|
+
info(color.dim(`status: ${pipe.deployStatus ?? pipe.buildStatus ?? '?'}`));
|
|
38
38
|
for (const ev of pipe.events ?? []) {
|
|
39
39
|
info(` ${color.dim((ev.createdAt ?? '').slice(11, 19))} ${ev.step.padEnd(8)} ${ev.status.padEnd(10)} ${ev.message ?? ''}`);
|
|
40
40
|
}
|
package/commands/projects.mjs
CHANGED
|
@@ -33,12 +33,21 @@ export async function createProject({ flags, positional }) {
|
|
|
33
33
|
|
|
34
34
|
export async function linkProject({ flags, positional }) {
|
|
35
35
|
let slug = flags.slug || positional[0];
|
|
36
|
+
const all = await apiGet('/projects');
|
|
37
|
+
|
|
36
38
|
if (!slug) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
if (all.length === 0) {
|
|
40
|
+
return fail('No existing projects found. Run `glash projects:new` to create one.');
|
|
41
|
+
}
|
|
42
|
+
info('\nYour existing projects:');
|
|
43
|
+
all.forEach((p, i) => info(` ${color.dim(i + 1 + '.')} ${color.bold(p.name)} ${color.dim('(' + p.slug + ')')}`));
|
|
44
|
+
const choice = await prompt('\nWhich project do you want to link to? (enter number or slug): ');
|
|
45
|
+
const p = all.find((x, i) => x.slug === choice || String(i + 1) === choice);
|
|
46
|
+
if (!p) return fail(`No project found matching "${choice}".`);
|
|
47
|
+
slug = p.slug;
|
|
39
48
|
}
|
|
49
|
+
|
|
40
50
|
try {
|
|
41
|
-
const all = await apiGet('/projects');
|
|
42
51
|
const p = all.find((x) => x.slug === slug || x.id === slug);
|
|
43
52
|
if (!p) return fail(`No project found matching "${slug}". Run \`glash projects\` to list.`);
|
|
44
53
|
await writeProjectLink({ projectId: p.id, slug: p.slug });
|
package/commands/pull.mjs
CHANGED
|
@@ -1,13 +1,112 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// glash pull
|
|
2
|
+
//
|
|
3
|
+
// Default mode: pulls the linked glashDB project's source + .env to the
|
|
4
|
+
// current directory.
|
|
5
|
+
//
|
|
6
|
+
// glash pull → uses the linked project (.glash/project.json)
|
|
7
|
+
// glash pull <slug> → pulls a specific project by slug
|
|
8
|
+
// glash pull --out ../somewhere → write to a different directory
|
|
9
|
+
// glash pull --no-env → skip writing .env
|
|
10
|
+
//
|
|
11
|
+
// Vercel mode (unchanged): glash pull --from vercel --project <id> [--team <id>]
|
|
12
|
+
// delegates to the existing reference implementation in glash-pull.mjs.
|
|
13
|
+
|
|
3
14
|
import { spawnSync } from 'node:child_process';
|
|
4
15
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { dirname, join } from 'node:path';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
import { dirname, join, resolve } from 'node:path';
|
|
17
|
+
import { mkdir, writeFile, stat } from 'node:fs/promises';
|
|
18
|
+
import { createWriteStream } from 'node:fs';
|
|
19
|
+
import { Readable } from 'node:stream';
|
|
20
|
+
import { argv, cwd, exit, stdout, stderr } from 'node:process';
|
|
21
|
+
import { API_URL, getToken, readProjectLink } from '../lib/config.mjs';
|
|
22
|
+
import { color, info, fail } from '../lib/ui.mjs';
|
|
23
|
+
|
|
24
|
+
export async function pull(parsed) {
|
|
25
|
+
const flags = parsed?.flags ?? {};
|
|
26
|
+
const positional = parsed?.positional ?? [];
|
|
27
|
+
|
|
28
|
+
// Vercel-import compatibility path.
|
|
29
|
+
if (flags.from === 'vercel') {
|
|
30
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const script = join(here, '..', 'glash-pull.mjs');
|
|
32
|
+
const r = spawnSync(process.execPath, [script, ...argv.slice(3)], { stdio: 'inherit' });
|
|
33
|
+
exit(r.status ?? 0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const token = await getToken();
|
|
37
|
+
if (!token) return fail('Not signed in. Run `glash login` first.');
|
|
38
|
+
|
|
39
|
+
// Resolve which project to pull
|
|
40
|
+
let slugOrId = positional[0] ?? flags.project;
|
|
41
|
+
if (!slugOrId) {
|
|
42
|
+
const link = await readProjectLink();
|
|
43
|
+
if (link?.slug) slugOrId = link.slug;
|
|
44
|
+
else if (link?.projectId) slugOrId = link.projectId;
|
|
45
|
+
}
|
|
46
|
+
if (!slugOrId) {
|
|
47
|
+
return fail('No project specified. Pass `glash pull <slug>` or run `glash link <slug>` first.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const outDir = resolve(flags.out ? String(flags.out) : cwd());
|
|
51
|
+
await mkdir(outDir, { recursive: true });
|
|
52
|
+
|
|
53
|
+
info(color.dim(`→ pulling ${slugOrId} into ${outDir}`));
|
|
54
|
+
|
|
55
|
+
// 1) source.tar.gz
|
|
56
|
+
await downloadAndExtract(token, slugOrId, outDir);
|
|
57
|
+
|
|
58
|
+
// 2) .env (unless --no-env)
|
|
59
|
+
if (!flags['no-env']) {
|
|
60
|
+
await downloadEnv(token, slugOrId, outDir, !!flags.force);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
stdout.write(color.dim(`\n next: cd ${outDir} && glash deploy\n`));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function downloadAndExtract(token, slugOrId, outDir) {
|
|
67
|
+
const url = `${API_URL}/projects/${encodeURIComponent(slugOrId)}/source.tar.gz`;
|
|
68
|
+
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
const body = await res.text().catch(() => '');
|
|
71
|
+
throw new Error(`source download failed (${res.status}): ${body.slice(0, 200) || res.statusText}`);
|
|
72
|
+
}
|
|
73
|
+
if (!res.body) throw new Error('empty response from server');
|
|
74
|
+
|
|
75
|
+
// Stream-pipe into `tar -xz -C outDir` via stdin
|
|
76
|
+
const child = spawnSync('tar', ['-xz', '-C', outDir], { input: Buffer.from(await res.arrayBuffer()) });
|
|
77
|
+
if (child.status !== 0) {
|
|
78
|
+
throw new Error(`tar extract failed: ${child.stderr?.toString() || 'unknown'}`);
|
|
79
|
+
}
|
|
80
|
+
info(color.dim(` ✓ source written to ${outDir}`));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function downloadEnv(token, slugOrId, outDir, force) {
|
|
84
|
+
const url = `${API_URL}/projects/${encodeURIComponent(slugOrId)}/env-vars/dotenv`;
|
|
85
|
+
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
stderr.write(color.dim(` ! skipped .env (${res.status})\n`));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const body = await res.text();
|
|
91
|
+
if (!body.trim()) {
|
|
92
|
+
info(color.dim(' · no env vars set on this project'));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Preserve any existing .env unless --force
|
|
97
|
+
const envPath = join(outDir, '.env');
|
|
98
|
+
const exists = await stat(envPath).then(() => true).catch(() => false);
|
|
99
|
+
if (exists && !force) {
|
|
100
|
+
const backup = `${envPath}.glash-backup-${Date.now()}`;
|
|
101
|
+
await writeFile(backup, await readFileOrEmpty(envPath));
|
|
102
|
+
info(color.dim(` · existing .env backed up → ${backup}`));
|
|
103
|
+
}
|
|
104
|
+
await writeFile(envPath, body, { mode: 0o600 });
|
|
105
|
+
const lines = body.split('\n').filter(Boolean).length;
|
|
106
|
+
info(color.dim(` ✓ ${lines} env var${lines === 1 ? '' : 's'} written to .env`));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function readFileOrEmpty(p) {
|
|
110
|
+
try { const { readFile } = await import('node:fs/promises'); return await readFile(p); }
|
|
111
|
+
catch { return ''; }
|
|
13
112
|
}
|
package/lib/zip.mjs
CHANGED
|
@@ -14,7 +14,34 @@ export async function zipDirectory(sourceDir, outPath, { ignore = [] } = {}) {
|
|
|
14
14
|
archive.pipe(output);
|
|
15
15
|
|
|
16
16
|
// Default ignores
|
|
17
|
-
const defaultIgnore = [
|
|
17
|
+
const defaultIgnore = [
|
|
18
|
+
'node_modules/**',
|
|
19
|
+
'.git/**',
|
|
20
|
+
'.next/**',
|
|
21
|
+
'.nuxt/**',
|
|
22
|
+
'.output/**',
|
|
23
|
+
'.svelte-kit/**',
|
|
24
|
+
'.vercel/**',
|
|
25
|
+
'.wrangler/**',
|
|
26
|
+
'.netlify/**',
|
|
27
|
+
'dist/**',
|
|
28
|
+
'build/**',
|
|
29
|
+
'out/**',
|
|
30
|
+
'coverage/**',
|
|
31
|
+
'.turbo/**',
|
|
32
|
+
'.cache/**',
|
|
33
|
+
'.parcel-cache/**',
|
|
34
|
+
'.vite/**',
|
|
35
|
+
'.glash/**',
|
|
36
|
+
'*.log',
|
|
37
|
+
'npm-debug.log*',
|
|
38
|
+
'yarn-debug.log*',
|
|
39
|
+
'yarn-error.log*',
|
|
40
|
+
'.env',
|
|
41
|
+
'.env.local',
|
|
42
|
+
'.env.production',
|
|
43
|
+
'.env.development',
|
|
44
|
+
];
|
|
18
45
|
const allIgnore = [...new Set([...defaultIgnore, ...ignore])];
|
|
19
46
|
|
|
20
47
|
archive.glob('**/*', {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glash/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "glashDB command-line interface — deploy projects, manage env vars,
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "glashDB command-line interface — deploy projects, pull source + env, manage env vars, import from Vercel.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://glashdb.com",
|
|
7
7
|
"repository": {
|