@crombolo/cli 0.9.1 → 0.10.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/bin/crombolo.ts +112 -37
- package/package.json +7 -7
- package/src/operations/commands.ts +203 -333
- package/src/templates.ts +1 -1
- package/templates/basic/bundle/package.json +16 -0
- package/templates/demo/harbourstone/bundle/package.json +16 -0
- package/templates/demo/harbourstone/files/schema/forms/contact.toml +2 -2
- package/templates/starter/bundle/package.json +16 -0
- package/src/operations/sections.ts +0 -423
- /package/templates/basic/bundle/{bundle.json → content/bundle.json} +0 -0
- /package/templates/basic/bundle/{nodes → content/nodes}/36955f6e-d110-469e-b280-72096eccd148.json +0 -0
- /package/templates/demo/harbourstone/bundle/{blobs → content/blobs}/180cc8ef/distillery-from-the-cliff-path.jpg +0 -0
- /package/templates/demo/harbourstone/bundle/{blobs → content/blobs}/68c9cfdf/bottle-brine-and-kiln.jpg +0 -0
- /package/templates/demo/harbourstone/bundle/{blobs → content/blobs}/6be45535/bottle-oloroso-pier.jpg +0 -0
- /package/templates/demo/harbourstone/bundle/{blobs → content/blobs}/6d8d6398/bottle-the-cove.jpg +0 -0
- /package/templates/demo/harbourstone/bundle/{blobs → content/blobs}/7b4713f8/copper-pot-stills.jpg +0 -0
- /package/templates/demo/harbourstone/bundle/{blobs → content/blobs}/c114b7d8/bottle-neap-tide.jpg +0 -0
- /package/templates/demo/harbourstone/bundle/{blobs → content/blobs}/fd337567/casks-in-the-sea-facing-warehouse.jpg +0 -0
- /package/templates/demo/harbourstone/bundle/{bundle.json → content/bundle.json} +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/069cc3c5-c9f5-4792-90c9-e2bfcc4855b6.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/13788fe7-1a45-4b48-8ae4-f4227e154e1e.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/180cc8ef-caf1-4316-b6a7-e3b5aee41ffd.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/22144db2-e214-4006-a0bb-53b5bd121fd6.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/26e87610-e2d6-4b47-bf92-3967f7186907.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/2caa099b-3884-472d-9cea-797a0f0c376f.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/2efbdb5b-7888-4a49-bca5-c0f85b76e472.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/2fe03891-1294-4344-8e24-47e8b212e5b0.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/38b009f7-03c0-4fda-b5c7-843ad6044e9c.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/3d312cc8-2937-40e1-ba14-fd781c9a862d.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/3dc30fc1-973a-4217-b26f-24902210be17.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/40107d59-f3e9-4592-aee8-5b03d4586cbd.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/483a0d3b-a10c-4c9d-8c81-d9607d37e968.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/4b760c0b-b83a-4eee-aa29-c225cfabf19b.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/4e5c7f3f-54cb-4328-ad61-bc4c2736b890.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/62f479ff-0875-453e-99ae-3bf1bec5e3c8.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/68c9cfdf-6b40-47da-a1d2-17256a6cc6db.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/6ad8f0f3-9334-47f1-b805-80c4b4b4fa88.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/6be45535-83e5-4469-acf0-4127016721ae.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/6d8d6398-738f-4f38-ad3c-e5d6e9a971fe.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/7b4713f8-d917-457d-a9d8-7dd01a3617b4.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/88ec1185-53b5-439a-a2ce-3f56603be4ff.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/97469df2-107c-4010-8c14-4de88f735529.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/a2eb439f-71d9-4ea6-89d7-c5bc72618281.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/a562f0a2-cb6e-4ead-a1ce-4d180632419f.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/a5e36a18-a2ef-4eb5-85b4-7e12b21cca06.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/b1876d3a-50a7-4358-84bf-a0899932ffa9.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/b4e38bbd-c1a3-431a-b7a9-5868aa32993a.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/c114b7d8-0138-404b-9d8f-a39d992577a2.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/cb4c569e-c28a-43b6-a4d7-d33bb544e973.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/cd681b4b-9c4e-4624-9c13-450aa6a9c135.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/d60ef12b-0f0a-4db5-9763-21fa52b8d4da.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/d7f13222-d060-4496-b86d-bef21da963c1.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/f36dd4a9-927c-4ca3-a790-c9c859ac7302.json +0 -0
- /package/templates/demo/harbourstone/bundle/{nodes → content/nodes}/fd337567-df67-41ca-ae94-9d84d8d6042f.json +0 -0
- /package/templates/starter/bundle/{bundle.json → content/bundle.json} +0 -0
- /package/templates/starter/bundle/{nodes → content/nodes}/634dd948-cf83-4cad-9b7b-7c938b33da76.json +0 -0
- /package/templates/starter/bundle/{nodes → content/nodes}/64d95359-d91a-4050-b440-3e5124a74999.json +0 -0
- /package/templates/starter/bundle/{nodes → content/nodes}/d2997ebb-36ca-47cb-bbc8-31fefca787c0.json +0 -0
package/bin/crombolo.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* crombolo start boot the site; resets and prints the admin password
|
|
7
7
|
* crombolo start --keep-admin boot without touching the admin password
|
|
8
|
-
* crombolo start --bundle <
|
|
8
|
+
* crombolo start --bundle <pkg> apply a bundle's content, once, then serve
|
|
9
9
|
* crombolo init scaffold a site in the current directory
|
|
10
10
|
* crombolo init --template <id> …from a starter template, content included
|
|
11
11
|
* crombolo status what this environment is, without starting it
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
* crombolo components check compile every component before a visitor does
|
|
21
21
|
* crombolo assets list the stylesheets and scripts beside them
|
|
22
22
|
*
|
|
23
|
-
* crombolo content export write a subtree as a bundle, to move to another environment
|
|
24
|
-
* crombolo content check read-only: what
|
|
25
|
-
* crombolo content import apply a bundle, in one transaction, and record the run
|
|
23
|
+
* crombolo content export write a subtree as a private bundle package, to move to another environment
|
|
24
|
+
* crombolo content check read-only: what installing a bundle's content here would do
|
|
25
|
+
* crombolo content import apply a bundle's content, in one transaction, and record the run
|
|
26
26
|
* crombolo content runs the imports applied here
|
|
27
27
|
* crombolo content revert put an import back, restoring what the site served
|
|
28
28
|
*
|
|
@@ -35,13 +35,14 @@
|
|
|
35
35
|
* crombolo upgrade --plan print the DDL each pending framework migration would run
|
|
36
36
|
* crombolo upgrade back up, apply pending framework migrations, record them
|
|
37
37
|
* crombolo upgrade ledger print migration_history
|
|
38
|
+
* crombolo bundle purge <id> back up, drop a bundle's tables and clear its state
|
|
38
39
|
*
|
|
39
40
|
* crombolo maintenance pause refuse editors' saves cluster-wide; readers unaffected
|
|
40
41
|
* crombolo maintenance resume let editors save again
|
|
41
42
|
*/
|
|
42
43
|
import { existsSync } from 'node:fs'
|
|
43
44
|
import { mkdir, writeFile } from 'node:fs/promises'
|
|
44
|
-
import { join, relative } from 'node:path'
|
|
45
|
+
import { basename, join, relative, resolve } from 'node:path'
|
|
45
46
|
import { AuthStore, resetAdminPassword } from '@crombolo/auth'
|
|
46
47
|
import { ObjectTypes } from '@crombolo/core'
|
|
47
48
|
import {
|
|
@@ -112,6 +113,7 @@ import {
|
|
|
112
113
|
syncDomainsFile,
|
|
113
114
|
syncSchemaDirectory,
|
|
114
115
|
undoDomainsFile,
|
|
116
|
+
VERSION,
|
|
115
117
|
withoutNode,
|
|
116
118
|
writeDictionaryUdt,
|
|
117
119
|
writeDomains,
|
|
@@ -122,7 +124,7 @@ import {
|
|
|
122
124
|
importBundle,
|
|
123
125
|
isResolution,
|
|
124
126
|
isRevertResolution,
|
|
125
|
-
|
|
127
|
+
packageNameFor,
|
|
126
128
|
RESOLUTIONS,
|
|
127
129
|
RESOLUTIONS_FILE,
|
|
128
130
|
REVERT_RESOLUTIONS,
|
|
@@ -288,20 +290,24 @@ async function importStartupBundles(
|
|
|
288
290
|
server: ServerHandle,
|
|
289
291
|
config: CromboloConfig,
|
|
290
292
|
): Promise<string[]> {
|
|
291
|
-
const
|
|
292
|
-
if (
|
|
293
|
+
const targets = flagValues('--bundle')
|
|
294
|
+
if (targets.length === 0) return []
|
|
293
295
|
|
|
294
296
|
const lines: string[] = []
|
|
295
297
|
const db = server.db
|
|
296
298
|
let imported = false
|
|
297
|
-
for (const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
for (const target of targets) {
|
|
300
|
+
let resolved: ops.ResolvedBundle
|
|
301
|
+
try {
|
|
302
|
+
resolved = ops.readBundle(target, config.siteDir)
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if (!(error instanceof ops.CommandError)) throw error
|
|
305
|
+
console.error(` ${error.message}`)
|
|
306
|
+
for (const problem of error.problems) console.error(` ${problem}`)
|
|
307
|
+
console.error(`\n${target} cannot be read. The site has not started.`)
|
|
302
308
|
process.exit(1)
|
|
303
309
|
}
|
|
304
|
-
const set =
|
|
310
|
+
const set = resolved.set
|
|
305
311
|
const standing = await new TransferRunRepository(db).appliedFor(set.manifest.id)
|
|
306
312
|
if (standing) {
|
|
307
313
|
lines.push(` bundle ${set.manifest.id} already imported (run ${standing.id})`)
|
|
@@ -319,7 +325,7 @@ async function importStartupBundles(
|
|
|
319
325
|
if (backup.path) console.log(` backup ${backup.path}`)
|
|
320
326
|
}
|
|
321
327
|
|
|
322
|
-
const prepared = await ops.transferOptions(db, config,
|
|
328
|
+
const prepared = await ops.transferOptions(db, config, resolved, transferInput(target))
|
|
323
329
|
for (const problem of prepared.problems) console.error(` ${problem}`)
|
|
324
330
|
const started = performance.now()
|
|
325
331
|
const result = await importBundle(db, set, {
|
|
@@ -327,19 +333,22 @@ async function importStartupBundles(
|
|
|
327
333
|
publish: flags.has('--publish'),
|
|
328
334
|
nodeId: config.nodeId,
|
|
329
335
|
label: flagValue('--label'),
|
|
336
|
+
package: { name: resolved.package.packageName, version: resolved.package.version },
|
|
330
337
|
})
|
|
331
338
|
await writeReport(db, result.check.findings, { source: 'transfer', scope: set.manifest.id })
|
|
332
339
|
if (!result.runId) {
|
|
333
340
|
printTransferFindings(result.check)
|
|
334
341
|
console.error(
|
|
335
|
-
`\n${
|
|
342
|
+
`\n${target} was refused: ${result.check.outstanding.length} finding(s) outstanding. ` +
|
|
336
343
|
'Nothing was written, and the site has not started.',
|
|
337
344
|
)
|
|
338
345
|
process.exit(1)
|
|
339
346
|
}
|
|
340
347
|
const c = result.check.counts
|
|
341
348
|
const placed = describePlaced(
|
|
342
|
-
|
|
349
|
+
resolved.blobs.size > 0
|
|
350
|
+
? await placeBlobs(await mediaStoreFor(config), resolved.blobs)
|
|
351
|
+
: null,
|
|
343
352
|
)
|
|
344
353
|
lines.push(
|
|
345
354
|
` bundle ${set.manifest.id}: ${c.create} created, ${c.update} updated` +
|
|
@@ -353,7 +362,7 @@ async function importStartupBundles(
|
|
|
353
362
|
kind: 'transfer',
|
|
354
363
|
durationMs: Math.round(performance.now() - started),
|
|
355
364
|
appliedBy: config.nodeId,
|
|
356
|
-
note: `run ${result.runId} (start --bundle)`,
|
|
365
|
+
note: `run ${result.runId}, ${resolved.package.packageName}@${resolved.package.version} (start --bundle)`,
|
|
357
366
|
})
|
|
358
367
|
imported = true
|
|
359
368
|
}
|
|
@@ -992,14 +1001,14 @@ function describePlaced(placed: { placed: number; bytes: number } | null): strin
|
|
|
992
1001
|
}
|
|
993
1002
|
|
|
994
1003
|
/** The flags `check` and `import` share: resolutions, placement, blobs. */
|
|
995
|
-
function transferInput(
|
|
1004
|
+
function transferInput(bundle: string): ops.TransferInput {
|
|
996
1005
|
const allFlag = flagValue('--resolve-all')
|
|
997
1006
|
if (allFlag !== undefined && !isResolution(allFlag)) {
|
|
998
1007
|
console.error(` "${allFlag}" is not one of ${RESOLUTIONS.join(', ')}`)
|
|
999
1008
|
process.exit(1)
|
|
1000
1009
|
}
|
|
1001
1010
|
return {
|
|
1002
|
-
|
|
1011
|
+
bundle,
|
|
1003
1012
|
under: flagValue('--under'),
|
|
1004
1013
|
resolutions: parseResolutions(),
|
|
1005
1014
|
resolveAll: allFlag as Resolution | undefined,
|
|
@@ -1058,7 +1067,9 @@ function describeBundle(bundle: ops.BundleSummary): void {
|
|
|
1058
1067
|
async function contentImport(config: CromboloConfig): Promise<void> {
|
|
1059
1068
|
const dir = positional[1]
|
|
1060
1069
|
if (!dir) {
|
|
1061
|
-
console.error(
|
|
1070
|
+
console.error(
|
|
1071
|
+
'content import needs a bundle: a package directory, or the name of a bundle this site depends on.',
|
|
1072
|
+
)
|
|
1062
1073
|
process.exit(1)
|
|
1063
1074
|
}
|
|
1064
1075
|
const result = await ops.contentImport(config, {
|
|
@@ -1285,7 +1296,9 @@ function printTransferFindings(check: TransferCheck): void {
|
|
|
1285
1296
|
async function contentCheck(config: CromboloConfig): Promise<void> {
|
|
1286
1297
|
const dir = positional[1]
|
|
1287
1298
|
if (!dir) {
|
|
1288
|
-
console.error(
|
|
1299
|
+
console.error(
|
|
1300
|
+
'content check needs a bundle: a package directory, or the name of a bundle this site depends on.',
|
|
1301
|
+
)
|
|
1289
1302
|
process.exit(1)
|
|
1290
1303
|
}
|
|
1291
1304
|
const result = await ops.contentCheck(config, transferInput(dir))
|
|
@@ -1296,7 +1309,7 @@ async function contentCheck(config: CromboloConfig): Promise<void> {
|
|
|
1296
1309
|
printTransferFindings(result.check)
|
|
1297
1310
|
|
|
1298
1311
|
if (flags.has('--save')) {
|
|
1299
|
-
const target = join(
|
|
1312
|
+
const target = join(result.payloadDir, RESOLUTIONS_FILE)
|
|
1300
1313
|
await writeFile(
|
|
1301
1314
|
target,
|
|
1302
1315
|
writeResolutions({
|
|
@@ -1331,6 +1344,11 @@ async function contentExport(config: CromboloConfig): Promise<void> {
|
|
|
1331
1344
|
drafts: flags.has('--drafts'),
|
|
1332
1345
|
withBlobs: flags.has('--with-blobs'),
|
|
1333
1346
|
withBlueprints: flags.has('--with-blueprints'),
|
|
1347
|
+
// An export is a private package; without --name it is named for its directory.
|
|
1348
|
+
package: {
|
|
1349
|
+
name: flagValue('--name') ?? packageNameFor(basename(resolve(out))),
|
|
1350
|
+
version: flagValue('--package-version'),
|
|
1351
|
+
},
|
|
1334
1352
|
})
|
|
1335
1353
|
for (const key of result.blobs.missing)
|
|
1336
1354
|
console.error(` missing ${key} is not in this environment's media store`)
|
|
@@ -1341,6 +1359,7 @@ async function contentExport(config: CromboloConfig): Promise<void> {
|
|
|
1341
1359
|
}
|
|
1342
1360
|
const counts = Object.entries(result.counts)
|
|
1343
1361
|
console.log(` wrote ${relative(cwd, out) || '.'}`)
|
|
1362
|
+
console.log(` package ${result.package.name}@${result.package.version} (private)`)
|
|
1344
1363
|
console.log(
|
|
1345
1364
|
` bundle ${result.bundleId} (${result.snapshot}): ${
|
|
1346
1365
|
counts.length > 0 ? counts.map(([kind, n]) => `${n} ${kind}`).join(', ') : 'nothing selected'
|
|
@@ -1362,7 +1381,7 @@ async function contentExport(config: CromboloConfig): Promise<void> {
|
|
|
1362
1381
|
}
|
|
1363
1382
|
|
|
1364
1383
|
/**
|
|
1365
|
-
* `
|
|
1384
|
+
* `schema-as-code.md` leaves the dictionary in the database, because
|
|
1366
1385
|
* translators work in production where `schema/` is read-only — so this is how
|
|
1367
1386
|
* a lower environment gets the translations.
|
|
1368
1387
|
*/
|
|
@@ -1604,6 +1623,7 @@ async function importSite(): Promise<void> {
|
|
|
1604
1623
|
siteName: flagValue('--name'),
|
|
1605
1624
|
drafts: flags.has('--drafts'),
|
|
1606
1625
|
staging: flagValue('--staging'),
|
|
1626
|
+
coreVersion: VERSION,
|
|
1607
1627
|
})
|
|
1608
1628
|
} catch (error) {
|
|
1609
1629
|
console.error(` ${error instanceof Error ? error.message : error}`)
|
|
@@ -1827,7 +1847,7 @@ async function upgrade(): Promise<void> {
|
|
|
1827
1847
|
try {
|
|
1828
1848
|
for (const row of await readLedger(db))
|
|
1829
1849
|
console.log(
|
|
1830
|
-
` ${row.appliedAt} ${row.kind.padEnd(8)} ${row.name} ${row.durationMs}ms${row.appliedBy ? ` by ${row.appliedBy}` : ''}${row.note ? ` (${row.note})` : ''}`,
|
|
1850
|
+
` ${row.appliedAt} ${row.kind.padEnd(8)} ${row.bundle ? `[${row.bundle}] ` : ''}${row.name} ${row.durationMs}ms${row.appliedBy ? ` by ${row.appliedBy}` : ''}${row.note ? ` (${row.note})` : ''}`,
|
|
1831
1851
|
)
|
|
1832
1852
|
} finally {
|
|
1833
1853
|
await db.close()
|
|
@@ -1837,6 +1857,11 @@ async function upgrade(): Promise<void> {
|
|
|
1837
1857
|
case 'check': {
|
|
1838
1858
|
if (!flags.has('--fix')) {
|
|
1839
1859
|
const report = await ops.upgradeCheck(config, { set: parseSet() })
|
|
1860
|
+
for (const bundle of report.bundles)
|
|
1861
|
+
if (bundle.pending.length > 0)
|
|
1862
|
+
console.log(
|
|
1863
|
+
` bundle ${bundle.id}: ${bundle.pending.map((step) => `${step.name} (${step.kind})`).join(', ')}`,
|
|
1864
|
+
)
|
|
1840
1865
|
printFindings(report)
|
|
1841
1866
|
if (!report.siteChecked)
|
|
1842
1867
|
console.log(
|
|
@@ -1867,12 +1892,20 @@ async function upgrade(): Promise<void> {
|
|
|
1867
1892
|
if (flags.has('--plan')) {
|
|
1868
1893
|
const plan = await ops.upgradePlan(config)
|
|
1869
1894
|
if (plan.steps.length === 0) console.log('No framework migration is pending.')
|
|
1870
|
-
|
|
1895
|
+
const printStep = (step: ops.PlannedStepResult, indent = '') => {
|
|
1871
1896
|
console.log(
|
|
1872
|
-
`\n== ${step.name} (${step.kind}${step.release ? `, release ${step.release}` : ''})`,
|
|
1897
|
+
`\n${indent}== ${step.name} (${step.kind}${step.release ? `, release ${step.release}` : ''})`,
|
|
1873
1898
|
)
|
|
1874
|
-
if (step.unplannable) console.log(
|
|
1875
|
-
for (const sql of step.statements) console.log(
|
|
1899
|
+
if (step.unplannable) console.log(`${indent} unplannable: ${step.unplannable}`)
|
|
1900
|
+
for (const sql of step.statements) console.log(`${indent} ${sql};`)
|
|
1901
|
+
}
|
|
1902
|
+
for (const step of plan.steps) printStep(step)
|
|
1903
|
+
// After core's, in the order `bundles` lists them, because that is the order they run.
|
|
1904
|
+
for (const bundle of plan.bundles) {
|
|
1905
|
+
console.log(
|
|
1906
|
+
`\nbundle ${bundle.id} (${bundle.name}): ${bundle.steps.length === 0 ? 'nothing pending' : `${bundle.steps.length} step(s)`}`,
|
|
1907
|
+
)
|
|
1908
|
+
for (const step of bundle.steps) printStep(step, ' ')
|
|
1876
1909
|
}
|
|
1877
1910
|
console.log(
|
|
1878
1911
|
plan.site.checked
|
|
@@ -1900,6 +1933,8 @@ async function upgrade(): Promise<void> {
|
|
|
1900
1933
|
if (result.action === 'nothing')
|
|
1901
1934
|
return console.log('Nothing to upgrade: the database is at this version.')
|
|
1902
1935
|
for (const name of result.frameworkApplied) console.log(` applied ${name}`)
|
|
1936
|
+
for (const bundle of result.bundlesApplied)
|
|
1937
|
+
for (const name of bundle.applied) console.log(` applied [${bundle.id}] ${name}`)
|
|
1903
1938
|
console.log(
|
|
1904
1939
|
` schema ${result.schema?.action} ${result.schema?.version ?? ''}+${result.schema?.revision ?? ''}`,
|
|
1905
1940
|
)
|
|
@@ -1910,6 +1945,37 @@ async function upgrade(): Promise<void> {
|
|
|
1910
1945
|
}
|
|
1911
1946
|
}
|
|
1912
1947
|
|
|
1948
|
+
/**
|
|
1949
|
+
* `bundle purge <id>`: drops a bundle's tables and forgets its state. Taking a
|
|
1950
|
+
* bundle out of `bundles` leaves them in place, so re-adding it resumes; this is
|
|
1951
|
+
* the deliberate act that does not.
|
|
1952
|
+
*/
|
|
1953
|
+
async function bundlePurge(): Promise<void> {
|
|
1954
|
+
const config = await siteConfig()
|
|
1955
|
+
const id = positional[1]
|
|
1956
|
+
if (!id) {
|
|
1957
|
+
console.error('bundle purge needs the bundle id.')
|
|
1958
|
+
process.exit(1)
|
|
1959
|
+
}
|
|
1960
|
+
const plan = await ops.bundlePurgePlan(config, id)
|
|
1961
|
+
if (plan.tables.length === 0) console.log(` The bundle "${id}" has no tables in this database.`)
|
|
1962
|
+
else {
|
|
1963
|
+
console.log(` This drops ${plan.tables.length} table(s), and every row in them:`)
|
|
1964
|
+
for (const table of plan.tables) console.log(` ${table}`)
|
|
1965
|
+
}
|
|
1966
|
+
if (!flags.has('--yes')) {
|
|
1967
|
+
const answer = prompt(` Purge the bundle "${id}"? Type its id to confirm:`)
|
|
1968
|
+
if (answer?.trim() !== id) {
|
|
1969
|
+
console.log(' Nothing was dropped.')
|
|
1970
|
+
process.exit(1)
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
const result = await ops.bundlePurge(config, id, { backupTaken: flags.has('--backup-taken') })
|
|
1974
|
+
if (result.backup.path) console.log(` backup ${result.backup.path}`)
|
|
1975
|
+
for (const table of result.dropped) console.log(` dropped ${table}`)
|
|
1976
|
+
console.log(` The bundle "${id}"'s state is cleared; adding it again starts its tables afresh.`)
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1913
1979
|
/**
|
|
1914
1980
|
* `maintenance`: the cluster-wide pause on editing that a restore or a swap
|
|
1915
1981
|
* needs. It is a database flag, so it reaches every node on its next write.
|
|
@@ -1948,7 +2014,7 @@ function help(): void {
|
|
|
1948
2014
|
console.log(`crombolo <command>
|
|
1949
2015
|
|
|
1950
2016
|
start [--keep-admin] boot the site in the current directory
|
|
1951
|
-
[--bundle <
|
|
2017
|
+
[--bundle <pkg>] apply a bundle's content first, once per bundle, then serve;
|
|
1952
2018
|
takes the \`content import\` flags (--publish, --under, …)
|
|
1953
2019
|
init [--force] [--name <site name>] [--postgres] [--template <id>]
|
|
1954
2020
|
scaffold a site here; --template list prints the
|
|
@@ -1977,22 +2043,25 @@ function help(): void {
|
|
|
1977
2043
|
assets new <stylesheet|script> <name>
|
|
1978
2044
|
write an empty one in the place the backoffice edits
|
|
1979
2045
|
content export --root <path|uuid> --out <dir>
|
|
1980
|
-
write a
|
|
1981
|
-
and what the destination must already have
|
|
2046
|
+
write a private bundle package: the subtree, its
|
|
2047
|
+
dependencies and what the destination must already have
|
|
1982
2048
|
[--only] [--drafts] [--with-blobs] [--with-blueprints]
|
|
1983
|
-
|
|
2049
|
+
[--name <npm name>] [--package-version <version>]
|
|
2050
|
+
A bundle is an npm package. <pkg> is its directory, or the name of one this
|
|
2051
|
+
site depends on.
|
|
2052
|
+
content check <pkg> what installing its content here would do, and what it
|
|
1984
2053
|
needs from a person first; writes to the Changes dashboard
|
|
1985
2054
|
[--under <path|uuid>] [--resolve <key>=take-bundle|keep-local|skip]
|
|
1986
2055
|
[--resolve-all <choice>] [--allow-missing-blobs] [--save]
|
|
1987
|
-
content import <
|
|
2056
|
+
content import <pkg> re-check, back up, then apply: documents and elements
|
|
1988
2057
|
arrive as drafts, media is live. One transaction, so a
|
|
1989
2058
|
failure leaves nothing behind
|
|
1990
2059
|
[--publish] [--label <text>] [--backup-taken] + the check flags
|
|
1991
2060
|
[--no-schema] [--no-files] decline a section it carries
|
|
1992
2061
|
[--replace-files] allow replacing files this site has;
|
|
1993
2062
|
refused without it, and the originals are kept for a revert
|
|
1994
|
-
bundle check <
|
|
1995
|
-
bundle install <
|
|
2063
|
+
bundle check <pkg> the same as content check
|
|
2064
|
+
bundle install <pkg> the same as content import; a bundle carrying its own
|
|
1996
2065
|
schema and views installs into a site that has neither
|
|
1997
2066
|
content runs [--limit N] the imports applied here, newest first
|
|
1998
2067
|
content publish <path|uuid> take it live; --descendants for the branch below it,
|
|
@@ -2031,6 +2100,8 @@ function help(): void {
|
|
|
2031
2100
|
upgrade [--force <reason>] re-check, back up, apply framework steps, cut over, ledger
|
|
2032
2101
|
upgrade --plan the DDL pending framework migrations would run
|
|
2033
2102
|
upgrade ledger print migration_history
|
|
2103
|
+
bundle purge <id> [--yes] back up, then drop a bundle's tables and clear its state;
|
|
2104
|
+
refused while crombolo.config.ts still wires it
|
|
2034
2105
|
maintenance pause [--reason <text>]
|
|
2035
2106
|
refuse every editor's save, on every node, until resumed;
|
|
2036
2107
|
readers are unaffected and no node drains
|
|
@@ -2073,11 +2144,15 @@ async function dispatch(): Promise<void> {
|
|
|
2073
2144
|
break
|
|
2074
2145
|
/**
|
|
2075
2146
|
* The artifact's own name. A bundle that carries its own structure is what
|
|
2076
|
-
* the Packages section builds (`docs/
|
|
2147
|
+
* the Packages section builds (`docs/bundles.md`), and `install` reads
|
|
2077
2148
|
* better than `import` for one — but it is the same operation on the same
|
|
2078
2149
|
* artifact, so this is the same code path, not a second one.
|
|
2079
2150
|
*/
|
|
2080
2151
|
case 'bundle':
|
|
2152
|
+
if (positional[0] === 'purge') {
|
|
2153
|
+
await bundlePurge()
|
|
2154
|
+
break
|
|
2155
|
+
}
|
|
2081
2156
|
if (positional[0] === 'install') positional[0] = 'import'
|
|
2082
2157
|
await content()
|
|
2083
2158
|
break
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crombolo/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "The crombolo command line: scaffolding a site, schema, views, domains, content transfer and upgrades.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Crombolo",
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@crombolo/auth": "0.
|
|
38
|
-
"@crombolo/core": "0.
|
|
39
|
-
"@crombolo/data": "0.
|
|
40
|
-
"@crombolo/schema": "0.
|
|
41
|
-
"@crombolo/server": "0.
|
|
42
|
-
"@crombolo/transfer": "0.
|
|
37
|
+
"@crombolo/auth": "0.10.0",
|
|
38
|
+
"@crombolo/core": "0.10.0",
|
|
39
|
+
"@crombolo/data": "0.10.0",
|
|
40
|
+
"@crombolo/schema": "0.10.0",
|
|
41
|
+
"@crombolo/server": "0.10.0",
|
|
42
|
+
"@crombolo/transfer": "0.10.0"
|
|
43
43
|
}
|
|
44
44
|
}
|