@budibase/cli 1.1.32-alpha.4 → 1.1.32
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/package.json +15 -30
- package/src/constants.js +0 -1
- package/src/index.js +0 -1
- package/src/options.js +1 -2
- package/src/structures/Command.js +2 -4
- package/src/utils.js +0 -11
- package/src/backups/index.js +0 -121
- package/src/backups/objectStore.js +0 -63
- package/src/backups/utils.js +0 -88
- package/src/core/db.js +0 -38
- package/src/prebuilds.js +0 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/cli",
|
|
3
|
-
"version": "1.1.32
|
|
3
|
+
"version": "1.1.32",
|
|
4
4
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,44 +9,29 @@
|
|
|
9
9
|
"author": "Budibase",
|
|
10
10
|
"license": "GPL-3.0",
|
|
11
11
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"build": "yarn prebuild && renamer --find .node --replace .fake 'prebuilds/**' && pkg . --out-path build && yarn postbuild",
|
|
14
|
-
"postbuild": "rm -rf prebuilds 2> /dev/null"
|
|
12
|
+
"build": "pkg . --out-path build"
|
|
15
13
|
},
|
|
16
14
|
"pkg": {
|
|
17
15
|
"targets": [
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
],
|
|
22
|
-
"assets": [
|
|
23
|
-
"node_modules/@budibase/backend-core/dist/**/*",
|
|
24
|
-
"prebuilds/**/*"
|
|
16
|
+
"node14-linux",
|
|
17
|
+
"node14-win",
|
|
18
|
+
"node14-macos"
|
|
25
19
|
],
|
|
26
20
|
"outputPath": "build"
|
|
27
21
|
},
|
|
28
22
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"inquirer": "8.0.0",
|
|
37
|
-
"lookpath": "1.1.0",
|
|
38
|
-
"node-fetch": "2",
|
|
39
|
-
"pkg": "5.7.0",
|
|
23
|
+
"axios": "^0.21.1",
|
|
24
|
+
"chalk": "^4.1.0",
|
|
25
|
+
"commander": "^7.1.0",
|
|
26
|
+
"docker-compose": "^0.23.6",
|
|
27
|
+
"inquirer": "^8.0.0",
|
|
28
|
+
"lookpath": "^1.1.0",
|
|
29
|
+
"pkg": "^5.3.0",
|
|
40
30
|
"posthog-node": "1.0.7",
|
|
41
|
-
"
|
|
42
|
-
"pouchdb-replication-stream": "1.2.9",
|
|
43
|
-
"randomstring": "1.1.5",
|
|
44
|
-
"tar": "6.1.11"
|
|
31
|
+
"randomstring": "^1.1.5"
|
|
45
32
|
},
|
|
46
33
|
"devDependencies": {
|
|
47
|
-
"
|
|
48
|
-
"eslint": "^7.20.0",
|
|
49
|
-
"renamer": "^4.0.0"
|
|
34
|
+
"eslint": "^7.20.0"
|
|
50
35
|
},
|
|
51
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "900d5de0723c1b7acf218446507d52755fdf7d83"
|
|
52
37
|
}
|
package/src/constants.js
CHANGED
package/src/index.js
CHANGED
package/src/options.js
CHANGED
|
@@ -39,10 +39,8 @@ class Command {
|
|
|
39
39
|
let executed = false
|
|
40
40
|
for (let opt of thisCmd.opts) {
|
|
41
41
|
const lookup = opt.command.split(" ")[0].replace("--", "")
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
Object.keys(options).length > 1 ? options : options[lookup]
|
|
45
|
-
await opt.func(input)
|
|
42
|
+
if (options[lookup]) {
|
|
43
|
+
await opt.func(options[lookup])
|
|
46
44
|
executed = true
|
|
47
45
|
}
|
|
48
46
|
}
|
package/src/utils.js
CHANGED
|
@@ -2,7 +2,6 @@ const chalk = require("chalk")
|
|
|
2
2
|
const fs = require("fs")
|
|
3
3
|
const axios = require("axios")
|
|
4
4
|
const path = require("path")
|
|
5
|
-
const progress = require("cli-progress")
|
|
6
5
|
|
|
7
6
|
exports.downloadFile = async (url, filePath) => {
|
|
8
7
|
filePath = path.resolve(filePath)
|
|
@@ -57,13 +56,3 @@ exports.parseEnv = env => {
|
|
|
57
56
|
}
|
|
58
57
|
return result
|
|
59
58
|
}
|
|
60
|
-
|
|
61
|
-
exports.progressBar = total => {
|
|
62
|
-
const bar = new progress.SingleBar({}, progress.Presets.shades_classic)
|
|
63
|
-
bar.start(total, 0)
|
|
64
|
-
return bar
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
exports.checkSlashesInUrl = url => {
|
|
68
|
-
return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
|
|
69
|
-
}
|
package/src/backups/index.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
const Command = require("../structures/Command")
|
|
2
|
-
const { CommandWords } = require("../constants")
|
|
3
|
-
const fs = require("fs")
|
|
4
|
-
const { join } = require("path")
|
|
5
|
-
const { getAllDbs } = require("../core/db")
|
|
6
|
-
const tar = require("tar")
|
|
7
|
-
const { progressBar } = require("../utils")
|
|
8
|
-
const {
|
|
9
|
-
TEMP_DIR,
|
|
10
|
-
COUCH_DIR,
|
|
11
|
-
MINIO_DIR,
|
|
12
|
-
getConfig,
|
|
13
|
-
replication,
|
|
14
|
-
getPouches,
|
|
15
|
-
} = require("./utils")
|
|
16
|
-
const { exportObjects, importObjects } = require("./objectStore")
|
|
17
|
-
|
|
18
|
-
async function exportBackup(opts) {
|
|
19
|
-
const envFile = opts.env || undefined
|
|
20
|
-
let filename = opts["export"] || opts
|
|
21
|
-
if (typeof filename !== "string") {
|
|
22
|
-
filename = `backup-${new Date().toISOString()}.tar.gz`
|
|
23
|
-
}
|
|
24
|
-
const config = await getConfig(envFile)
|
|
25
|
-
const dbList = await getAllDbs(config["COUCH_DB_URL"])
|
|
26
|
-
const { Remote, Local } = getPouches(config)
|
|
27
|
-
if (fs.existsSync(TEMP_DIR)) {
|
|
28
|
-
fs.rmSync(TEMP_DIR, { recursive: true })
|
|
29
|
-
}
|
|
30
|
-
const couchDir = join(TEMP_DIR, COUCH_DIR)
|
|
31
|
-
fs.mkdirSync(TEMP_DIR)
|
|
32
|
-
fs.mkdirSync(couchDir)
|
|
33
|
-
console.log("CouchDB Export")
|
|
34
|
-
const bar = progressBar(dbList.length)
|
|
35
|
-
let count = 0
|
|
36
|
-
for (let db of dbList) {
|
|
37
|
-
bar.update(++count)
|
|
38
|
-
const remote = new Remote(db)
|
|
39
|
-
const local = new Local(join(TEMP_DIR, COUCH_DIR, db))
|
|
40
|
-
await replication(remote, local)
|
|
41
|
-
}
|
|
42
|
-
bar.stop()
|
|
43
|
-
console.log("S3 Export")
|
|
44
|
-
await exportObjects()
|
|
45
|
-
tar.create(
|
|
46
|
-
{
|
|
47
|
-
sync: true,
|
|
48
|
-
gzip: true,
|
|
49
|
-
file: filename,
|
|
50
|
-
cwd: join(TEMP_DIR),
|
|
51
|
-
},
|
|
52
|
-
[COUCH_DIR, MINIO_DIR]
|
|
53
|
-
)
|
|
54
|
-
fs.rmSync(TEMP_DIR, { recursive: true })
|
|
55
|
-
console.log(`Generated export file - ${filename}`)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function importBackup(opts) {
|
|
59
|
-
const envFile = opts.env || undefined
|
|
60
|
-
const filename = opts["import"] || opts
|
|
61
|
-
const config = await getConfig(envFile)
|
|
62
|
-
if (!filename || !fs.existsSync(filename)) {
|
|
63
|
-
console.error("Cannot import without specifying a valid file to import")
|
|
64
|
-
process.exit(-1)
|
|
65
|
-
}
|
|
66
|
-
if (fs.existsSync(TEMP_DIR)) {
|
|
67
|
-
fs.rmSync(TEMP_DIR, { recursive: true })
|
|
68
|
-
}
|
|
69
|
-
fs.mkdirSync(TEMP_DIR)
|
|
70
|
-
tar.extract({
|
|
71
|
-
sync: true,
|
|
72
|
-
cwd: join(TEMP_DIR),
|
|
73
|
-
file: filename,
|
|
74
|
-
})
|
|
75
|
-
const { Remote, Local } = getPouches(config)
|
|
76
|
-
const dbList = fs.readdirSync(join(TEMP_DIR, COUCH_DIR))
|
|
77
|
-
console.log("CouchDB Import")
|
|
78
|
-
const bar = progressBar(dbList.length)
|
|
79
|
-
let count = 0
|
|
80
|
-
for (let db of dbList) {
|
|
81
|
-
bar.update(++count)
|
|
82
|
-
const remote = new Remote(db)
|
|
83
|
-
const local = new Local(join(TEMP_DIR, COUCH_DIR, db))
|
|
84
|
-
await replication(local, remote)
|
|
85
|
-
}
|
|
86
|
-
bar.stop()
|
|
87
|
-
console.log("MinIO Import")
|
|
88
|
-
await importObjects()
|
|
89
|
-
console.log("Import complete")
|
|
90
|
-
fs.rmSync(TEMP_DIR, { recursive: true })
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function pickOne(opts) {
|
|
94
|
-
if (opts["import"]) {
|
|
95
|
-
return importBackup(opts)
|
|
96
|
-
} else if (opts["export"]) {
|
|
97
|
-
return exportBackup(opts)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const command = new Command(`${CommandWords.BACKUPS}`)
|
|
102
|
-
.addHelp(
|
|
103
|
-
"Allows building backups of Budibase, as well as importing a backup to a new instance."
|
|
104
|
-
)
|
|
105
|
-
.addSubOption(
|
|
106
|
-
"--export [filename]",
|
|
107
|
-
"Export a backup from an existing Budibase installation.",
|
|
108
|
-
exportBackup
|
|
109
|
-
)
|
|
110
|
-
.addSubOption(
|
|
111
|
-
"--import [filename]",
|
|
112
|
-
"Import a backup to a new Budibase installation.",
|
|
113
|
-
importBackup
|
|
114
|
-
)
|
|
115
|
-
.addSubOption(
|
|
116
|
-
"--env [envFile]",
|
|
117
|
-
"Provide an environment variable file to configure the CLI.",
|
|
118
|
-
pickOne
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
exports.command = command
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
ObjectStoreBuckets,
|
|
3
|
-
ObjectStore,
|
|
4
|
-
retrieve,
|
|
5
|
-
uploadDirectory,
|
|
6
|
-
makeSureBucketExists,
|
|
7
|
-
} = require("@budibase/backend-core/objectStore")
|
|
8
|
-
const fs = require("fs")
|
|
9
|
-
const { join } = require("path")
|
|
10
|
-
const { TEMP_DIR, MINIO_DIR } = require("./utils")
|
|
11
|
-
const { progressBar } = require("../utils")
|
|
12
|
-
|
|
13
|
-
const bucketList = Object.values(ObjectStoreBuckets)
|
|
14
|
-
|
|
15
|
-
exports.exportObjects = async () => {
|
|
16
|
-
const path = join(TEMP_DIR, MINIO_DIR)
|
|
17
|
-
fs.mkdirSync(path)
|
|
18
|
-
let fullList = []
|
|
19
|
-
for (let bucket of bucketList) {
|
|
20
|
-
const client = ObjectStore(bucket)
|
|
21
|
-
try {
|
|
22
|
-
await client.headBucket().promise()
|
|
23
|
-
} catch (err) {
|
|
24
|
-
continue
|
|
25
|
-
}
|
|
26
|
-
const list = await client.listObjectsV2().promise()
|
|
27
|
-
fullList = fullList.concat(list.Contents.map(el => ({ ...el, bucket })))
|
|
28
|
-
}
|
|
29
|
-
const bar = progressBar(fullList.length)
|
|
30
|
-
let count = 0
|
|
31
|
-
for (let object of fullList) {
|
|
32
|
-
const filename = object.Key
|
|
33
|
-
const data = await retrieve(object.bucket, filename)
|
|
34
|
-
const possiblePath = filename.split("/")
|
|
35
|
-
if (possiblePath.length > 1) {
|
|
36
|
-
const dirs = possiblePath.slice(0, possiblePath.length - 1)
|
|
37
|
-
fs.mkdirSync(join(path, object.bucket, ...dirs), { recursive: true })
|
|
38
|
-
}
|
|
39
|
-
fs.writeFileSync(join(path, object.bucket, ...possiblePath), data)
|
|
40
|
-
bar.update(++count)
|
|
41
|
-
}
|
|
42
|
-
bar.stop()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
exports.importObjects = async () => {
|
|
46
|
-
const path = join(TEMP_DIR, MINIO_DIR)
|
|
47
|
-
const buckets = fs.readdirSync(path)
|
|
48
|
-
let total = 0
|
|
49
|
-
buckets.forEach(bucket => {
|
|
50
|
-
const files = fs.readdirSync(join(path, bucket))
|
|
51
|
-
total += files.length
|
|
52
|
-
})
|
|
53
|
-
const bar = progressBar(total)
|
|
54
|
-
let count = 0
|
|
55
|
-
for (let bucket of buckets) {
|
|
56
|
-
const client = ObjectStore(bucket)
|
|
57
|
-
await makeSureBucketExists(client, bucket)
|
|
58
|
-
const files = await uploadDirectory(bucket, join(path, bucket), "/")
|
|
59
|
-
count += files.length
|
|
60
|
-
bar.update(count)
|
|
61
|
-
}
|
|
62
|
-
bar.stop()
|
|
63
|
-
}
|
package/src/backups/utils.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
const dotenv = require("dotenv")
|
|
2
|
-
const fs = require("fs")
|
|
3
|
-
const { string } = require("../questions")
|
|
4
|
-
const { getPouch } = require("../core/db")
|
|
5
|
-
|
|
6
|
-
exports.DEFAULT_COUCH = "http://budibase:budibase@localhost:10000/db/"
|
|
7
|
-
exports.DEFAULT_MINIO = "http://localhost:10000/"
|
|
8
|
-
exports.TEMP_DIR = ".temp"
|
|
9
|
-
exports.COUCH_DIR = "couchdb"
|
|
10
|
-
exports.MINIO_DIR = "minio"
|
|
11
|
-
|
|
12
|
-
const REQUIRED = [
|
|
13
|
-
{ value: "MAIN_PORT", default: "10000" },
|
|
14
|
-
{ value: "COUCH_DB_URL", default: exports.DEFAULT_COUCH },
|
|
15
|
-
{ value: "MINIO_URL", default: exports.DEFAULT_MINIO },
|
|
16
|
-
{ value: "MINIO_ACCESS_KEY" },
|
|
17
|
-
{ value: "MINIO_SECRET_KEY" },
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
exports.checkURLs = config => {
|
|
21
|
-
const mainPort = config["MAIN_PORT"],
|
|
22
|
-
username = config["COUCH_DB_USER"],
|
|
23
|
-
password = config["COUCH_DB_PASSWORD"]
|
|
24
|
-
if (!config["COUCH_DB_URL"] && mainPort && username && password) {
|
|
25
|
-
config[
|
|
26
|
-
"COUCH_DB_URL"
|
|
27
|
-
] = `http://${username}:${password}@localhost:${mainPort}/db/`
|
|
28
|
-
}
|
|
29
|
-
if (!config["MINIO_URL"]) {
|
|
30
|
-
config["MINIO_URL"] = exports.DEFAULT_MINIO
|
|
31
|
-
}
|
|
32
|
-
return config
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
exports.askQuestions = async () => {
|
|
36
|
-
console.log(
|
|
37
|
-
"*** NOTE: use a .env file to load these parameters repeatedly ***"
|
|
38
|
-
)
|
|
39
|
-
let config = {}
|
|
40
|
-
for (let property of REQUIRED) {
|
|
41
|
-
config[property.value] = await string(property.value, property.default)
|
|
42
|
-
}
|
|
43
|
-
return config
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
exports.loadEnvironment = path => {
|
|
47
|
-
if (!fs.existsSync(path)) {
|
|
48
|
-
throw "Unable to file specified .env file"
|
|
49
|
-
}
|
|
50
|
-
const env = fs.readFileSync(path, "utf8")
|
|
51
|
-
const config = exports.checkURLs(dotenv.parse(env))
|
|
52
|
-
for (let required of REQUIRED) {
|
|
53
|
-
if (!config[required.value]) {
|
|
54
|
-
throw `Cannot find "${required.value}" property in .env file`
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return config
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// true is the default value passed by commander
|
|
61
|
-
exports.getConfig = async (envFile = true) => {
|
|
62
|
-
let config
|
|
63
|
-
if (envFile !== true) {
|
|
64
|
-
config = exports.loadEnvironment(envFile)
|
|
65
|
-
} else {
|
|
66
|
-
config = await exports.askQuestions()
|
|
67
|
-
}
|
|
68
|
-
return config
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
exports.replication = (from, to) => {
|
|
72
|
-
return new Promise((resolve, reject) => {
|
|
73
|
-
from.replicate
|
|
74
|
-
.to(to)
|
|
75
|
-
.on("complete", () => {
|
|
76
|
-
resolve()
|
|
77
|
-
})
|
|
78
|
-
.on("error", err => {
|
|
79
|
-
reject(err)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
exports.getPouches = config => {
|
|
85
|
-
const Remote = getPouch(config["COUCH_DB_URL"])
|
|
86
|
-
const Local = getPouch()
|
|
87
|
-
return { Remote, Local }
|
|
88
|
-
}
|
package/src/core/db.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const PouchDB = require("pouchdb")
|
|
2
|
-
const { checkSlashesInUrl } = require("../utils")
|
|
3
|
-
const fetch = require("node-fetch")
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Fully qualified URL including username and password, or nothing for local
|
|
7
|
-
*/
|
|
8
|
-
exports.getPouch = (url = undefined) => {
|
|
9
|
-
let POUCH_DB_DEFAULTS = {}
|
|
10
|
-
if (!url) {
|
|
11
|
-
POUCH_DB_DEFAULTS = {
|
|
12
|
-
prefix: undefined,
|
|
13
|
-
adapter: "leveldb",
|
|
14
|
-
}
|
|
15
|
-
} else {
|
|
16
|
-
POUCH_DB_DEFAULTS = {
|
|
17
|
-
prefix: url,
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const replicationStream = require("pouchdb-replication-stream")
|
|
21
|
-
PouchDB.plugin(replicationStream.plugin)
|
|
22
|
-
PouchDB.adapter("writableStream", replicationStream.adapters.writableStream)
|
|
23
|
-
return PouchDB.defaults(POUCH_DB_DEFAULTS)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
exports.getAllDbs = async url => {
|
|
27
|
-
const response = await fetch(
|
|
28
|
-
checkSlashesInUrl(encodeURI(`${url}/_all_dbs`)),
|
|
29
|
-
{
|
|
30
|
-
method: "GET",
|
|
31
|
-
}
|
|
32
|
-
)
|
|
33
|
-
if (response.status === 200) {
|
|
34
|
-
return await response.json()
|
|
35
|
-
} else {
|
|
36
|
-
throw "Cannot connect to CouchDB instance"
|
|
37
|
-
}
|
|
38
|
-
}
|
package/src/prebuilds.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
const os = require("os")
|
|
2
|
-
const { join } = require("path")
|
|
3
|
-
const fs = require("fs")
|
|
4
|
-
const PREBUILDS = "prebuilds"
|
|
5
|
-
const ARCH = `${os.platform()}-${os.arch()}`
|
|
6
|
-
const PREBUILD_DIR = join(process.execPath, "..", PREBUILDS, ARCH)
|
|
7
|
-
|
|
8
|
-
checkForBinaries()
|
|
9
|
-
|
|
10
|
-
function checkForBinaries() {
|
|
11
|
-
const readDir = join(__filename, "..", "..", PREBUILDS, ARCH)
|
|
12
|
-
if (fs.existsSync(PREBUILD_DIR) || !fs.existsSync(readDir)) {
|
|
13
|
-
return
|
|
14
|
-
}
|
|
15
|
-
const natives = fs.readdirSync(readDir)
|
|
16
|
-
if (fs.existsSync(readDir)) {
|
|
17
|
-
fs.mkdirSync(PREBUILD_DIR, { recursive: true })
|
|
18
|
-
for (let native of natives) {
|
|
19
|
-
const filename = `${native.split(".fake")[0]}.node`
|
|
20
|
-
fs.cpSync(join(readDir, native), join(PREBUILD_DIR, filename))
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function cleanup() {
|
|
26
|
-
if (fs.existsSync(PREBUILD_DIR)) {
|
|
27
|
-
fs.rmSync(PREBUILD_DIR, { recursive: true })
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const events = ["exit", "SIGINT", "SIGUSR1", "SIGUSR2", "uncaughtException"]
|
|
32
|
-
events.forEach(event => {
|
|
33
|
-
process.on(event, cleanup)
|
|
34
|
-
})
|