ninedeploy 0.18.26 → 0.18.27
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/commands/databaseUpgrade.d.ts +25 -0
- package/dist/commands/databaseUpgrade.d.ts.map +1 -0
- package/dist/commands/databaseUpgrade.js +136 -0
- package/dist/commands/databaseUpgrade.js.map +1 -0
- package/dist/commands/importApply.d.ts +114 -0
- package/dist/commands/importApply.d.ts.map +1 -0
- package/dist/commands/importApply.js +403 -0
- package/dist/commands/importApply.js.map +1 -0
- package/dist/commands/importCutover.d.ts +95 -0
- package/dist/commands/importCutover.d.ts.map +1 -0
- package/dist/commands/importCutover.js +293 -0
- package/dist/commands/importCutover.js.map +1 -0
- package/dist/commands/importData.d.ts +60 -0
- package/dist/commands/importData.d.ts.map +1 -0
- package/dist/commands/importData.js +286 -0
- package/dist/commands/importData.js.map +1 -0
- package/dist/commands/importPlan.d.ts +32 -0
- package/dist/commands/importPlan.d.ts.map +1 -0
- package/dist/commands/importPlan.js +138 -0
- package/dist/commands/importPlan.js.map +1 -0
- package/dist/commands/importVolumes.d.ts +38 -0
- package/dist/commands/importVolumes.d.ts.map +1 -0
- package/dist/commands/importVolumes.js +161 -0
- package/dist/commands/importVolumes.js.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/importers/caprover.d.ts +13 -0
- package/dist/lib/importers/caprover.d.ts.map +1 -0
- package/dist/lib/importers/caprover.js +150 -0
- package/dist/lib/importers/caprover.js.map +1 -0
- package/dist/lib/importers/connections.d.ts +39 -0
- package/dist/lib/importers/connections.d.ts.map +1 -0
- package/dist/lib/importers/connections.js +119 -0
- package/dist/lib/importers/connections.js.map +1 -0
- package/dist/lib/importers/coolify.d.ts +10 -0
- package/dist/lib/importers/coolify.d.ts.map +1 -0
- package/dist/lib/importers/coolify.js +174 -0
- package/dist/lib/importers/coolify.js.map +1 -0
- package/dist/lib/importers/dokploy.d.ts +13 -0
- package/dist/lib/importers/dokploy.d.ts.map +1 -0
- package/dist/lib/importers/dokploy.js +183 -0
- package/dist/lib/importers/dokploy.js.map +1 -0
- package/dist/lib/importers/heroku.d.ts +50 -0
- package/dist/lib/importers/heroku.d.ts.map +1 -0
- package/dist/lib/importers/heroku.js +159 -0
- package/dist/lib/importers/heroku.js.map +1 -0
- package/dist/lib/importers/plan.d.ts +137 -0
- package/dist/lib/importers/plan.d.ts.map +1 -0
- package/dist/lib/importers/plan.js +110 -0
- package/dist/lib/importers/plan.js.map +1 -0
- package/dist/lib/importers/shell.d.ts +24 -0
- package/dist/lib/importers/shell.d.ts.map +1 -0
- package/dist/lib/importers/shell.js +45 -0
- package/dist/lib/importers/shell.js.map +1 -0
- package/dist/lib/importers/sources.d.ts +26 -0
- package/dist/lib/importers/sources.d.ts.map +1 -0
- package/dist/lib/importers/sources.js +100 -0
- package/dist/lib/importers/sources.js.map +1 -0
- package/package.json +3 -3
- package/src/commands/databaseUpgrade.ts +124 -0
- package/src/commands/importApply.ts +445 -0
- package/src/commands/importCutover.ts +329 -0
- package/src/commands/importData.ts +302 -0
- package/src/commands/importPlan.ts +143 -0
- package/src/commands/importVolumes.ts +168 -0
- package/src/index.ts +61 -0
- package/src/lib/importers/caprover.ts +141 -0
- package/src/lib/importers/connections.ts +133 -0
- package/src/lib/importers/coolify.ts +164 -0
- package/src/lib/importers/dokploy.ts +167 -0
- package/src/lib/importers/heroku.ts +168 -0
- package/src/lib/importers/plan.ts +209 -0
- package/src/lib/importers/shell.ts +56 -0
- package/src/lib/importers/sources.ts +96 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ninedeploy import data <plan.json> [--dump <database>=<file> …] [--yes]`
|
|
3
|
+
*
|
|
4
|
+
* A1 step 3, data half (docs/plans/COMPETITIVE_ROADMAP_2026-11.md). Moves
|
|
5
|
+
* database contents into the databases `import apply` created.
|
|
6
|
+
*
|
|
7
|
+
* - Without --yes it prints the cutover order and, for each database, the
|
|
8
|
+
* command that takes a dump on the source host. The commands read the
|
|
9
|
+
* credentials from the database container's own environment, inside
|
|
10
|
+
* single quotes, so no password reaches this output or the shell history.
|
|
11
|
+
* - With --dump and --yes it uploads each file through the panel's chunked
|
|
12
|
+
* dump import, declaring the file's sha256. The panel checks it before it
|
|
13
|
+
* restores anything.
|
|
14
|
+
* - The receipt records each import's id, sha256 and size, never content. A
|
|
15
|
+
* run that stops during the upload resumes the same import. A database whose
|
|
16
|
+
* data is already in is not imported again.
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from 'node:crypto';
|
|
19
|
+
import { createReadStream, existsSync, readFileSync, statSync } from 'node:fs';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { getClient } from '../client.js';
|
|
22
|
+
import { loadConfig } from '../config.js';
|
|
23
|
+
import { c, error, header, info, success } from '../lib/format.js';
|
|
24
|
+
import { fileBase, flagArg, isPlainName, sourceTarget } from '../lib/importers/shell.js';
|
|
25
|
+
import { renderReceipt, validatePlan, writeReceipt } from './importApply.js';
|
|
26
|
+
import { importVolumes, renderVolumePreview } from './importVolumes.js';
|
|
27
|
+
/** Engines the panel's dump import accepts (schemas databaseImport.ts). */
|
|
28
|
+
const IMPORTABLE = {
|
|
29
|
+
postgres: true, mysql: true, mariadb: true, mongo: true, redis: true, keydb: true, dragonfly: true, clickhouse: false,
|
|
30
|
+
};
|
|
31
|
+
/** Dump file name per engine, by format. */
|
|
32
|
+
const DUMP_FILE = {
|
|
33
|
+
postgres: (n) => `${n}.dump`, mysql: (n) => `${n}.sql`, mariadb: (n) => `${n}.sql`, mongo: (n) => `${n}.archive.gz`,
|
|
34
|
+
redis: (n) => `${n}.rdb`, keydb: (n) => `${n}.rdb`, dragonfly: (n) => `${n}.rdb`, clickhouse: (n) => n,
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The command, run on the source host, that writes a dump the panel's import
|
|
38
|
+
* accepts: pg_dump custom format, mysqldump/mariadb-dump SQL of the app
|
|
39
|
+
* database (not the system schemas, which the import refuses), a gzipped
|
|
40
|
+
* mongodump archive, an RDB snapshot.
|
|
41
|
+
*
|
|
42
|
+
* The MySQL/MariaDB dump names the database positionally, never with
|
|
43
|
+
* --databases: that form writes `CREATE DATABASE shopdb; USE shopdb;`, and
|
|
44
|
+
* the panel feeds the file to `mysql … app` as it is, so the rows would land
|
|
45
|
+
* in `shopdb` instead of the managed `app` database (and the panel refuses
|
|
46
|
+
* any `USE` other than `USE app` from a non-operator).
|
|
47
|
+
*/
|
|
48
|
+
export function dumpCommand(db) {
|
|
49
|
+
if (!db.engine || !IMPORTABLE[db.engine])
|
|
50
|
+
return null;
|
|
51
|
+
const file = DUMP_FILE[db.engine](fileBase(db.name));
|
|
52
|
+
if (db.runtime?.kind === 'heroku-app') {
|
|
53
|
+
// Heroku Postgres: a pg_dump custom-format backup taken by Heroku, downloaded with the Heroku CLI.
|
|
54
|
+
if (db.engine !== 'postgres')
|
|
55
|
+
return null;
|
|
56
|
+
const app = isPlainName(db.runtime.name) ? db.runtime.name : '<app>';
|
|
57
|
+
return {
|
|
58
|
+
command: `heroku pg:backups:capture -a ${app} && curl -sSfo ${file} "$(heroku pg:backups:url -a ${app})"`,
|
|
59
|
+
file,
|
|
60
|
+
notes: app === '<app>' ? ['Replace <app> with the Heroku app name.'] : [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const notes = [];
|
|
64
|
+
const { target, note } = sourceTarget(db.runtime, 'the name of the database container');
|
|
65
|
+
if (note)
|
|
66
|
+
notes.push(note);
|
|
67
|
+
let inner;
|
|
68
|
+
let auth = '';
|
|
69
|
+
switch (db.engine) {
|
|
70
|
+
case 'postgres':
|
|
71
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell parameter expansion, run inside the container
|
|
72
|
+
inner = 'pg_dump -Fc -U "${POSTGRES_USER:-postgres}" "${POSTGRES_DB:-${POSTGRES_USER:-postgres}}"';
|
|
73
|
+
break;
|
|
74
|
+
case 'mysql':
|
|
75
|
+
inner = 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers "$MYSQL_DATABASE"';
|
|
76
|
+
notes.push('If the container has no MYSQL_DATABASE, put the database name in its place.');
|
|
77
|
+
break;
|
|
78
|
+
case 'mariadb':
|
|
79
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell parameter expansion, run inside the container
|
|
80
|
+
inner = 'exec mariadb-dump -uroot -p"${MARIADB_ROOT_PASSWORD:-$MYSQL_ROOT_PASSWORD}" --single-transaction --routines --triggers "${MARIADB_DATABASE:-$MYSQL_DATABASE}"';
|
|
81
|
+
notes.push('If the container has no MARIADB_DATABASE or MYSQL_DATABASE, put the database name in its place.');
|
|
82
|
+
break;
|
|
83
|
+
case 'mongo':
|
|
84
|
+
inner = 'exec mongodump --archive --gzip -u "$MONGO_INITDB_ROOT_USERNAME" -p "$MONGO_INITDB_ROOT_PASSWORD" --authenticationDatabase admin';
|
|
85
|
+
break;
|
|
86
|
+
default: {
|
|
87
|
+
// redis, keydb, dragonfly: an RDB snapshot through the CLI the image ships.
|
|
88
|
+
const cli = db.engine === 'keydb' ? 'keydb-cli' : 'redis-cli';
|
|
89
|
+
inner = `${cli} --rdb /tmp/nd-dump.rdb >/dev/null && cat /tmp/nd-dump.rdb && rm -f /tmp/nd-dump.rdb`;
|
|
90
|
+
auth = ' -e REDISCLI_AUTH';
|
|
91
|
+
notes.push('With a password, first run `read -rs REDISCLI_AUTH; export REDISCLI_AUTH` on the source host; -e passes it by name, never on the command line.');
|
|
92
|
+
if (db.engine === 'dragonfly')
|
|
93
|
+
notes.push('Dragonfly images do not always ship redis-cli; run it from a redis image on the same network if it is missing.');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { command: `docker exec${auth} ${target} sh -c '${inner}' > ${file}`, file, notes };
|
|
97
|
+
}
|
|
98
|
+
/** The order that loses no writes, and the steps this command does not do. */
|
|
99
|
+
export const CUTOVER_ORDER = [
|
|
100
|
+
'A day ahead: lower the TTL of each domain\'s DNS record (for example to 60 seconds).',
|
|
101
|
+
'Stop writes on the source: stop the apps that write, or put them in maintenance. A dump of a live database misses every write made after it.',
|
|
102
|
+
'Take the final dump of each database with the commands below, on the source host.',
|
|
103
|
+
'Import them: ninedeploy import data plan.json --dump <database>=<file> --yes',
|
|
104
|
+
'Deploy each service in NineDeploy and check it is healthy. import apply attached the new database where a copied connection string allowed it; change the variables it listed by hand.',
|
|
105
|
+
'Switch DNS to this panel, then add the domains: ninedeploy import cutover plan.json --yes (it adds only hosts whose DNS already points here).',
|
|
106
|
+
];
|
|
107
|
+
const SOURCE_CHECKED_LIVE = { caprover: true, coolify: false, dokploy: true, heroku: false };
|
|
108
|
+
export function renderDataPreview(plan, receipt) {
|
|
109
|
+
const out = ['Order:'];
|
|
110
|
+
for (const [i, step] of CUTOVER_ORDER.entries())
|
|
111
|
+
out.push(` ${i + 1}. ${step}`);
|
|
112
|
+
out.push('', 'Databases:');
|
|
113
|
+
for (const db of plan.databases) {
|
|
114
|
+
const created = receipt?.items[`database:${db.ref}`];
|
|
115
|
+
const data = receipt?.items[`data:${db.ref}`];
|
|
116
|
+
if (!created || created.state !== 'done') {
|
|
117
|
+
out.push(` – ${db.name}: not created yet (run import apply first)`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (data?.state === 'done') {
|
|
121
|
+
out.push(` ✓ ${db.name}: imported (#${data.importId}, sha256 ${data.sha256?.slice(0, 12)}…)`);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// A Heroku add-on without the app name has no command (and no container to docker exec into).
|
|
125
|
+
const cmd = plan.source.kind === 'heroku' && db.runtime?.kind !== 'heroku-app' ? null : dumpCommand(db);
|
|
126
|
+
if (!cmd && plan.source.kind === 'heroku') {
|
|
127
|
+
out.push(` – ${db.name}: ${db.engine === 'postgres' ? 'pass --app <heroku app> to import plan for the pg:backups command, or download a backup from Heroku' : 'Heroku has no export command for it; move its keys by hand'}`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!cmd) {
|
|
131
|
+
out.push(` – ${db.name}: the panel has no dump import for ${db.engine ?? 'this engine'}; move its data by hand`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
out.push(` + ${db.name} (${db.engine}, NineDeploy database #${created.id}):`);
|
|
135
|
+
out.push(` ${cmd.command}`);
|
|
136
|
+
for (const n of cmd.notes)
|
|
137
|
+
out.push(` ${n}`);
|
|
138
|
+
out.push(` then: --dump ${flagArg(db.name, cmd.file)}`);
|
|
139
|
+
}
|
|
140
|
+
if (plan.source.kind === 'heroku')
|
|
141
|
+
out.push('', 'The Heroku commands follow the Heroku CLI documentation and have not been run against Heroku.');
|
|
142
|
+
else if (!SOURCE_CHECKED_LIVE[plan.source.kind])
|
|
143
|
+
out.push('', `The ${plan.source.kind} commands follow its source code and have not been run against a live instance yet.`);
|
|
144
|
+
out.push(...renderVolumePreview(plan, receipt));
|
|
145
|
+
return out.join('\n');
|
|
146
|
+
}
|
|
147
|
+
const FINISHED_OK = ['completed', 'completed_with_warnings'];
|
|
148
|
+
/** Import each named dump, recording every step in the receipt. */
|
|
149
|
+
export async function importData(plan, dumps, deps) {
|
|
150
|
+
const now = deps.now ?? (() => new Date());
|
|
151
|
+
const { receipt } = deps;
|
|
152
|
+
if (receipt.source.kind !== plan.source.kind || receipt.source.url !== plan.source.url) {
|
|
153
|
+
throw new Error(`The receipt is for a plan of ${receipt.source.kind} at ${receipt.source.url}, not ${plan.source.kind} at ${plan.source.url}. Pass another --receipt.`);
|
|
154
|
+
}
|
|
155
|
+
// Check every dump before uploading any.
|
|
156
|
+
const jobs = [];
|
|
157
|
+
for (const [name, file] of Object.entries(dumps)) {
|
|
158
|
+
const db = plan.databases.find((d) => d.name === name);
|
|
159
|
+
if (!db)
|
|
160
|
+
throw new Error(`--dump names no database in the plan: ${name}.`);
|
|
161
|
+
const created = receipt.items[`database:${db.ref}`];
|
|
162
|
+
if (created?.state !== 'done' || created.id === undefined)
|
|
163
|
+
throw new Error(`${name} has not been created yet: run \`ninedeploy import apply\` first.`);
|
|
164
|
+
if (!db.engine || !IMPORTABLE[db.engine])
|
|
165
|
+
throw new Error(`The panel has no dump import for ${name} (${db.engine ?? 'no managed engine'}).`);
|
|
166
|
+
const { sha256, bytes } = await deps.hashFile(file);
|
|
167
|
+
if (bytes === 0)
|
|
168
|
+
throw new Error(`${file} is empty.`);
|
|
169
|
+
const prior = receipt.items[`data:${db.ref}`];
|
|
170
|
+
if (prior?.state === 'done' && prior.sha256 !== sha256) {
|
|
171
|
+
throw new Error(`${name} already has a dump imported (sha256 ${prior.sha256?.slice(0, 12)}…), and this one differs. import data does not import twice; use \`ninedeploy databases import ${created.id} --file …\` if you mean to.`);
|
|
172
|
+
}
|
|
173
|
+
if ((prior?.state === 'pending' || prior?.state === 'failed') && prior.importId !== undefined && prior.sha256 !== sha256) {
|
|
174
|
+
throw new Error(`${name} has import #${prior.importId} recorded for a different dump. Check it with \`ninedeploy databases imports ${created.id}\`; use \`ninedeploy databases import ${created.id} --file …\` to deliberately import another dump.`);
|
|
175
|
+
}
|
|
176
|
+
jobs.push({ db, databaseId: created.id, file, sha256, bytes });
|
|
177
|
+
}
|
|
178
|
+
for (const job of jobs) {
|
|
179
|
+
const key = `data:${job.db.ref}`;
|
|
180
|
+
const prior = receipt.items[key];
|
|
181
|
+
if (prior?.state === 'done')
|
|
182
|
+
continue;
|
|
183
|
+
const base = { kind: 'data', ref: job.db.ref, name: job.db.name, id: job.databaseId, sha256: job.sha256, bytes: job.bytes };
|
|
184
|
+
const put = (state, notes, importId) => {
|
|
185
|
+
receipt.items[key] = { ...base, state, notes, ...(importId !== undefined ? { importId } : {}), at: now().toISOString() };
|
|
186
|
+
deps.save(receipt);
|
|
187
|
+
};
|
|
188
|
+
// An upload of this same file that stopped part-way continues where the panel stands.
|
|
189
|
+
let resumeImportId = (prior?.state === 'pending' || prior?.state === 'failed') && prior.sha256 === job.sha256 ? prior.importId : undefined;
|
|
190
|
+
put('pending', [], resumeImportId);
|
|
191
|
+
const options = ['redis', 'keydb', 'dragonfly'].includes(job.db.engine) ? { confirmReplace: true } : {};
|
|
192
|
+
let row;
|
|
193
|
+
try {
|
|
194
|
+
const existing = resumeImportId === undefined ? undefined : await deps.client.databases.imports.get(job.databaseId, resumeImportId);
|
|
195
|
+
if (existing && FINISHED_OK.includes(existing.status)) {
|
|
196
|
+
row = existing;
|
|
197
|
+
}
|
|
198
|
+
else if (existing?.status === 'running') {
|
|
199
|
+
row = await deps.client.databases.imports.wait(job.databaseId, existing.id);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
if (existing && ['failed', 'cancelled', 'expired'].includes(existing.status)) {
|
|
203
|
+
resumeImportId = undefined;
|
|
204
|
+
put('pending', []);
|
|
205
|
+
}
|
|
206
|
+
row = await deps.client.databases.importFile(job.databaseId, deps.open(job.file), {
|
|
207
|
+
sizeBytes: job.bytes,
|
|
208
|
+
sha256: job.sha256,
|
|
209
|
+
filename: path.basename(job.file),
|
|
210
|
+
...(resumeImportId !== undefined ? { resumeImportId } : { options }),
|
|
211
|
+
onCreated: (r) => put('pending', [], r.id),
|
|
212
|
+
});
|
|
213
|
+
row = await deps.client.databases.imports.wait(job.databaseId, row.id);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
put('failed', [err instanceof Error ? err.message : String(err)], receipt.items[key]?.importId);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
// The panel's own message can quote the dump, so the receipt keeps the status only.
|
|
221
|
+
if (FINISHED_OK.includes(row.status)) {
|
|
222
|
+
put('done', [`Imported${row.status === 'completed_with_warnings' ? ' with warnings' : ''}: ${job.bytes} bytes, sha256 checked by the panel.`], row.id);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
put('failed', [`Import #${row.id} ended ${row.status}. Details: ninedeploy databases imports ${job.databaseId}`], row.id);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return receipt;
|
|
229
|
+
}
|
|
230
|
+
async function hashFile(file) {
|
|
231
|
+
const st = statSync(file);
|
|
232
|
+
if (!st.isFile())
|
|
233
|
+
throw new Error(`${file} is not a regular file.`);
|
|
234
|
+
const hash = createHash('sha256');
|
|
235
|
+
for await (const chunk of createReadStream(file))
|
|
236
|
+
hash.update(chunk);
|
|
237
|
+
return { sha256: hash.digest('hex'), bytes: st.size };
|
|
238
|
+
}
|
|
239
|
+
export async function importDataAction(planPath, opts, deps = {}) {
|
|
240
|
+
try {
|
|
241
|
+
const plan = validatePlan(JSON.parse(readFileSync(planPath, 'utf8')));
|
|
242
|
+
const receiptPath = opts.receipt ?? `${planPath.replace(/\.json$/i, '')}.receipt.json`;
|
|
243
|
+
if (!existsSync(receiptPath))
|
|
244
|
+
throw new Error(`No receipt at ${receiptPath}: run \`ninedeploy import apply ${planPath} --yes\` first.`);
|
|
245
|
+
const receipt = JSON.parse(readFileSync(receiptPath, 'utf8'));
|
|
246
|
+
const panel = (deps.panel ?? loadConfig().baseUrl).replace(/\/+$/, '');
|
|
247
|
+
if (receipt.panel !== panel)
|
|
248
|
+
throw new Error(`The receipt is for ${receipt.panel}, but the CLI is signed in to ${panel}.`);
|
|
249
|
+
const pairs = (flag, what, specs = []) => {
|
|
250
|
+
const out = {};
|
|
251
|
+
for (const spec of specs) {
|
|
252
|
+
const eq = spec.indexOf('=');
|
|
253
|
+
if (eq <= 0 || eq === spec.length - 1)
|
|
254
|
+
throw new Error(`--${flag} takes <${what}>=<file>, got "${spec}".`);
|
|
255
|
+
out[spec.slice(0, eq)] = spec.slice(eq + 1);
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
};
|
|
259
|
+
const dumps = pairs('dump', 'database', opts.dump);
|
|
260
|
+
const archives = pairs('volume', 'service', opts.volume);
|
|
261
|
+
header(`Data into ${panel}`);
|
|
262
|
+
if (!opts.yes || Object.keys(dumps).length + Object.keys(archives).length === 0) {
|
|
263
|
+
console.log(renderDataPreview(plan, receipt));
|
|
264
|
+
info(`${c.dim('Nothing was imported.')} Pass --dump <database>=<file> or --volume <service>=<file> with --yes to import.`);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const client = deps.client ?? getClient();
|
|
268
|
+
const common = {
|
|
269
|
+
client, receipt, save: (r) => writeReceipt(receiptPath, r), hashFile,
|
|
270
|
+
open: (file) => (offset) => createReadStream(file, { start: offset }),
|
|
271
|
+
};
|
|
272
|
+
await importData(plan, dumps, common);
|
|
273
|
+
await importVolumes(plan, archives, common);
|
|
274
|
+
const items = Object.values(receipt.items).filter((i) => i.kind === 'data' || i.kind === 'volume');
|
|
275
|
+
console.log(renderReceipt({ ...receipt, items: Object.fromEntries(items.map((i) => [`${i.kind}:${i.ref}`, i])) }));
|
|
276
|
+
const failed = items.filter((i) => i.state === 'failed').length;
|
|
277
|
+
if (failed)
|
|
278
|
+
error(`${failed} import(s) failed. Fix the cause and run the same command again. Receipt: ${receiptPath}`);
|
|
279
|
+
else
|
|
280
|
+
success(`Done. Receipt: ${receiptPath}. Next: deploy and check each service, switch DNS, then run import cutover.`);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
error(err instanceof Error ? err.message : String(err));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=importData.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importData.js","sourceRoot":"","sources":["../../src/commands/importData.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAyB,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EAAsB,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAqB,MAAM,oBAAoB,CAAC;AAa3F,2EAA2E;AAC3E,MAAM,UAAU,GAAgC;IAC9C,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK;CACtH,CAAC;AAEF,4CAA4C;AAC5C,MAAM,SAAS,GAA8C;IAC3D,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,aAAa;IACnH,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CACvG,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,EAAgB;IAC1C,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,IAAI,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,YAAY,EAAE,CAAC;QACtC,mGAAmG;QACnG,IAAI,EAAE,CAAC,MAAM,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC;QAC1C,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;QACrE,OAAO;YACL,OAAO,EAAE,gCAAgC,GAAG,kBAAkB,IAAI,gCAAgC,GAAG,IAAI;YACzG,IAAI;YACJ,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,yCAAyC,CAAC,CAAC,CAAC,CAAC,EAAE;SAC1E,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,oCAAoC,CAAC,CAAC;IACxF,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,KAAa,CAAC;IAClB,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC;QAClB,KAAK,UAAU;YACb,4GAA4G;YAC5G,KAAK,GAAG,0FAA0F,CAAC;YACnG,MAAM;QACR,KAAK,OAAO;YACV,KAAK,GAAG,6GAA6G,CAAC;YACtH,KAAK,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;YAC1F,MAAM;QACR,KAAK,SAAS;YACZ,4GAA4G;YAC5G,KAAK,GAAG,+JAA+J,CAAC;YACxK,KAAK,CAAC,IAAI,CAAC,iGAAiG,CAAC,CAAC;YAC9G,MAAM;QACR,KAAK,OAAO;YACV,KAAK,GAAG,kIAAkI,CAAC;YAC3I,MAAM;QACR,SAAS,CAAC;YACR,4EAA4E;YAC5E,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC;YAC9D,KAAK,GAAG,GAAG,GAAG,sFAAsF,CAAC;YACrG,IAAI,GAAG,mBAAmB,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,gJAAgJ,CAAC,CAAC;YAC7J,IAAI,EAAE,CAAC,MAAM,KAAK,WAAW;gBAAE,KAAK,CAAC,IAAI,CAAC,gHAAgH,CAAC,CAAC;QAC9J,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,cAAc,IAAI,IAAI,MAAM,WAAW,KAAK,OAAO,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7F,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,sFAAsF;IACtF,8IAA8I;IAC9I,mFAAmF;IACnF,8EAA8E;IAC9E,wLAAwL;IACxL,+IAA+I;CAChJ,CAAC;AAEF,MAAM,mBAAmB,GAAgC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAE1H,MAAM,UAAU,iBAAiB,CAAC,IAAgB,EAAE,OAA6B;IAC/E,MAAM,GAAG,GAAa,CAAC,QAAQ,CAAC,CAAC;IACjC,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,aAAa,CAAC,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACjF,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAC3B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACzC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,4CAA4C,CAAC,CAAC;YACrE,SAAS;QACX,CAAC;QACD,IAAI,IAAI,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,gBAAgB,IAAI,CAAC,QAAQ,YAAY,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YAC/F,SAAS;QACX,CAAC;QACD,8FAA8F;QAC9F,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACxG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,qGAAqG,CAAC,CAAC,CAAC,4DAA4D,EAAE,CAAC,CAAC;YAC/N,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,sCAAsC,EAAE,CAAC,MAAM,IAAI,aAAa,yBAAyB,CAAC,CAAC;YAClH,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,MAAM,0BAA0B,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;QAC/E,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK;YAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAClD,GAAG,CAAC,IAAI,CAAC,sBAAsB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ;QAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,+FAA+F,CAAC,CAAC;SAC5I,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,qFAAqF,CAAC,CAAC;IAC5K,GAAG,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAChD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAaD,MAAM,WAAW,GAA4C,CAAC,WAAW,EAAE,yBAAyB,CAAC,CAAC;AAEtG,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAgB,EAAE,KAA6B,EAAE,IAAc;IAC9F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QACvF,MAAM,IAAI,KAAK,CAAC,gCAAgC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,2BAA2B,CAAC,CAAC;IAC1K,CAAC;IACD,yCAAyC;IACzC,MAAM,IAAI,GAAiG,EAAE,CAAC;IAC9G,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,GAAG,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QACpD,IAAI,OAAO,EAAE,KAAK,KAAK,MAAM,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mEAAmE,CAAC,CAAC;QACvJ,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,KAAK,EAAE,CAAC,MAAM,IAAI,mBAAmB,IAAI,CAAC,CAAC;QAC7I,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,KAAK,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,YAAY,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QAC9C,IAAI,KAAK,EAAE,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wCAAwC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,kGAAkG,OAAO,CAAC,EAAE,6BAA6B,CAAC,CAAC;QACtO,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,KAAK,EAAE,KAAK,KAAK,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACzH,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,gBAAgB,KAAK,CAAC,QAAQ,gFAAgF,OAAO,CAAC,EAAE,yCAAyC,OAAO,CAAC,EAAE,kDAAkD,CAAC,CAAC;QACxP,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAK,EAAE,KAAK,KAAK,MAAM;YAAE,SAAS;QACtC,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,MAAe,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;QACrI,MAAM,GAAG,GAAG,CAAC,KAAoC,EAAE,KAAe,EAAE,QAAiB,EAAE,EAAE;YACvF,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;YACzH,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC,CAAC;QACF,sFAAsF;QACtF,IAAI,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,KAAK,EAAE,KAAK,KAAK,QAAQ,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3I,GAAG,CAAC,SAAS,EAAE,EAAE,EAAE,cAAc,CAAC,CAAC;QACnC,MAAM,OAAO,GAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAO,CAAC,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChI,IAAI,GAAmB,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;YACpI,IAAI,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtD,GAAG,GAAG,QAAQ,CAAC;YACjB,CAAC;iBAAM,IAAI,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1C,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC9E,CAAC;iBAAM,CAAC;gBACN,IAAI,QAAQ,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC7E,cAAc,GAAG,SAAS,CAAC;oBAC3B,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;gBACrB,CAAC;gBACD,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;oBAChF,SAAS,EAAE,GAAG,CAAC,KAAK;oBACpB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;oBACjC,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;oBACpE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;iBAC3C,CAAC,CAAC;gBACH,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,QAAQ,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;YAChG,SAAS;QACX,CAAC;QACD,oFAAoF;QACpF,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACrC,GAAG,CAAC,MAAM,EAAE,CAAC,WAAW,GAAG,CAAC,MAAM,KAAK,yBAAyB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,KAAK,sCAAsC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACzJ,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,QAAQ,EAAE,CAAC,WAAW,GAAG,CAAC,EAAE,UAAU,GAAG,CAAC,MAAM,2CAA2C,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5H,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,IAAY;IAClC,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,yBAAyB,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAAE,IAAI,CAAC,MAAM,CAAC,KAAe,CAAC,CAAC;IAC/E,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AACxD,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAgB,EAAE,IAAoB,EAAE,IAAI,GAA4C,EAAE;IAC/H,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,eAAe,CAAC;QACvF,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,WAAW,mCAAmC,QAAQ,iBAAiB,CAAC,CAAC;QACxI,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAkB,CAAC;QAC/E,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,UAAU,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACvE,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,CAAC,KAAK,iCAAiC,KAAK,GAAG,CAAC,CAAC;QAC3H,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,IAAY,EAAE,KAAK,GAAa,EAAE,EAAE,EAAE;YACjE,MAAM,GAAG,GAA2B,EAAE,CAAC;YACvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC7B,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,MAAM,GAAG,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,KAAK,IAAI,WAAW,IAAI,kBAAkB,IAAI,IAAI,CAAC,CAAC;gBAC3G,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC9C,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChF,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YAC9C,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,uBAAuB,CAAC,mFAAmF,CAAC,CAAC;YAC3H,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG;YACb,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAgB,EAAE,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,QAAQ;YACnF,IAAI,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;SACtF,CAAC;QACF,MAAM,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QACnG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnH,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;QAChE,IAAI,MAAM;YAAE,KAAK,CAAC,GAAG,MAAM,6EAA6E,WAAW,EAAE,CAAC,CAAC;;YAClH,OAAO,CAAC,kBAAkB,WAAW,6EAA6E,CAAC,CAAC;IAC3H,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ImportPlan, SourceKind } from '../lib/importers/plan.js';
|
|
2
|
+
export declare const IMPORT_SOURCES: readonly SourceKind[];
|
|
3
|
+
type PanelSource = Exclude<SourceKind, 'heroku'>;
|
|
4
|
+
/** Where each source's credential comes from, without the command line. Heroku is read from files and needs none. */
|
|
5
|
+
export declare const CREDENTIAL_ENV: Record<PanelSource, string>;
|
|
6
|
+
export interface ImportPlanDeps {
|
|
7
|
+
/** Heroku: the checkout to read app.json / Procfile from, the branch, and the Heroku app name. */
|
|
8
|
+
heroku?: {
|
|
9
|
+
dir?: string;
|
|
10
|
+
branch?: string;
|
|
11
|
+
app?: string;
|
|
12
|
+
};
|
|
13
|
+
/** Reads a file of the checkout, or undefined when it does not exist (tests replace it). */
|
|
14
|
+
readFile?: (file: string) => string | undefined;
|
|
15
|
+
env?: NodeJS.ProcessEnv;
|
|
16
|
+
ask?: (message: string) => Promise<string>;
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
}
|
|
19
|
+
/** Build the plan (no output). Throws SourceError with a reason a person can act on. */
|
|
20
|
+
export declare function buildImportPlan(kind: string, url: string, deps?: ImportPlanDeps): Promise<ImportPlan>;
|
|
21
|
+
/** The plan as text a person reads: per item, what would be created and what needs them. */
|
|
22
|
+
export declare function renderImportPlan(plan: ImportPlan): string;
|
|
23
|
+
export declare function importPlanAction(kind: string, opts: {
|
|
24
|
+
url: string;
|
|
25
|
+
json?: boolean;
|
|
26
|
+
out?: string;
|
|
27
|
+
dir?: string;
|
|
28
|
+
branch?: string;
|
|
29
|
+
app?: string;
|
|
30
|
+
}, deps?: ImportPlanDeps): Promise<void>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=importPlan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importPlan.d.ts","sourceRoot":"","sources":["../../src/commands/importPlan.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAa,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAKlF,eAAO,MAAM,cAAc,EAAE,SAAS,UAAU,EAAiD,CAAC;AAElG,KAAK,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AAEjD,qHAAqH;AACrH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAItD,CAAC;AAOF,MAAM,WAAW,cAAc;IAC7B,kGAAkG;IAClG,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,wFAAwF;AACxF,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAqC/G;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA2BzD;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAChG,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ninedeploy import plan <coolify|dokploy|caprover> --url <source panel>`
|
|
3
|
+
*
|
|
4
|
+
* A1 step 1 (docs/plans/COMPETITIVE_ROADMAP_2026-11.md): read another
|
|
5
|
+
* platform's API from this machine and print what NineDeploy would create,
|
|
6
|
+
* what it cannot carry, and what needs a person. Nothing is created on
|
|
7
|
+
* NineDeploy and nothing changes on the source. The source credential is read
|
|
8
|
+
* from an environment variable or a hidden prompt, never from the command
|
|
9
|
+
* line, and stays on this machine; the plan holds variable names only.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { planFromCaprover } from '../lib/importers/caprover.js';
|
|
14
|
+
import { planFromHeroku } from '../lib/importers/heroku.js';
|
|
15
|
+
import { planFromCoolify } from '../lib/importers/coolify.js';
|
|
16
|
+
import { planFromDokploy } from '../lib/importers/dokploy.js';
|
|
17
|
+
import { caproverSource, coolifySource, dokploySource, normaliseBase, OtpRequiredError, SourceError } from '../lib/importers/sources.js';
|
|
18
|
+
import { c, error, header, info, success } from '../lib/format.js';
|
|
19
|
+
import { promptHidden } from '../prompts.js';
|
|
20
|
+
export const IMPORT_SOURCES = ['coolify', 'dokploy', 'caprover', 'heroku'];
|
|
21
|
+
/** Where each source's credential comes from, without the command line. Heroku is read from files and needs none. */
|
|
22
|
+
export const CREDENTIAL_ENV = {
|
|
23
|
+
coolify: 'NINEDEPLOY_IMPORT_TOKEN',
|
|
24
|
+
dokploy: 'NINEDEPLOY_IMPORT_TOKEN',
|
|
25
|
+
caprover: 'NINEDEPLOY_IMPORT_PASSWORD',
|
|
26
|
+
};
|
|
27
|
+
const PROMPT = {
|
|
28
|
+
coolify: 'Coolify API token (a "read" token is enough): ',
|
|
29
|
+
dokploy: 'Dokploy API key: ',
|
|
30
|
+
caprover: 'CapRover password: ',
|
|
31
|
+
};
|
|
32
|
+
/** Build the plan (no output). Throws SourceError with a reason a person can act on. */
|
|
33
|
+
export async function buildImportPlan(kind, url, deps = {}) {
|
|
34
|
+
if (!IMPORT_SOURCES.includes(kind))
|
|
35
|
+
throw new SourceError(`Unknown source "${kind}" (expected ${IMPORT_SOURCES.join(', ')}).`);
|
|
36
|
+
const source = kind;
|
|
37
|
+
if (source === 'heroku') {
|
|
38
|
+
// The repository URL stays whole (normaliseBase would cut its path).
|
|
39
|
+
const dir = path.resolve(deps.heroku?.dir ?? '.');
|
|
40
|
+
const read = deps.readFile ?? ((f) => (existsSync(f) ? readFileSync(f, 'utf8') : undefined));
|
|
41
|
+
try {
|
|
42
|
+
return planFromHeroku({ appJson: read(path.join(dir, 'app.json')), procfile: read(path.join(dir, 'Procfile')), herokuYml: read(path.join(dir, 'heroku.yml')) !== undefined }, { repoUrl: url.trim(), branch: deps.heroku?.branch ?? 'main', app: deps.heroku?.app, dirName: path.basename(dir) });
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new SourceError(err instanceof Error ? err.message : String(err));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const env = deps.env ?? process.env;
|
|
49
|
+
const ask = deps.ask ?? promptHidden;
|
|
50
|
+
const base = normaliseBase(url);
|
|
51
|
+
const credential = env[CREDENTIAL_ENV[source]]?.trim() || (await ask(PROMPT[source])).trim();
|
|
52
|
+
if (!credential)
|
|
53
|
+
throw new SourceError(`No credential: set ${CREDENTIAL_ENV[source]} or type it at the prompt.`);
|
|
54
|
+
let get;
|
|
55
|
+
if (source === 'coolify')
|
|
56
|
+
get = coolifySource(base, credential, deps.fetch);
|
|
57
|
+
else if (source === 'dokploy')
|
|
58
|
+
get = dokploySource(base, credential, deps.fetch);
|
|
59
|
+
else {
|
|
60
|
+
const otpEnv = env['NINEDEPLOY_IMPORT_OTP']?.trim();
|
|
61
|
+
try {
|
|
62
|
+
get = await caproverSource(base, credential, otpEnv || undefined, deps.fetch);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (!(err instanceof OtpRequiredError) || otpEnv)
|
|
66
|
+
throw err;
|
|
67
|
+
const otp = (await ask('CapRover two-factor code: ')).trim();
|
|
68
|
+
get = await caproverSource(base, credential, otp, deps.fetch);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (source === 'coolify')
|
|
72
|
+
return planFromCoolify(get, base);
|
|
73
|
+
if (source === 'dokploy')
|
|
74
|
+
return planFromDokploy(get, base);
|
|
75
|
+
return planFromCaprover(get, base);
|
|
76
|
+
}
|
|
77
|
+
/** The plan as text a person reads: per item, what would be created and what needs them. */
|
|
78
|
+
export function renderImportPlan(plan) {
|
|
79
|
+
const lines = [];
|
|
80
|
+
const s = plan.source;
|
|
81
|
+
lines.push(`Source: ${s.kind}${s.version ? ` ${s.version}` : ''} at ${s.url}`);
|
|
82
|
+
lines.push(`Scope: ${s.scope}`);
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push(`Services (${plan.services.length})`);
|
|
85
|
+
for (const svc of plan.services) {
|
|
86
|
+
const what = svc.kind === 'image' ? `image ${svc.create.image ?? '(unknown)'}` : svc.kind === 'git' ? `git ${svc.create.repoUrl ?? '(no repository)'}${svc.create.branch ? `@${svc.create.branch}` : ''}` : 'compose stack';
|
|
87
|
+
lines.push(` • ${svc.name}: ${what}${svc.create.port ? `, port ${svc.create.port}` : ''}`);
|
|
88
|
+
if (svc.domains.length)
|
|
89
|
+
lines.push(` domains: ${svc.domains.map((d) => `${d.https ? 'https' : 'http'}://${d.host}${d.sourceDefault ? " (the source's own host; stays)" : ''}`).join(', ')}`);
|
|
90
|
+
if (svc.env.length)
|
|
91
|
+
lines.push(` env: ${svc.env.length} variable(s): ${svc.env.map((e) => e.key).join(', ')} (names only)`);
|
|
92
|
+
if (svc.volumes.length)
|
|
93
|
+
lines.push(` volumes: ${svc.volumes.map((v) => v.mountPath).join(', ')}`);
|
|
94
|
+
if (svc.create.build?.startCmd)
|
|
95
|
+
lines.push(` start: ${svc.create.build.startCmd}`);
|
|
96
|
+
if (svc.settings?.healthCheck)
|
|
97
|
+
lines.push(` health check: ${svc.settings.healthCheck}`);
|
|
98
|
+
if (svc.attachments?.length)
|
|
99
|
+
lines.push(` attaches: ${svc.attachments.map((a) => `${a.database} as ${a.envAlias}`).join(', ')}`);
|
|
100
|
+
for (const i of svc.issues)
|
|
101
|
+
lines.push(` ${i.level === 'unsupported' ? '✗' : i.level === 'manual' ? '!' : '·'} ${i.option}: ${i.detail}`);
|
|
102
|
+
}
|
|
103
|
+
lines.push('');
|
|
104
|
+
lines.push(`Databases (${plan.databases.length})`);
|
|
105
|
+
for (const db of plan.databases) {
|
|
106
|
+
lines.push(` • ${db.name}: ${db.engine ?? 'no managed equivalent'}${db.image ? ` (${db.image})` : ''}`);
|
|
107
|
+
for (const i of db.issues)
|
|
108
|
+
lines.push(` ${i.level === 'unsupported' ? '✗' : i.level === 'manual' ? '!' : '·'} ${i.option}: ${i.detail}`);
|
|
109
|
+
}
|
|
110
|
+
lines.push('');
|
|
111
|
+
for (const n of plan.notes)
|
|
112
|
+
lines.push(`Note: ${n}`);
|
|
113
|
+
return lines.join('\n');
|
|
114
|
+
}
|
|
115
|
+
export async function importPlanAction(kind, opts, deps = {}) {
|
|
116
|
+
let plan;
|
|
117
|
+
try {
|
|
118
|
+
plan = await buildImportPlan(kind, opts.url, { ...deps, heroku: { dir: opts.dir, branch: opts.branch, app: opts.app } });
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
error(err instanceof Error ? err.message : String(err));
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const json = `${JSON.stringify(plan, null, 2)}\n`;
|
|
126
|
+
if (opts.out) {
|
|
127
|
+
writeFileSync(opts.out, json);
|
|
128
|
+
success(`Plan written to ${opts.out}`);
|
|
129
|
+
}
|
|
130
|
+
if (opts.json) {
|
|
131
|
+
process.stdout.write(json);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
header(`Import plan: ${plan.services.length} service(s), ${plan.databases.length} database(s)`);
|
|
135
|
+
console.log(renderImportPlan(plan));
|
|
136
|
+
info(`${c.dim('Nothing was created or changed.')} Save it with --out plan.json, then preview step 2 with \`ninedeploy import apply plan.json\`.`);
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=importPlan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importPlan.js","sourceRoot":"","sources":["../../src/commands/importPlan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AACzI,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,CAAC,MAAM,cAAc,GAA0B,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAIlG,qHAAqH;AACrH,MAAM,CAAC,MAAM,cAAc,GAAgC;IACzD,OAAO,EAAE,yBAAyB;IAClC,OAAO,EAAE,yBAAyB;IAClC,QAAQ,EAAE,4BAA4B;CACvC,CAAC;AACF,MAAM,MAAM,GAAgC;IAC1C,OAAO,EAAE,gDAAgD;IACzD,OAAO,EAAE,mBAAmB;IAC5B,QAAQ,EAAE,qBAAqB;CAChC,CAAC;AAYF,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,GAAW,EAAE,IAAI,GAAmB,EAAE;IACxF,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAkB,CAAC;QAAE,MAAM,IAAI,WAAW,CAAC,mBAAmB,IAAI,eAAe,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7I,MAAM,MAAM,GAAG,IAAkB,CAAC;IAClC,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,qEAAqE;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QACrG,IAAI,CAAC;YACH,OAAO,cAAc,CACnB,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,KAAK,SAAS,EAAE,EACtJ,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CACnH,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,WAAW,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,YAAY,CAAC;IACrC,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,UAAU,GAAG,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7F,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,WAAW,CAAC,sBAAsB,cAAc,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;IACjH,IAAI,GAAc,CAAC;IACnB,IAAI,MAAM,KAAK,SAAS;QAAE,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;SACvE,IAAI,MAAM,KAAK,SAAS;QAAE,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;SAC5E,CAAC;QACJ,MAAM,MAAM,GAAG,GAAG,CAAC,uBAAuB,CAAC,EAAE,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,IAAI,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAChF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,CAAC,GAAG,YAAY,gBAAgB,CAAC,IAAI,MAAM;gBAAE,MAAM,GAAG,CAAC;YAC5D,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,4BAA4B,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7D,GAAG,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5D,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5D,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,gBAAgB,CAAC,IAAgB;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/E,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAChC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACjD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,iBAAiB,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC;QAC5N,KAAK,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,KAAK,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,kBAAkB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClM,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,GAAG,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACjI,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,kBAAkB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxF,IAAI,GAAG,CAAC,QAAQ,EAAE,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,uBAAuB,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7F,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtI,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACjJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IACnD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,MAAM,IAAI,uBAAuB,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzG,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAChJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,IAAgG,EAChG,IAAI,GAAmB,EAAE;IAEzB,IAAI,IAAgB,CAAC;IACrB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC3H,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAClD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC9B,OAAO,CAAC,mBAAmB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,MAAM,CAAC,gBAAgB,IAAI,CAAC,QAAQ,CAAC,MAAM,gBAAgB,IAAI,CAAC,SAAS,CAAC,MAAM,cAAc,CAAC,CAAC;IAChG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,iCAAiC,CAAC,gGAAgG,CAAC,CAAC;AACpJ,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { NineDeployClient } from '../client.js';
|
|
2
|
+
import type { ImportPlan, PlanService } from '../lib/importers/plan.js';
|
|
3
|
+
import type { ImportReceipt } from './importApply.js';
|
|
4
|
+
/** The panel calls the volume half makes; NineDeployClient satisfies it. */
|
|
5
|
+
export interface VolumeClient {
|
|
6
|
+
volumeImports: {
|
|
7
|
+
importFile: NineDeployClient['volumeImports']['importFile'];
|
|
8
|
+
get: NineDeployClient['volumeImports']['get'];
|
|
9
|
+
wait: NineDeployClient['volumeImports']['wait'];
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The source-host command that archives a service's data volume, or why it
|
|
14
|
+
* cannot be printed. Null when the service has no data volume.
|
|
15
|
+
*/
|
|
16
|
+
export declare function volumeCommand(s: PlanService): {
|
|
17
|
+
command: string;
|
|
18
|
+
file: string;
|
|
19
|
+
notes: string[];
|
|
20
|
+
} | {
|
|
21
|
+
manual: string;
|
|
22
|
+
} | null;
|
|
23
|
+
/** The preview lines for every service with a data volume. */
|
|
24
|
+
export declare function renderVolumePreview(plan: ImportPlan, receipt: ImportReceipt | null): string[];
|
|
25
|
+
export interface VolumeDeps {
|
|
26
|
+
client: VolumeClient;
|
|
27
|
+
receipt: ImportReceipt;
|
|
28
|
+
save: (receipt: ImportReceipt) => void;
|
|
29
|
+
hashFile: (file: string) => Promise<{
|
|
30
|
+
sha256: string;
|
|
31
|
+
bytes: number;
|
|
32
|
+
}>;
|
|
33
|
+
open: (file: string) => (offset: number) => AsyncIterable<Uint8Array>;
|
|
34
|
+
now?: () => Date;
|
|
35
|
+
}
|
|
36
|
+
/** Check every archive, then import each, recording every step in the receipt. */
|
|
37
|
+
export declare function importVolumes(plan: ImportPlan, archives: Record<string, string>, deps: VolumeDeps): Promise<ImportReceipt>;
|
|
38
|
+
//# sourceMappingURL=importVolumes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importVolumes.d.ts","sourceRoot":"","sources":["../../src/commands/importVolumes.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE;QACb,UAAU,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC;QAC5D,GAAG,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;KACjD,CAAC;CACH;AAKD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAyB5H;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,EAAE,CAmB7F;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,CAAC,UAAU,CAAC,CAAC;IACtE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,kFAAkF;AAClF,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAoEhI"}
|