@kitecd/cli 1.1.0 → 1.2.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/dist/doctor.js +181 -0
- package/dist/export.js +144 -0
- package/dist/ignore.js +38 -0
- package/dist/import.js +333 -0
- package/dist/index.js +201 -3
- package/dist/ops.js +338 -0
- package/dist/pack.js +15 -5
- package/dist/serve.js +27 -1
- package/dist/server/index.js +14578 -5647
- package/dist/upload.js +9 -3
- package/dist/verify.js +161 -0
- package/dist/web/assets/AuditLog-BFmJfgzL.js +1 -0
- package/dist/web/assets/ConfirmDialog-CJ8lJeUc.js +1 -0
- package/dist/web/assets/ConfirmDialog-D8avT8FJ.css +1 -0
- package/dist/web/assets/Dashboard-BTliTkq1.js +1 -0
- package/dist/web/assets/DefaultLayout-BG_y85yG.js +1 -0
- package/dist/web/assets/DefaultLayout-CZENO67n.css +1 -0
- package/dist/web/assets/FileExplorer-Bf32_MMS.js +1 -0
- package/dist/web/assets/LogBoard-0so-XiW3.css +1 -0
- package/dist/web/assets/LogBoard-CDz4n0DY.js +6 -0
- package/dist/web/assets/Login-C3zfObzP.js +1 -0
- package/dist/web/assets/Migration-BaKlcCkB.js +1 -0
- package/dist/web/assets/ProjectDetail-BL_D0OJY.js +1 -0
- package/dist/web/assets/ProjectDetail-ia6-z1kZ.css +1 -0
- package/dist/web/assets/ProjectList-7AnhOS7h.css +1 -0
- package/dist/web/assets/ProjectList-C4KuZEq4.js +1 -0
- package/dist/web/assets/Settings-CzWG9312.js +1 -0
- package/dist/web/assets/Storage-BCao_e7Y.js +1 -0
- package/dist/web/assets/Storage-DNadqpUy.css +1 -0
- package/dist/web/assets/{activity-DItEGOtI.js → activity-Ba-YPfmn.js} +1 -1
- package/dist/web/assets/archive-CJ5gDBKY.js +1 -0
- package/dist/web/assets/arrow-left-WEOMLXIi.js +1 -0
- package/dist/web/assets/chevron-right-DLiDJVJl.js +1 -0
- package/dist/web/assets/{circle-alert-Bfrn_ovD.js → circle-alert-DQzM-U4P.js} +1 -1
- package/dist/web/assets/clock-IwxBKgP4.js +1 -0
- package/dist/web/assets/constants-Ch47JPs3.js +1 -0
- package/dist/web/assets/copy-C1rADgcQ.js +1 -0
- package/dist/web/assets/createLucideIcon-CE-ry2oA.js +1 -0
- package/dist/web/assets/database-CI6acTSY.js +1 -0
- package/dist/web/assets/eye-Cas8HsmK.js +1 -0
- package/dist/web/assets/eye-off-4PD31MPV.js +1 -0
- package/dist/web/assets/file-text-C6nzo1us.js +1 -0
- package/dist/web/assets/folder-D9pvA6oI.js +1 -0
- package/dist/web/assets/folder-open-CGeIri0U.js +1 -0
- package/dist/web/assets/hard-drive-CGWwV4Ei.js +1 -0
- package/dist/web/assets/house-8ZvvlaEh.js +1 -0
- package/dist/web/assets/index-BFE6PEIL.js +2 -0
- package/dist/web/assets/index-XrzJwjrk.css +1 -0
- package/dist/web/assets/loader-circle-uiC7GaCG.js +1 -0
- package/dist/web/assets/plus-CfMi1Jv1.js +1 -0
- package/dist/web/assets/refresh-cw-0yb8DZDc.js +1 -0
- package/dist/web/assets/rotate-ccw-YevaWXw9.js +1 -0
- package/dist/web/assets/save-BuzCP3T1.js +1 -0
- package/dist/web/assets/scroll-text-BIYMFNN5.js +1 -0
- package/dist/web/assets/{server-C33taHNn.js → server-B31hj0g7.js} +1 -1
- package/dist/web/assets/{square-terminal-C8toRwjx.js → square-terminal-DT6aoXm0.js} +1 -1
- package/dist/web/assets/sun-BaEbKNDV.js +1 -0
- package/dist/web/assets/trash-2-BWqtqkeW.js +1 -0
- package/dist/web/index.html +3 -3
- package/package.json +2 -2
- package/dist/web/assets/Dashboard-7wBCpwzp.js +0 -1
- package/dist/web/assets/DefaultLayout-Bj8fPWym.css +0 -1
- package/dist/web/assets/DefaultLayout-LaPhlICp.js +0 -1
- package/dist/web/assets/FileExplorer-DvzkxXvZ.js +0 -1
- package/dist/web/assets/LogBoard-BWAPesBz.js +0 -6
- package/dist/web/assets/LogBoard-DzW-cEqH.css +0 -1
- package/dist/web/assets/Login-BhNdUJs0.js +0 -1
- package/dist/web/assets/ProjectDetail-DZwMOEto.js +0 -1
- package/dist/web/assets/ProjectList-Czr-438J.js +0 -1
- package/dist/web/assets/Settings-OmPvcQbD.js +0 -1
- package/dist/web/assets/clock-BPXGSCIV.js +0 -1
- package/dist/web/assets/constants-iI5LEC2F.js +0 -1
- package/dist/web/assets/createLucideIcon-Cgv1AIRL.js +0 -1
- package/dist/web/assets/folder-open-jX-_Q7bA.js +0 -1
- package/dist/web/assets/index-C9LiRc31.css +0 -1
- package/dist/web/assets/index-eyx6wNyQ.js +0 -2
- package/dist/web/assets/project-BFuaDcvV.js +0 -1
- package/dist/web/assets/refresh-cw-DWmqwQRn.js +0 -1
- package/dist/web/assets/save-BkiMrL9q.js +0 -1
- package/dist/web/assets/settings-CrCWmNyB.js +0 -1
package/dist/import.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import AdmZip from 'adm-zip';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { createClient } from '@libsql/client';
|
|
7
|
+
import { ensureKiteHome, readGlobalConfig, writeGlobalConfig } from './home.js';
|
|
8
|
+
const DEFAULT_LOCAL_SERVER_URL = 'http://127.0.0.1:5431';
|
|
9
|
+
const SUPPORTED_SCHEMA = 1;
|
|
10
|
+
const PROJECT_COLUMNS = [
|
|
11
|
+
'id', 'name', 'description', 'deploy_path', 'token',
|
|
12
|
+
'pre_deploy_script', 'post_deploy_script', 'env', 'status',
|
|
13
|
+
'created_at', 'updated_at',
|
|
14
|
+
];
|
|
15
|
+
const DEPLOYMENT_COLUMNS = [
|
|
16
|
+
'id', 'project_id', 'project_name', 'status', 'trigger_source',
|
|
17
|
+
'duration', 'output', 'start_time', 'end_time',
|
|
18
|
+
];
|
|
19
|
+
const CREATE_PROJECTS = `
|
|
20
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
name TEXT NOT NULL,
|
|
23
|
+
description TEXT,
|
|
24
|
+
deploy_path TEXT NOT NULL,
|
|
25
|
+
token TEXT NOT NULL UNIQUE,
|
|
26
|
+
pre_deploy_script TEXT,
|
|
27
|
+
post_deploy_script TEXT,
|
|
28
|
+
env TEXT,
|
|
29
|
+
status TEXT DEFAULT 'idle',
|
|
30
|
+
created_at TEXT NOT NULL,
|
|
31
|
+
updated_at TEXT NOT NULL
|
|
32
|
+
);
|
|
33
|
+
`;
|
|
34
|
+
const CREATE_SETTINGS = `
|
|
35
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
36
|
+
key TEXT PRIMARY KEY,
|
|
37
|
+
value TEXT NOT NULL
|
|
38
|
+
);
|
|
39
|
+
`;
|
|
40
|
+
const CREATE_DEPLOYMENTS = `
|
|
41
|
+
CREATE TABLE IF NOT EXISTS deployments (
|
|
42
|
+
id TEXT PRIMARY KEY,
|
|
43
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
44
|
+
project_name TEXT NOT NULL,
|
|
45
|
+
status TEXT NOT NULL,
|
|
46
|
+
trigger_source TEXT NOT NULL,
|
|
47
|
+
duration TEXT,
|
|
48
|
+
output TEXT,
|
|
49
|
+
start_time TEXT NOT NULL,
|
|
50
|
+
end_time TEXT
|
|
51
|
+
);
|
|
52
|
+
`;
|
|
53
|
+
const ensureSchema = async (client) => {
|
|
54
|
+
await client.execute(CREATE_PROJECTS);
|
|
55
|
+
try {
|
|
56
|
+
await client.execute(`ALTER TABLE projects ADD COLUMN env TEXT`);
|
|
57
|
+
}
|
|
58
|
+
catch { /* exists */ }
|
|
59
|
+
await client.execute(CREATE_SETTINGS);
|
|
60
|
+
await client.execute(CREATE_DEPLOYMENTS);
|
|
61
|
+
};
|
|
62
|
+
const readJsonEntry = (zip, name) => {
|
|
63
|
+
const entry = zip.getEntry(name);
|
|
64
|
+
if (!entry)
|
|
65
|
+
return null;
|
|
66
|
+
return JSON.parse(entry.getData().toString('utf-8'));
|
|
67
|
+
};
|
|
68
|
+
export async function runImport(file, options) {
|
|
69
|
+
const strategy = options.strategy || 'skip-existing';
|
|
70
|
+
if (!['merge', 'overwrite', 'skip-existing'].includes(strategy)) {
|
|
71
|
+
console.error(chalk.red(`Invalid strategy: ${strategy}. Use merge | overwrite | skip-existing.`));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
if (strategy === 'overwrite' && !options.yes && !options.dryRun) {
|
|
75
|
+
console.error(chalk.red('Strategy "overwrite" requires --yes to confirm destructive replacement.'));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const absFile = path.resolve(process.cwd(), file);
|
|
79
|
+
if (!fs.existsSync(absFile)) {
|
|
80
|
+
console.error(chalk.red(`Import file not found: ${absFile}`));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
const home = ensureKiteHome();
|
|
84
|
+
const dbPath = path.join(home, 'kite.db');
|
|
85
|
+
const spinner = ora('Reading import package...').start();
|
|
86
|
+
const zip = new AdmZip(absFile);
|
|
87
|
+
const manifest = readJsonEntry(zip, 'kite-export/manifest.json');
|
|
88
|
+
if (!manifest) {
|
|
89
|
+
spinner.fail();
|
|
90
|
+
console.error(chalk.red('Invalid import package: kite-export/manifest.json not found.'));
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
if (manifest.schemaVersion !== SUPPORTED_SCHEMA) {
|
|
94
|
+
spinner.fail();
|
|
95
|
+
console.error(chalk.red(`Unsupported schemaVersion ${manifest.schemaVersion} (CLI supports ${SUPPORTED_SCHEMA}). Upgrade CLI and retry.`));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
spinner.succeed(chalk.green(`Loaded export package (exported ${manifest.exportedAt}, kite ${manifest.kiteVersion})`));
|
|
99
|
+
const projects = readJsonEntry(zip, 'kite-export/projects.json') || [];
|
|
100
|
+
const settings = readJsonEntry(zip, 'kite-export/settings.json') || [];
|
|
101
|
+
const deployments = manifest.includes.deployments
|
|
102
|
+
? (readJsonEntry(zip, 'kite-export/deployments.json') || [])
|
|
103
|
+
: [];
|
|
104
|
+
const client = createClient({ url: `file:${dbPath}` });
|
|
105
|
+
try {
|
|
106
|
+
await ensureSchema(client);
|
|
107
|
+
// 预扫现状用于摘要 + skip-existing 判断
|
|
108
|
+
const existingProjectIds = new Set();
|
|
109
|
+
const existingTokens = new Set();
|
|
110
|
+
for (const row of (await client.execute(`SELECT id, token FROM projects`)).rows) {
|
|
111
|
+
existingProjectIds.add(String(row.id));
|
|
112
|
+
existingTokens.add(String(row.token));
|
|
113
|
+
}
|
|
114
|
+
const existingSettingKeys = new Set();
|
|
115
|
+
for (const row of (await client.execute(`SELECT key FROM settings`)).rows) {
|
|
116
|
+
existingSettingKeys.add(String(row.key));
|
|
117
|
+
}
|
|
118
|
+
const existingDeploymentIds = new Set();
|
|
119
|
+
for (const row of (await client.execute(`SELECT id FROM deployments`)).rows) {
|
|
120
|
+
existingDeploymentIds.add(String(row.id));
|
|
121
|
+
}
|
|
122
|
+
const projectCounts = { added: 0, updated: 0, skipped: 0 };
|
|
123
|
+
const settingCounts = { added: 0, updated: 0, skipped: 0 };
|
|
124
|
+
const deploymentCounts = { added: 0, updated: 0, skipped: 0 };
|
|
125
|
+
const projectsToWrite = [];
|
|
126
|
+
const settingsToWrite = [];
|
|
127
|
+
const deploymentsToWrite = [];
|
|
128
|
+
for (const p of projects) {
|
|
129
|
+
const id = String(p.id);
|
|
130
|
+
const token = String(p.token);
|
|
131
|
+
const idExists = existingProjectIds.has(id);
|
|
132
|
+
const tokenConflict = !idExists && existingTokens.has(token);
|
|
133
|
+
if (idExists) {
|
|
134
|
+
if (strategy === 'overwrite') {
|
|
135
|
+
projectsToWrite.push(p);
|
|
136
|
+
projectCounts.updated++;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
projectCounts.skipped++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (tokenConflict) {
|
|
143
|
+
// token unique constraint 会冲突,跳过避免报错
|
|
144
|
+
projectCounts.skipped++;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
projectsToWrite.push(p);
|
|
148
|
+
projectCounts.added++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
for (const s of settings) {
|
|
152
|
+
const key = String(s.key);
|
|
153
|
+
const exists = existingSettingKeys.has(key);
|
|
154
|
+
if (exists) {
|
|
155
|
+
if (strategy === 'overwrite') {
|
|
156
|
+
settingsToWrite.push(s);
|
|
157
|
+
settingCounts.updated++;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
settingCounts.skipped++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
settingsToWrite.push(s);
|
|
165
|
+
settingCounts.added++;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const d of deployments) {
|
|
169
|
+
const id = String(d.id);
|
|
170
|
+
const exists = existingDeploymentIds.has(id);
|
|
171
|
+
if (exists) {
|
|
172
|
+
if (strategy === 'overwrite') {
|
|
173
|
+
deploymentsToWrite.push(d);
|
|
174
|
+
deploymentCounts.updated++;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
deploymentCounts.skipped++;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
deploymentsToWrite.push(d);
|
|
182
|
+
deploymentCounts.added++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// 摘要
|
|
186
|
+
console.log(chalk.bold('\nImport summary:'));
|
|
187
|
+
console.log(` Strategy: ${strategy}`);
|
|
188
|
+
console.log(` DB path: ${dbPath}`);
|
|
189
|
+
console.log(` Projects: + ${projectCounts.added} new, ~ ${projectCounts.updated} update, - ${projectCounts.skipped} skip`);
|
|
190
|
+
console.log(` Settings: + ${settingCounts.added} new, ~ ${settingCounts.updated} update, - ${settingCounts.skipped} skip`);
|
|
191
|
+
if (manifest.includes.deployments) {
|
|
192
|
+
console.log(` Deployments: + ${deploymentCounts.added} new, ~ ${deploymentCounts.updated} update, - ${deploymentCounts.skipped} skip`);
|
|
193
|
+
}
|
|
194
|
+
if (manifest.includes.artifacts) {
|
|
195
|
+
const restoreCount = options.restoreArtifacts ? manifest.artifacts.filter(a => a.archive).length : 0;
|
|
196
|
+
console.log(` Artifacts: ${options.restoreArtifacts ? `restore ${restoreCount}` : 'available, not restored (pass --restore-artifacts)'}`);
|
|
197
|
+
}
|
|
198
|
+
console.log(` Global cfg: will write projectToken for ${projectsToWrite.length} projects to ~/.kite/config.json`);
|
|
199
|
+
if (options.dryRun) {
|
|
200
|
+
console.log(chalk.gray('\nDry-run complete. No changes written.'));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const writeSpinner = ora('Writing to database...').start();
|
|
204
|
+
const placeholders = (cols) => cols.map(() => '?').join(', ');
|
|
205
|
+
const upsertProject = (cols) => `INSERT INTO projects (${cols.join(', ')}) VALUES (${placeholders(cols)}) ` +
|
|
206
|
+
`ON CONFLICT(id) DO UPDATE SET ${cols.filter(c => c !== 'id').map(c => `${c} = excluded.${c}`).join(', ')}`;
|
|
207
|
+
const upsertSetting = `INSERT INTO settings (key, value) VALUES (?, ?) ` +
|
|
208
|
+
`ON CONFLICT(key) DO UPDATE SET value = excluded.value`;
|
|
209
|
+
const upsertDeployment = (cols) => `INSERT INTO deployments (${cols.join(', ')}) VALUES (${placeholders(cols)}) ` +
|
|
210
|
+
`ON CONFLICT(id) DO UPDATE SET ${cols.filter(c => c !== 'id').map(c => `${c} = excluded.${c}`).join(', ')}`;
|
|
211
|
+
const tx = await client.transaction('write');
|
|
212
|
+
try {
|
|
213
|
+
for (const p of projectsToWrite) {
|
|
214
|
+
const args = PROJECT_COLUMNS.map(c => (p[c] ?? null));
|
|
215
|
+
await tx.execute({ sql: upsertProject(PROJECT_COLUMNS), args });
|
|
216
|
+
}
|
|
217
|
+
for (const s of settingsToWrite) {
|
|
218
|
+
await tx.execute({ sql: upsertSetting, args: [String(s.key), String(s.value ?? '')] });
|
|
219
|
+
}
|
|
220
|
+
for (const d of deploymentsToWrite) {
|
|
221
|
+
const args = DEPLOYMENT_COLUMNS.map(c => (d[c] ?? null));
|
|
222
|
+
await tx.execute({ sql: upsertDeployment(DEPLOYMENT_COLUMNS), args });
|
|
223
|
+
}
|
|
224
|
+
await tx.commit();
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
await tx.rollback();
|
|
228
|
+
writeSpinner.fail();
|
|
229
|
+
throw err;
|
|
230
|
+
}
|
|
231
|
+
writeSpinner.succeed(chalk.green('Database updated.'));
|
|
232
|
+
// 写回 CLI 全局 config,方便目标机器直接 kite push
|
|
233
|
+
try {
|
|
234
|
+
const globalConfig = readGlobalConfig();
|
|
235
|
+
const nextProjectToken = { ...(globalConfig.projectToken || {}) };
|
|
236
|
+
let tokenAdded = 0;
|
|
237
|
+
let tokenUpdated = 0;
|
|
238
|
+
let tokenSkipped = 0;
|
|
239
|
+
for (const p of projectsToWrite) {
|
|
240
|
+
const projectId = String(p.id);
|
|
241
|
+
const token = p.token != null ? String(p.token) : '';
|
|
242
|
+
if (!projectId || !token)
|
|
243
|
+
continue;
|
|
244
|
+
const existing = nextProjectToken[projectId];
|
|
245
|
+
if (existing === undefined) {
|
|
246
|
+
nextProjectToken[projectId] = token;
|
|
247
|
+
tokenAdded++;
|
|
248
|
+
}
|
|
249
|
+
else if (strategy === 'overwrite') {
|
|
250
|
+
if (existing !== token) {
|
|
251
|
+
nextProjectToken[projectId] = token;
|
|
252
|
+
tokenUpdated++;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
tokenSkipped++;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const serverUrlMissing = !globalConfig.serverUrl;
|
|
260
|
+
const nextServerUrl = serverUrlMissing ? DEFAULT_LOCAL_SERVER_URL : globalConfig.serverUrl;
|
|
261
|
+
writeGlobalConfig({
|
|
262
|
+
...globalConfig,
|
|
263
|
+
serverUrl: nextServerUrl,
|
|
264
|
+
projectToken: nextProjectToken,
|
|
265
|
+
});
|
|
266
|
+
console.log(chalk.green('Global config updated (~/.kite/config.json).'));
|
|
267
|
+
console.log(chalk.gray(` projectToken: + ${tokenAdded} new, ~ ${tokenUpdated} update, - ${tokenSkipped} skip`));
|
|
268
|
+
if (serverUrlMissing) {
|
|
269
|
+
console.log(chalk.gray(` serverUrl: set to ${DEFAULT_LOCAL_SERVER_URL} (override via \`kite config:set serverUrl <url>\`)`));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
console.log(chalk.yellow(` ! Failed to update global config: ${err.message}`));
|
|
274
|
+
}
|
|
275
|
+
if (manifest.includes.artifacts && options.restoreArtifacts) {
|
|
276
|
+
// 重新读取写入后 projects 表,获取最新 deploy_path(可能用户保留了已有项目的路径)
|
|
277
|
+
const deployPathMap = new Map();
|
|
278
|
+
for (const row of (await client.execute(`SELECT id, deploy_path FROM projects`)).rows) {
|
|
279
|
+
deployPathMap.set(String(row.id), String(row.deploy_path));
|
|
280
|
+
}
|
|
281
|
+
const artifactSpinner = ora('Restoring artifacts...').start();
|
|
282
|
+
let okCount = 0;
|
|
283
|
+
let warnCount = 0;
|
|
284
|
+
for (const a of manifest.artifacts) {
|
|
285
|
+
if (!a.archive)
|
|
286
|
+
continue;
|
|
287
|
+
const target = deployPathMap.get(a.projectId);
|
|
288
|
+
if (!target) {
|
|
289
|
+
console.log(chalk.yellow(` ! ${a.projectId}: project not in DB, skip restore`));
|
|
290
|
+
warnCount++;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const entry = zip.getEntry(`kite-export/${a.archive}`);
|
|
294
|
+
if (!entry) {
|
|
295
|
+
console.log(chalk.yellow(` ! ${a.projectId}: archive entry missing`));
|
|
296
|
+
warnCount++;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
fs.mkdirSync(target, { recursive: true });
|
|
301
|
+
const tmpZipPath = path.join(home, 'tmp', `restore-${a.projectId}-${Date.now()}.zip`);
|
|
302
|
+
fs.mkdirSync(path.dirname(tmpZipPath), { recursive: true });
|
|
303
|
+
fs.writeFileSync(tmpZipPath, entry.getData());
|
|
304
|
+
const innerZip = new AdmZip(tmpZipPath);
|
|
305
|
+
// 路径穿越校验
|
|
306
|
+
for (const e of innerZip.getEntries()) {
|
|
307
|
+
const dest = path.resolve(target, e.entryName);
|
|
308
|
+
if (!dest.startsWith(path.resolve(target) + path.sep) && dest !== path.resolve(target)) {
|
|
309
|
+
throw new Error(`Refusing to extract entry outside target: ${e.entryName}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
innerZip.extractAllTo(target, true);
|
|
313
|
+
fs.unlinkSync(tmpZipPath);
|
|
314
|
+
okCount++;
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
console.log(chalk.yellow(` ! ${a.projectId}: restore failed - ${err.message}`));
|
|
318
|
+
warnCount++;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
artifactSpinner.succeed(chalk.green(`Artifacts restored: ${okCount} ok, ${warnCount} warnings`));
|
|
322
|
+
}
|
|
323
|
+
console.log(chalk.green('\nImport completed.'));
|
|
324
|
+
if (!manifest.includes.artifacts || !options.restoreArtifacts) {
|
|
325
|
+
console.log(chalk.gray('Tip: start `kite serve` to verify projects in the dashboard.'));
|
|
326
|
+
}
|
|
327
|
+
console.log(chalk.gray('Tip: `kite push` from your project directory now uses the restored token automatically.'));
|
|
328
|
+
console.log(chalk.gray('Tip: run `kite verify` to self-check migration integrity (db, deploy paths, tokens).'));
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
client.close();
|
|
332
|
+
}
|
|
333
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,12 @@ import { uploadZip } from './upload.js';
|
|
|
10
10
|
import { getConfigPath, getKiteHome, randomToken, readGlobalConfig, readLocalEnv, setGlobalConfig, writeGlobalConfig, writeLocalEnvValue, listProjectEnvs, resolveProjectConfig, envTokenKey } from './home.js';
|
|
11
11
|
import { LocalStore } from './local-store.js';
|
|
12
12
|
import { startServe } from './serve.js';
|
|
13
|
+
import { parseIgnoreOption } from './ignore.js';
|
|
14
|
+
import { runExport } from './export.js';
|
|
15
|
+
import { runImport } from './import.js';
|
|
16
|
+
import { runVerify } from './verify.js';
|
|
17
|
+
import { runDoctor } from './doctor.js';
|
|
18
|
+
import { runList, runStatus, runLogs, runRollback } from './ops.js';
|
|
13
19
|
// @ts-ignore
|
|
14
20
|
const cli = cac('kite');
|
|
15
21
|
// ==========================
|
|
@@ -482,6 +488,8 @@ const displayPackResult = (result) => {
|
|
|
482
488
|
cli.command('build', 'Pack project files and verify packaging (no upload)')
|
|
483
489
|
.option('--env <name>', 'Environment name (selects kite.config.<name>.json)')
|
|
484
490
|
.option('--out <dir>', 'Output directory to pack')
|
|
491
|
+
.option('--ignore <patterns>', 'Extra ignore patterns (comma separated, may repeat)')
|
|
492
|
+
.option('--no-ignore-builtin', 'Disable built-in ignore patterns (node_modules, .git, .env*, etc.)')
|
|
485
493
|
.action(async (options) => {
|
|
486
494
|
try {
|
|
487
495
|
const allEnvs = listProjectEnvs();
|
|
@@ -513,9 +521,21 @@ cli.command('build', 'Pack project files and verify packaging (no upload)')
|
|
|
513
521
|
console.error(chalk.red(`Error: Output directory not found: ${sourceDir}`));
|
|
514
522
|
process.exit(1);
|
|
515
523
|
}
|
|
524
|
+
const cliIgnore = parseIgnoreOption(options.ignore);
|
|
525
|
+
const ignore = cliIgnore.length > 0
|
|
526
|
+
? cliIgnore
|
|
527
|
+
: (Array.isArray(projectConfig.ignore) ? projectConfig.ignore : []);
|
|
528
|
+
// cac: --no-ignore-builtin 时 options.ignoreBuiltin === false;未传时 true
|
|
529
|
+
const ignoreBuiltin = options.ignoreBuiltin === false
|
|
530
|
+
? true
|
|
531
|
+
: (projectConfig.ignoreBuiltin === true);
|
|
516
532
|
const spinner = ora('Packing files...').start();
|
|
517
533
|
const zipFilePath = path.resolve(process.cwd(), '.deploy-archive.zip');
|
|
518
|
-
const result = await packProject(sourceDir, zipFilePath,
|
|
534
|
+
const result = await packProject(sourceDir, zipFilePath, {
|
|
535
|
+
files: files.length > 0 ? files : undefined,
|
|
536
|
+
ignore,
|
|
537
|
+
ignoreBuiltin,
|
|
538
|
+
});
|
|
519
539
|
spinner.succeed(chalk.green('Pack successful!'));
|
|
520
540
|
displayPackResult(result);
|
|
521
541
|
console.log(chalk.gray(` Archive: ${zipFilePath}`));
|
|
@@ -538,7 +558,10 @@ cli.command('push', 'Push and deploy project')
|
|
|
538
558
|
.option('--post <script>', 'Post-deploy script (Server side)')
|
|
539
559
|
.option('--command <script>', 'Deploy command alias, same as --post')
|
|
540
560
|
.option('--set-env <vars>', 'Environment variables as JSON or KEY=VALUE (overrides config)')
|
|
561
|
+
.option('--ignore <patterns>', 'Extra ignore patterns (comma separated, may repeat)')
|
|
562
|
+
.option('--no-ignore-builtin', 'Disable built-in ignore patterns (node_modules, .git, .env*, etc.)')
|
|
541
563
|
.action(async (options) => {
|
|
564
|
+
const pushStartedAt = new Date().toISOString();
|
|
542
565
|
try {
|
|
543
566
|
// 1. 解析环境配置
|
|
544
567
|
const allEnvs = listProjectEnvs();
|
|
@@ -624,9 +647,21 @@ cli.command('push', 'Push and deploy project')
|
|
|
624
647
|
process.exit(1);
|
|
625
648
|
}
|
|
626
649
|
// 5. 打包文件
|
|
650
|
+
const cliIgnore = parseIgnoreOption(options.ignore);
|
|
651
|
+
const ignore = cliIgnore.length > 0
|
|
652
|
+
? cliIgnore
|
|
653
|
+
: (Array.isArray(projectConfig.ignore) ? projectConfig.ignore : []);
|
|
654
|
+
// cac: --no-ignore-builtin 时 options.ignoreBuiltin === false;未传时 true
|
|
655
|
+
const ignoreBuiltin = options.ignoreBuiltin === false
|
|
656
|
+
? true
|
|
657
|
+
: (projectConfig.ignoreBuiltin === true);
|
|
627
658
|
const spinner = ora('Packing files...').start();
|
|
628
659
|
const zipFilePath = path.resolve(process.cwd(), '.deploy-archive.zip');
|
|
629
|
-
const packResult = await packProject(sourceDir, zipFilePath,
|
|
660
|
+
const packResult = await packProject(sourceDir, zipFilePath, {
|
|
661
|
+
files: files.length > 0 ? files : undefined,
|
|
662
|
+
ignore,
|
|
663
|
+
ignoreBuiltin,
|
|
664
|
+
});
|
|
630
665
|
spinner.succeed(chalk.green('Packed successfully'));
|
|
631
666
|
displayPackResult(packResult);
|
|
632
667
|
// 6. 上传与部署
|
|
@@ -639,13 +674,20 @@ cli.command('push', 'Push and deploy project')
|
|
|
639
674
|
projectId,
|
|
640
675
|
preDeploy,
|
|
641
676
|
postDeploy,
|
|
642
|
-
env: deployEnv
|
|
677
|
+
env: deployEnv,
|
|
678
|
+
startedAt: pushStartedAt
|
|
643
679
|
});
|
|
644
680
|
if (result.success) {
|
|
645
681
|
console.log(chalk.green(`\nDeployed successfully! (${result.duration})`));
|
|
682
|
+
if (result.traceId) {
|
|
683
|
+
console.log(chalk.gray(` trace: ${result.traceId}`));
|
|
684
|
+
}
|
|
646
685
|
}
|
|
647
686
|
else {
|
|
648
687
|
console.error(chalk.red('\nDeployment failed'));
|
|
688
|
+
if (result.traceId) {
|
|
689
|
+
console.error(chalk.gray(` trace: ${result.traceId}`));
|
|
690
|
+
}
|
|
649
691
|
process.exit(1);
|
|
650
692
|
}
|
|
651
693
|
}
|
|
@@ -661,6 +703,162 @@ cli.command('push', 'Push and deploy project')
|
|
|
661
703
|
}
|
|
662
704
|
}
|
|
663
705
|
});
|
|
706
|
+
// ==========================
|
|
707
|
+
// Migration commands: export / import
|
|
708
|
+
// ==========================
|
|
709
|
+
cli.command('export', 'Export Kite database (and optional artifacts) to a portable archive')
|
|
710
|
+
.option('--out <file>', 'Output file path (default: kite-export-<timestamp>.zip)')
|
|
711
|
+
.option('--no-include-artifacts', 'Skip packing each project deployPath contents (default: include)')
|
|
712
|
+
.option('--no-include-logs', 'Skip deployment history (default: include)')
|
|
713
|
+
.option('--projects <ids>', 'Comma separated project ids to include (default: all)')
|
|
714
|
+
.option('--ignore <patterns>', 'Extra ignore patterns for artifacts (comma separated, may repeat)')
|
|
715
|
+
.option('--no-ignore-builtin', 'Disable built-in ignore patterns when packing artifacts')
|
|
716
|
+
.action(async (options) => {
|
|
717
|
+
try {
|
|
718
|
+
const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
|
719
|
+
const ignoreBuiltin = options.ignoreBuiltin === false;
|
|
720
|
+
await runExport({
|
|
721
|
+
out: options.out,
|
|
722
|
+
includeArtifacts: options.includeArtifacts !== false,
|
|
723
|
+
includeLogs: options.includeLogs !== false,
|
|
724
|
+
projects: options.projects,
|
|
725
|
+
ignore: options.ignore,
|
|
726
|
+
ignoreBuiltin,
|
|
727
|
+
}, pkg.version || '0.0.0');
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
console.error(chalk.red(`\nExport failed: ${error.message}`));
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
cli.command('import <file>', 'Import Kite database from an export archive')
|
|
735
|
+
.option('--strategy <mode>', 'Conflict strategy: merge | overwrite | skip-existing', { default: 'skip-existing' })
|
|
736
|
+
.option('--no-restore-artifacts', 'Skip restoring each project deployPath from the archive (default: restore when archive contains artifacts)')
|
|
737
|
+
.option('--dry-run', 'Show summary without writing')
|
|
738
|
+
.option('--yes', 'Confirm destructive --strategy overwrite')
|
|
739
|
+
.action(async (file, options) => {
|
|
740
|
+
try {
|
|
741
|
+
await runImport(file, {
|
|
742
|
+
strategy: options.strategy,
|
|
743
|
+
restoreArtifacts: options.restoreArtifacts !== false,
|
|
744
|
+
dryRun: !!options.dryRun,
|
|
745
|
+
yes: !!options.yes,
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
console.error(chalk.red(`\nImport failed: ${error.message}`));
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
cli.command('verify', 'Verify ~/.kite migration integrity (db, deploy paths, tokens, optional server health)')
|
|
754
|
+
.option('--check-server', 'Also probe configured serverUrl with HTTP GET')
|
|
755
|
+
.option('--timeout <ms>', 'Server probe timeout in ms', { default: 5000 })
|
|
756
|
+
.action(async (options) => {
|
|
757
|
+
try {
|
|
758
|
+
await runVerify({
|
|
759
|
+
checkServer: !!options.checkServer,
|
|
760
|
+
timeout: Number(options.timeout) || 5000,
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
console.error(chalk.red(`\nVerify failed: ${error.message}`));
|
|
765
|
+
process.exit(1);
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
cli.command('doctor', 'Run local + remote health diagnostics')
|
|
769
|
+
.option('--server <url>', 'Override server URL (defaults to global config / KITE_SERVER_URL)')
|
|
770
|
+
.option('--token <token>', 'Override admin token (defaults to global config / KITE_TOKEN)')
|
|
771
|
+
.action(async (options) => {
|
|
772
|
+
try {
|
|
773
|
+
const code = await runDoctor({ server: options.server, token: options.token });
|
|
774
|
+
process.exit(code);
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
console.error(chalk.red(`\nDoctor failed: ${error.message}`));
|
|
778
|
+
process.exit(1);
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
cli.command('list', 'List projects on Kite server')
|
|
782
|
+
.option('--server <url>', 'Override server URL')
|
|
783
|
+
.option('--token <token>', 'Override admin token')
|
|
784
|
+
.option('--env <name>', 'Filter by project env')
|
|
785
|
+
.option('--json', 'Output JSON (no colors)')
|
|
786
|
+
.action(async (options) => {
|
|
787
|
+
try {
|
|
788
|
+
const code = await runList({ server: options.server, token: options.token, env: options.env, json: options.json });
|
|
789
|
+
process.exit(code);
|
|
790
|
+
}
|
|
791
|
+
catch (error) {
|
|
792
|
+
console.error(chalk.red(`\nList failed: ${error.message}`));
|
|
793
|
+
process.exit(1);
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
cli.command('status [projectId]', 'Show recent deployments of a project')
|
|
797
|
+
.option('--server <url>', 'Override server URL')
|
|
798
|
+
.option('--token <token>', 'Override admin token')
|
|
799
|
+
.option('--env <name>', 'Pick kite.config.<env>.json when no projectId given')
|
|
800
|
+
.option('--limit <n>', 'Number of deployments to show (default 5, max 50)')
|
|
801
|
+
.option('--json', 'Output JSON')
|
|
802
|
+
.action(async (projectId, options) => {
|
|
803
|
+
try {
|
|
804
|
+
const code = await runStatus(projectId, {
|
|
805
|
+
server: options.server,
|
|
806
|
+
token: options.token,
|
|
807
|
+
env: options.env,
|
|
808
|
+
limit: options.limit ? Number(options.limit) : undefined,
|
|
809
|
+
json: options.json,
|
|
810
|
+
});
|
|
811
|
+
process.exit(code);
|
|
812
|
+
}
|
|
813
|
+
catch (error) {
|
|
814
|
+
console.error(chalk.red(`\nStatus failed: ${error.message}`));
|
|
815
|
+
process.exit(1);
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
cli.command('logs <deployId>', 'Print deployment logs (or follow live with -f)')
|
|
819
|
+
.option('--server <url>', 'Override server URL')
|
|
820
|
+
.option('--token <token>', 'Override admin token')
|
|
821
|
+
.option('-f, --follow', 'Stream live logs via SSE until the deployment finishes')
|
|
822
|
+
.option('--json', 'Output JSON (only without --follow)')
|
|
823
|
+
.action(async (deployId, options) => {
|
|
824
|
+
try {
|
|
825
|
+
const code = await runLogs(deployId, {
|
|
826
|
+
server: options.server,
|
|
827
|
+
token: options.token,
|
|
828
|
+
follow: options.follow,
|
|
829
|
+
json: options.json,
|
|
830
|
+
});
|
|
831
|
+
process.exit(code);
|
|
832
|
+
}
|
|
833
|
+
catch (error) {
|
|
834
|
+
console.error(chalk.red(`\nLogs failed: ${error.message}`));
|
|
835
|
+
process.exit(1);
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
cli.command('rollback [projectId]', 'Rollback a project to a previous successful deployment')
|
|
839
|
+
.option('--server <url>', 'Override server URL')
|
|
840
|
+
.option('--token <token>', 'Override admin token (admin required)')
|
|
841
|
+
.option('--env <name>', 'Pick kite.config.<env>.json when no projectId given')
|
|
842
|
+
.option('--to <deployId>', 'Target deployment to roll back to (default: previous success)')
|
|
843
|
+
.option('--yes', 'Skip interactive confirmation (required in non-TTY)')
|
|
844
|
+
.option('--json', 'Output JSON on success')
|
|
845
|
+
.action(async (projectId, options) => {
|
|
846
|
+
try {
|
|
847
|
+
const code = await runRollback(projectId, {
|
|
848
|
+
server: options.server,
|
|
849
|
+
token: options.token,
|
|
850
|
+
env: options.env,
|
|
851
|
+
to: options.to,
|
|
852
|
+
yes: options.yes,
|
|
853
|
+
json: options.json,
|
|
854
|
+
});
|
|
855
|
+
process.exit(code);
|
|
856
|
+
}
|
|
857
|
+
catch (error) {
|
|
858
|
+
console.error(chalk.red(`\nRollback failed: ${error.message}`));
|
|
859
|
+
process.exit(1);
|
|
860
|
+
}
|
|
861
|
+
});
|
|
664
862
|
cli.help();
|
|
665
863
|
const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
|
666
864
|
cli.version(pkg.version);
|