primo-cli 0.1.3 → 0.1.4
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 +111 -39
- package/dist/commands/build.js +488 -272
- package/dist/commands/deploy.d.ts +1 -1
- package/dist/commands/deploy.js +293 -141
- package/dist/commands/dev.d.ts +2 -0
- package/dist/commands/dev.js +2007 -150
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +65 -43
- package/dist/commands/login.d.ts +1 -2
- package/dist/commands/login.js +24 -6
- package/dist/commands/new.js +161 -274
- package/dist/commands/pull-library.d.ts +7 -0
- package/dist/commands/pull-library.js +92 -0
- package/dist/commands/pull.d.ts +0 -1
- package/dist/commands/pull.js +160 -165
- package/dist/commands/push-library.d.ts +7 -0
- package/dist/commands/push-library.js +88 -0
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/push.js +358 -51
- package/dist/commands/validate.d.ts +1 -1
- package/dist/commands/validate.js +379 -161
- package/dist/index.js +109 -19
- package/dist/utils/binary.js +1 -1
- package/dist/utils/format.d.ts +12 -0
- package/dist/utils/format.js +98 -0
- package/dist/utils/head-svelte.d.ts +2 -0
- package/dist/utils/head-svelte.js +53 -0
- package/dist/utils/server-config.d.ts +19 -0
- package/dist/utils/server-config.js +49 -0
- package/dist/utils/site-config.d.ts +11 -0
- package/dist/utils/site-config.js +14 -0
- package/package.json +8 -4
- package/dist/commands/export.d.ts +0 -8
- package/dist/commands/export.js +0 -163
- package/dist/commands/import.d.ts +0 -9
- package/dist/commands/import.js +0 -118
- package/dist/commands/publish.d.ts +0 -6
- package/dist/commands/publish.js +0 -239
package/dist/commands/push.js
CHANGED
|
@@ -4,75 +4,382 @@ import chalk from 'chalk';
|
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import archiver from 'archiver';
|
|
6
6
|
import { get_auth_token } from '../utils/auth.js';
|
|
7
|
+
import { read_site_config, get_site_config_path, SITE_CONFIG_FILE } from '../utils/site-config.js';
|
|
8
|
+
import { get_server_config_path, read_server_config } from '../utils/server-config.js';
|
|
9
|
+
async function path_exists(p) {
|
|
10
|
+
try {
|
|
11
|
+
await fs.access(p);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
7
18
|
export async function push_site(options) {
|
|
19
|
+
const root_dir = path.resolve(options.dir);
|
|
20
|
+
const has_site_yaml = await path_exists(get_site_config_path(root_dir));
|
|
21
|
+
const has_server_yaml = await path_exists(get_server_config_path(root_dir));
|
|
22
|
+
// Resolve a workspace-level default server URL (server.yaml). Used as the
|
|
23
|
+
// fallback for sites that don't declare their own `server` in site.yaml,
|
|
24
|
+
// and for the library push.
|
|
25
|
+
let workspace_server;
|
|
26
|
+
if (has_server_yaml) {
|
|
27
|
+
try {
|
|
28
|
+
const server_config = await read_server_config(root_dir);
|
|
29
|
+
workspace_server = server_config.server;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// fall through — bad server.yaml will surface elsewhere
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const effective_options = workspace_server && !options.server
|
|
36
|
+
? { ...options, server: workspace_server }
|
|
37
|
+
: options;
|
|
38
|
+
if (options.dryRun) {
|
|
39
|
+
await print_push_dry_run(root_dir, has_site_yaml, has_server_yaml, effective_options);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Server-folder mode: walk site subfolders + push library
|
|
43
|
+
if (!has_site_yaml && has_server_yaml) {
|
|
44
|
+
await push_server(root_dir, effective_options);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Single-site mode (cwd is a site folder, or --dir points at one)
|
|
8
48
|
const spinner = ora('Reading local files...').start();
|
|
9
49
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
50
|
+
await push_single_site(root_dir, effective_options, spinner);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
spinner.fail(`Push failed: ${error instanceof Error ? error.message : error}`);
|
|
54
|
+
if (is_auth_error(error))
|
|
55
|
+
print_auth_hint();
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function is_auth_error(error) {
|
|
60
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
61
|
+
return /Authentication required/i.test(msg) || /\b401\b/.test(msg) || /unauthorized/i.test(msg);
|
|
62
|
+
}
|
|
63
|
+
function print_auth_hint() {
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(chalk.dim(' Run `primo login -s <server-url>` to authenticate, then retry.'));
|
|
66
|
+
console.log('');
|
|
67
|
+
}
|
|
68
|
+
async function print_push_dry_run(root_dir, has_site_yaml, has_server_yaml, options) {
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(chalk.bold('Push preview (dry-run)'));
|
|
71
|
+
console.log('');
|
|
72
|
+
if (!has_site_yaml && !has_server_yaml) {
|
|
73
|
+
console.log(chalk.red(` No ${SITE_CONFIG_FILE} or ${path.basename(get_server_config_path(root_dir))} found in ${root_dir}.`));
|
|
74
|
+
console.log('');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const sites = [];
|
|
78
|
+
let library_present = false;
|
|
79
|
+
let inferred_server;
|
|
80
|
+
if (has_site_yaml) {
|
|
14
81
|
try {
|
|
15
|
-
const
|
|
16
|
-
config
|
|
82
|
+
const config = await read_site_config(root_dir);
|
|
83
|
+
sites.push({ dir: root_dir, config: config, label: config.name || path.basename(root_dir) });
|
|
84
|
+
inferred_server = config.server;
|
|
17
85
|
}
|
|
18
86
|
catch {
|
|
19
|
-
//
|
|
87
|
+
// fall through
|
|
20
88
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
89
|
+
}
|
|
90
|
+
else if (has_server_yaml) {
|
|
91
|
+
const sites_root = path.join(root_dir, 'sites');
|
|
92
|
+
if (await path_exists(sites_root)) {
|
|
93
|
+
const entries = await fs.readdir(sites_root, { withFileTypes: true });
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
if (!entry.isDirectory() || entry.name.startsWith('.'))
|
|
96
|
+
continue;
|
|
97
|
+
const candidate = path.join(sites_root, entry.name);
|
|
98
|
+
if (!await path_exists(get_site_config_path(candidate)))
|
|
99
|
+
continue;
|
|
100
|
+
try {
|
|
101
|
+
const config = await read_site_config(candidate);
|
|
102
|
+
sites.push({ dir: candidate, config: config, label: config.name || entry.name });
|
|
103
|
+
inferred_server = inferred_server || config.server;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// skip
|
|
107
|
+
}
|
|
108
|
+
}
|
|
26
109
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
110
|
+
library_present = await path_exists(path.join(root_dir, 'library'));
|
|
111
|
+
}
|
|
112
|
+
const server = (options.server || inferred_server)?.replace(/\/+$/, '');
|
|
113
|
+
console.log(` Target server: ${chalk.cyan(server || '(not set — pass --server or set in site.yaml)')}`);
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(chalk.bold(' Will sync:'));
|
|
116
|
+
if (sites.length === 0) {
|
|
117
|
+
console.log(chalk.yellow(' (no sites found)'));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
for (const site of sites) {
|
|
121
|
+
console.log(` ${chalk.green('+')} site: ${site.label} ${chalk.dim(`(${path.basename(site.dir)})`)}`);
|
|
30
122
|
}
|
|
31
|
-
|
|
123
|
+
}
|
|
124
|
+
if (library_present) {
|
|
125
|
+
console.log(` ${chalk.green('+')} library/`);
|
|
126
|
+
}
|
|
127
|
+
console.log('');
|
|
128
|
+
if (server) {
|
|
32
129
|
const token = options.token || await get_auth_token(server);
|
|
33
|
-
if (
|
|
34
|
-
|
|
130
|
+
if (token) {
|
|
131
|
+
console.log(chalk.dim(` Auth: token found for ${server}.`));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
console.log(chalk.yellow(` Auth: not logged in to ${server}. Run \`primo login -s ${server}\`.`));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (options.preview) {
|
|
138
|
+
console.log(chalk.dim(' --preview flag set: real push would request a server-side preview only.'));
|
|
139
|
+
}
|
|
140
|
+
console.log('');
|
|
141
|
+
console.log(chalk.dim(' No requests sent. Run without --dry-run to push.'));
|
|
142
|
+
console.log('');
|
|
143
|
+
}
|
|
144
|
+
async function push_server(root_dir, options) {
|
|
145
|
+
// Sites live under sites/<slug>/
|
|
146
|
+
const sites_root = path.join(root_dir, 'sites');
|
|
147
|
+
const site_dirs = [];
|
|
148
|
+
if (await path_exists(sites_root)) {
|
|
149
|
+
const entries = await fs.readdir(sites_root, { withFileTypes: true });
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (!entry.isDirectory() || entry.name.startsWith('.'))
|
|
152
|
+
continue;
|
|
153
|
+
const candidate = path.join(sites_root, entry.name);
|
|
154
|
+
if (await path_exists(get_site_config_path(candidate))) {
|
|
155
|
+
site_dirs.push(candidate);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (site_dirs.length === 0) {
|
|
160
|
+
console.log(chalk.yellow(' No site folders found in this server directory.'));
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
// --only <slug>: push just one site folder, skip the library
|
|
164
|
+
if (options.only) {
|
|
165
|
+
const match = site_dirs.find((d) => path.basename(d) === options.only);
|
|
166
|
+
if (!match) {
|
|
167
|
+
const available = site_dirs.map((d) => path.basename(d)).join(', ');
|
|
168
|
+
console.log(chalk.red(` No site folder named "${options.only}" under sites/.`));
|
|
169
|
+
console.log(chalk.dim(` Available: ${available}`));
|
|
35
170
|
process.exit(1);
|
|
36
171
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const form_data = new FormData();
|
|
46
|
-
form_data.append('file', new Blob([zip_buffer]), 'site.zip');
|
|
47
|
-
const response = await fetch(endpoint, {
|
|
48
|
-
method: 'POST',
|
|
49
|
-
headers: {
|
|
50
|
-
'Authorization': `Bearer ${token}`
|
|
51
|
-
},
|
|
52
|
-
body: form_data
|
|
53
|
-
});
|
|
54
|
-
if (!response.ok) {
|
|
55
|
-
const error = await response.text();
|
|
56
|
-
spinner.fail(`Push failed: ${error}`);
|
|
172
|
+
const spinner = ora(`Pushing ${chalk.cyan(path.basename(match))}...`).start();
|
|
173
|
+
try {
|
|
174
|
+
await push_single_site(match, { ...options, dir: match }, spinner);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
spinner.fail(`${path.basename(match)}: ${error instanceof Error ? error.message : error}`);
|
|
178
|
+
if (is_auth_error(error))
|
|
179
|
+
print_auth_hint();
|
|
57
180
|
process.exit(1);
|
|
58
181
|
}
|
|
59
|
-
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
let saw_auth_error = false;
|
|
185
|
+
// Push each site
|
|
186
|
+
for (const site_dir of site_dirs) {
|
|
187
|
+
const spinner = ora(`Pushing ${chalk.cyan(path.basename(site_dir))}...`).start();
|
|
188
|
+
try {
|
|
189
|
+
await push_single_site(site_dir, { ...options, dir: site_dir }, spinner);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
spinner.fail(`${path.basename(site_dir)}: ${error instanceof Error ? error.message : error}`);
|
|
193
|
+
if (is_auth_error(error))
|
|
194
|
+
saw_auth_error = true;
|
|
195
|
+
// Continue to remaining sites rather than abort the whole push
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Push library if present
|
|
199
|
+
const library_dir = path.join(root_dir, 'library');
|
|
200
|
+
if (await path_exists(library_dir)) {
|
|
201
|
+
const spinner = ora('Pushing library...').start();
|
|
202
|
+
try {
|
|
203
|
+
await push_library_dir(root_dir, options, spinner);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
spinner.fail(`library: ${error instanceof Error ? error.message : error}`);
|
|
207
|
+
if (is_auth_error(error))
|
|
208
|
+
saw_auth_error = true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (saw_auth_error)
|
|
212
|
+
print_auth_hint();
|
|
213
|
+
}
|
|
214
|
+
async function push_single_site(site_dir, options, spinner) {
|
|
215
|
+
let config = null;
|
|
216
|
+
try {
|
|
217
|
+
config = await read_site_config(site_dir);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// No config file, must provide options
|
|
221
|
+
}
|
|
222
|
+
const server = (options.server || config?.server)?.replace(/\/+$/, '');
|
|
223
|
+
const site_id = options.site || config?.site_id;
|
|
224
|
+
if (!server) {
|
|
225
|
+
throw new Error(`Server URL required. Use --server or add server field to ${SITE_CONFIG_FILE}.`);
|
|
226
|
+
}
|
|
227
|
+
if (!site_id) {
|
|
228
|
+
throw new Error(`Site ID required. Use --site or add site_id field to ${SITE_CONFIG_FILE}.`);
|
|
229
|
+
}
|
|
230
|
+
const token = options.token || await get_auth_token(server);
|
|
231
|
+
spinner.text = 'Packaging files...';
|
|
232
|
+
const zip_buffer = await create_zip(site_dir);
|
|
233
|
+
// If the user isn't logged in yet, skip the import attempt and try
|
|
234
|
+
// bootstrap directly. Bootstrap doesn't require auth (only allowed when
|
|
235
|
+
// the server has zero sites), so it's the right path for first-time
|
|
236
|
+
// setup against a fresh deployment.
|
|
237
|
+
if (!token) {
|
|
60
238
|
if (options.preview) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
239
|
+
throw new Error('Authentication required for --preview. Run `primo login` first.');
|
|
240
|
+
}
|
|
241
|
+
spinner.text = 'No auth token — attempting bootstrap...';
|
|
242
|
+
const bootstrap_result = await try_bootstrap_site(server, undefined, zip_buffer, config, site_id);
|
|
243
|
+
if (bootstrap_result.ok) {
|
|
244
|
+
spinner.succeed(`Bootstrapped ${config?.name || path.basename(site_dir)}`);
|
|
64
245
|
console.log('');
|
|
65
|
-
console.log(chalk.dim('
|
|
246
|
+
console.log(chalk.dim(' Site created on server and content uploaded.'));
|
|
247
|
+
console.log(chalk.dim(' Run `primo login` and re-push to update content later.'));
|
|
248
|
+
return;
|
|
66
249
|
}
|
|
67
|
-
|
|
68
|
-
|
|
250
|
+
throw new Error(bootstrap_result.error);
|
|
251
|
+
}
|
|
252
|
+
const endpoint = options.preview
|
|
253
|
+
? `${server}/api/palacms/import/${site_id}/preview`
|
|
254
|
+
: `${server}/api/palacms/import/${site_id}`;
|
|
255
|
+
spinner.text = options.preview ? 'Previewing changes...' : 'Pushing changes...';
|
|
256
|
+
const form_data = new FormData();
|
|
257
|
+
form_data.append('file', new Blob([zip_buffer]), 'site.zip');
|
|
258
|
+
const response = await fetch(endpoint, {
|
|
259
|
+
method: 'POST',
|
|
260
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
261
|
+
body: form_data
|
|
262
|
+
});
|
|
263
|
+
// 404 from import means the site doesn't exist on the server yet. On a
|
|
264
|
+
// freshly-deployed server we can fall back to /api/palacms/bootstrap,
|
|
265
|
+
// which creates the site and ingests the zip in one shot. Bootstrap is
|
|
266
|
+
// only available when the server has zero sites — past the first site,
|
|
267
|
+
// new sites must be created via the dashboard UI.
|
|
268
|
+
if (response.status === 404 && !options.preview) {
|
|
269
|
+
spinner.text = 'Site not found on server — bootstrapping...';
|
|
270
|
+
const bootstrap_result = await try_bootstrap_site(server, token, zip_buffer, config, site_id);
|
|
271
|
+
if (bootstrap_result.ok) {
|
|
272
|
+
spinner.succeed(`Bootstrapped ${config?.name || path.basename(site_dir)}`);
|
|
69
273
|
console.log('');
|
|
70
|
-
|
|
274
|
+
console.log(chalk.dim(' Site created on server and content uploaded.'));
|
|
275
|
+
console.log(chalk.dim(' Subsequent pushes will use the import endpoint.'));
|
|
276
|
+
return;
|
|
71
277
|
}
|
|
278
|
+
throw new Error(bootstrap_result.error);
|
|
72
279
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
280
|
+
if (!response.ok) {
|
|
281
|
+
throw new Error(await response.text());
|
|
282
|
+
}
|
|
283
|
+
const result = await response.json();
|
|
284
|
+
const label = config?.name || path.basename(site_dir);
|
|
285
|
+
if (options.preview) {
|
|
286
|
+
spinner.succeed(`Preview: ${label}`);
|
|
287
|
+
console.log('');
|
|
288
|
+
print_diff(result.diff);
|
|
289
|
+
console.log('');
|
|
290
|
+
console.log(chalk.dim(' Run without --preview to apply these changes'));
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
spinner.succeed(`Pushed ${label}`);
|
|
294
|
+
console.log('');
|
|
295
|
+
print_diff(result.diff);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async function try_bootstrap_site(server, token, zip_buffer, config, site_id) {
|
|
299
|
+
const form = new FormData();
|
|
300
|
+
form.append('site_id', site_id);
|
|
301
|
+
if (config?.name)
|
|
302
|
+
form.append('name', config.name);
|
|
303
|
+
if (config?.host)
|
|
304
|
+
form.append('host', config.host);
|
|
305
|
+
if (config?.group)
|
|
306
|
+
form.append('group', config.group);
|
|
307
|
+
form.append('file', new Blob([zip_buffer]), 'site.zip');
|
|
308
|
+
const headers = {};
|
|
309
|
+
if (token)
|
|
310
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
311
|
+
const response = await fetch(`${server}/api/palacms/bootstrap`, {
|
|
312
|
+
method: 'POST',
|
|
313
|
+
headers,
|
|
314
|
+
body: form
|
|
315
|
+
});
|
|
316
|
+
if (response.ok)
|
|
317
|
+
return { ok: true };
|
|
318
|
+
if (response.status === 403) {
|
|
319
|
+
return {
|
|
320
|
+
ok: false,
|
|
321
|
+
error: `Site '${site_id}' not found on server, and bootstrap is locked ` +
|
|
322
|
+
`(server already has other sites). Create the site in the dashboard first, ` +
|
|
323
|
+
`then update site_id in site.yaml to match.`
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return { ok: false, error: await response.text() };
|
|
327
|
+
}
|
|
328
|
+
async function push_library_dir(root_dir, options, spinner) {
|
|
329
|
+
// Resolve server: --server > any site.yaml's server (they all point at the same server)
|
|
330
|
+
let server = options.server?.replace(/\/+$/, '');
|
|
331
|
+
if (!server) {
|
|
332
|
+
const sites_root = path.join(root_dir, 'sites');
|
|
333
|
+
if (await path_exists(sites_root)) {
|
|
334
|
+
const entries = await fs.readdir(sites_root, { withFileTypes: true });
|
|
335
|
+
for (const entry of entries) {
|
|
336
|
+
if (!entry.isDirectory())
|
|
337
|
+
continue;
|
|
338
|
+
try {
|
|
339
|
+
const config = await read_site_config(path.join(sites_root, entry.name));
|
|
340
|
+
if (config.server) {
|
|
341
|
+
server = config.server.replace(/\/+$/, '');
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch { }
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (!server)
|
|
350
|
+
throw new Error('Server URL required for library push.');
|
|
351
|
+
const token = options.token || await get_auth_token(server);
|
|
352
|
+
if (!token)
|
|
353
|
+
throw new Error('Authentication required. Run `primo login` first.');
|
|
354
|
+
spinner.text = 'Packaging library...';
|
|
355
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
356
|
+
const chunks = [];
|
|
357
|
+
const zip_buffer = await new Promise((resolve, reject) => {
|
|
358
|
+
archive.on('data', (chunk) => chunks.push(chunk));
|
|
359
|
+
archive.on('end', () => resolve(Buffer.concat(chunks)));
|
|
360
|
+
archive.on('error', reject);
|
|
361
|
+
archive.directory(path.join(root_dir, 'library'), 'library');
|
|
362
|
+
archive.finalize();
|
|
363
|
+
});
|
|
364
|
+
spinner.text = 'Pushing library...';
|
|
365
|
+
const form_data = new FormData();
|
|
366
|
+
form_data.append('file', new Blob([zip_buffer]), 'library.zip');
|
|
367
|
+
const response = await fetch(`${server}/api/palacms/import-library`, {
|
|
368
|
+
method: 'POST',
|
|
369
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
370
|
+
body: form_data
|
|
371
|
+
});
|
|
372
|
+
if (response.status === 404) {
|
|
373
|
+
spinner.warn('Library push not supported by this server — skipping');
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (!response.ok) {
|
|
377
|
+
throw new Error(await response.text());
|
|
378
|
+
}
|
|
379
|
+
const result = await response.json();
|
|
380
|
+
spinner.succeed('Pushed library');
|
|
381
|
+
if (result.summary) {
|
|
382
|
+
console.log(chalk.dim(` groups/ ${result.summary.groups}, blocks/ ${result.summary.blocks}`));
|
|
76
383
|
}
|
|
77
384
|
}
|
|
78
385
|
async function create_zip(dir) {
|
|
@@ -88,8 +395,8 @@ async function create_zip(dir) {
|
|
|
88
395
|
const full_path = path.join(dir, subdir);
|
|
89
396
|
archive.directory(full_path, subdir);
|
|
90
397
|
}
|
|
91
|
-
// Add
|
|
92
|
-
archive.file(path.join(dir,
|
|
398
|
+
// Add site config
|
|
399
|
+
archive.file(path.join(dir, SITE_CONFIG_FILE), { name: SITE_CONFIG_FILE });
|
|
93
400
|
archive.finalize();
|
|
94
401
|
});
|
|
95
402
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare function normalize_site(site_dir: string): Promise<void>;
|
|
2
1
|
interface ValidateOptions {
|
|
3
2
|
dir: string;
|
|
4
3
|
strict?: boolean;
|
|
5
4
|
}
|
|
5
|
+
export declare function normalize_site(site_dir: string): Promise<void>;
|
|
6
6
|
export declare function validate_site(options: ValidateOptions): Promise<void>;
|
|
7
7
|
export {};
|