@budibase/cli 0.0.1

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.
Files changed (67) hide show
  1. package/.eslintrc +12 -0
  2. package/LICENSE.md +674 -0
  3. package/dist/analytics/Client.js +32 -0
  4. package/dist/analytics/index.js +55 -0
  5. package/dist/backups/index.js +119 -0
  6. package/dist/backups/objectStore.js +82 -0
  7. package/dist/backups/utils.js +110 -0
  8. package/dist/constants.js +9 -0
  9. package/dist/core/db.js +55 -0
  10. package/dist/environment.js +4 -0
  11. package/dist/events.js +13 -0
  12. package/dist/exec.js +49 -0
  13. package/dist/hosting/genUser.js +32 -0
  14. package/dist/hosting/index.js +21 -0
  15. package/dist/hosting/init.js +117 -0
  16. package/dist/hosting/makeFiles.js +144 -0
  17. package/dist/hosting/start.js +63 -0
  18. package/dist/hosting/status.js +30 -0
  19. package/dist/hosting/stop.js +30 -0
  20. package/dist/hosting/types.js +2 -0
  21. package/dist/hosting/update.js +62 -0
  22. package/dist/hosting/utils.js +159 -0
  23. package/dist/hosting/watch.js +47 -0
  24. package/dist/index.js +38 -0
  25. package/dist/options.js +14 -0
  26. package/dist/plugins/index.js +199 -0
  27. package/dist/plugins/skeleton.js +75 -0
  28. package/dist/prebuilds.js +46 -0
  29. package/dist/questions.js +58 -0
  30. package/dist/structures/Command.js +75 -0
  31. package/dist/structures/ConfigManager.js +39 -0
  32. package/dist/tsconfig.build.tsbuildinfo +1 -0
  33. package/dist/utils.js +126 -0
  34. package/package.json +62 -0
  35. package/src/analytics/Client.ts +33 -0
  36. package/src/analytics/index.ts +60 -0
  37. package/src/backups/index.ts +130 -0
  38. package/src/backups/objectStore.ts +69 -0
  39. package/src/backups/utils.ts +99 -0
  40. package/src/constants.ts +4 -0
  41. package/src/core/db.ts +39 -0
  42. package/src/environment.ts +3 -0
  43. package/src/events.ts +11 -0
  44. package/src/exec.ts +27 -0
  45. package/src/hosting/genUser.ts +23 -0
  46. package/src/hosting/index.ts +48 -0
  47. package/src/hosting/init.ts +76 -0
  48. package/src/hosting/makeFiles.ts +140 -0
  49. package/src/hosting/start.ts +30 -0
  50. package/src/hosting/status.ts +13 -0
  51. package/src/hosting/stop.ts +13 -0
  52. package/src/hosting/types.ts +4 -0
  53. package/src/hosting/update.ts +55 -0
  54. package/src/hosting/utils.ts +127 -0
  55. package/src/hosting/watch.ts +37 -0
  56. package/src/index.ts +27 -0
  57. package/src/options.ts +8 -0
  58. package/src/plugins/index.ts +198 -0
  59. package/src/plugins/skeleton.ts +65 -0
  60. package/src/prebuilds.ts +50 -0
  61. package/src/questions.ts +40 -0
  62. package/src/structures/Command.ts +94 -0
  63. package/src/structures/ConfigManager.ts +49 -0
  64. package/src/utils.ts +112 -0
  65. package/start.sh +3 -0
  66. package/tsconfig.build.json +24 -0
  67. package/tsconfig.json +21 -0
@@ -0,0 +1,65 @@
1
+ import fetch from "node-fetch"
2
+ import fs from "fs"
3
+ import os from "os"
4
+ import { join } from "path"
5
+ import { processStringSync } from "@budibase/string-templates"
6
+ const download = require("download")
7
+ const tar = require("tar")
8
+
9
+ const HBS_FILES = ["package.json.hbs", "schema.json.hbs", "README.md.hbs"]
10
+
11
+ async function getSkeletonUrl(type: string) {
12
+ const resp = await fetch(
13
+ "https://api.github.com/repos/budibase/budibase-skeleton/releases/latest"
14
+ )
15
+ if (resp.status >= 300) {
16
+ throw new Error("Failed to retrieve skeleton metadata")
17
+ }
18
+ const json = (await resp.json()) as { assets: any[] }
19
+ for (let asset of json["assets"]) {
20
+ if (asset.name && asset.name.includes(type)) {
21
+ return asset["browser_download_url"]
22
+ }
23
+ }
24
+ throw new Error("No skeleton found in latest release.")
25
+ }
26
+
27
+ export async function getSkeleton(type: string, name: string) {
28
+ const url = await getSkeletonUrl(type)
29
+ const tarballFile = join(os.tmpdir(), "skeleton.tar.gz")
30
+
31
+ // download the full skeleton tarball
32
+ fs.writeFileSync(tarballFile, await download(url))
33
+ fs.mkdirSync(name)
34
+ // extract it and get what we need
35
+ await tar.extract({
36
+ file: tarballFile,
37
+ C: name,
38
+ })
39
+ // clear up
40
+ fs.rmSync(tarballFile)
41
+ }
42
+
43
+ export async function fleshOutSkeleton(
44
+ type: string,
45
+ name: string,
46
+ description: string,
47
+ version: string
48
+ ) {
49
+ for (let file of HBS_FILES) {
50
+ const oldFile = join(name, file),
51
+ newFile = join(name, file.substring(0, file.length - 4))
52
+ const hbsContents = fs.readFileSync(oldFile, "utf8")
53
+ if (!hbsContents) {
54
+ continue
55
+ }
56
+ const output = processStringSync(hbsContents, {
57
+ name,
58
+ description,
59
+ version,
60
+ })
61
+ // write the updated file and remove the HBS file
62
+ fs.writeFileSync(newFile, output)
63
+ fs.rmSync(oldFile)
64
+ }
65
+ }
@@ -0,0 +1,50 @@
1
+ import os from "os"
2
+ import { join } from "path"
3
+ import fs from "fs"
4
+ import { error } from "./utils"
5
+
6
+ const PREBUILDS = "prebuilds"
7
+ const ARCH = `${os.platform()}-${os.arch()}`
8
+ const PREBUILD_DIR = join(process.execPath, "..", PREBUILDS, ARCH)
9
+
10
+ // running as built CLI pkg bundle
11
+ if (!process.argv[0].includes("node")) {
12
+ checkForBinaries()
13
+ }
14
+
15
+ function checkForBinaries() {
16
+ const readDir = join(__filename, "..", "..", PREBUILDS, ARCH)
17
+ if (fs.existsSync(PREBUILD_DIR) || !fs.existsSync(readDir)) {
18
+ return
19
+ }
20
+ const natives = fs.readdirSync(readDir)
21
+ if (fs.existsSync(readDir)) {
22
+ fs.mkdirSync(PREBUILD_DIR, { recursive: true })
23
+ for (let native of natives) {
24
+ const filename = `${native.split(".fake")[0]}.node`
25
+ fs.cpSync(join(readDir, native), join(PREBUILD_DIR, filename))
26
+ }
27
+ }
28
+ }
29
+
30
+ function cleanup(evt?: number) {
31
+ if (evt && !isNaN(evt)) {
32
+ return
33
+ }
34
+ if (evt) {
35
+ console.error(
36
+ error(
37
+ "Failed to run CLI command - please report with the following message:"
38
+ )
39
+ )
40
+ console.error(error(evt))
41
+ }
42
+ if (fs.existsSync(PREBUILD_DIR)) {
43
+ fs.rmSync(PREBUILD_DIR, { recursive: true })
44
+ }
45
+ }
46
+
47
+ const events = ["exit", "SIGINT", "SIGUSR1", "SIGUSR2", "uncaughtException"]
48
+ events.forEach(event => {
49
+ process.on(event, cleanup)
50
+ })
@@ -0,0 +1,40 @@
1
+ const inquirer = require("inquirer")
2
+
3
+ export async function confirmation(question: string) {
4
+ const config = {
5
+ type: "confirm",
6
+ message: question,
7
+ default: true,
8
+ name: "confirmation",
9
+ }
10
+ return (await inquirer.prompt(config)).confirmation
11
+ }
12
+
13
+ export async function string(question: string, defaultString?: string) {
14
+ const config: any = {
15
+ type: "input",
16
+ name: "string",
17
+ message: question,
18
+ }
19
+ if (defaultString) {
20
+ config.default = defaultString
21
+ }
22
+ return (await inquirer.prompt(config)).string
23
+ }
24
+
25
+ export async function number(question: string, defaultNumber?: number) {
26
+ const config: any = {
27
+ type: "input",
28
+ name: "number",
29
+ message: question,
30
+ validate: (value: string) => {
31
+ let valid = !isNaN(parseFloat(value))
32
+ return valid || "Please enter a number"
33
+ },
34
+ filter: Number,
35
+ }
36
+ if (defaultNumber) {
37
+ config.default = defaultNumber
38
+ }
39
+ return (await inquirer.prompt(config)).number
40
+ }
@@ -0,0 +1,94 @@
1
+ import {
2
+ getSubHelpDescription,
3
+ getHelpDescription,
4
+ error,
5
+ capitaliseFirstLetter,
6
+ } from "../utils"
7
+
8
+ type CommandOpt = {
9
+ command: string
10
+ help: string
11
+ func?: Function
12
+ extras: any[]
13
+ }
14
+
15
+ export class Command {
16
+ command: string
17
+ opts: CommandOpt[]
18
+ func?: Function
19
+ help?: string
20
+
21
+ constructor(command: string, func?: Function) {
22
+ // if there are options, need to just get the command name
23
+ this.command = command
24
+ this.opts = []
25
+ this.func = func
26
+ }
27
+
28
+ convertToCommander(lookup: string) {
29
+ const parts = lookup.toLowerCase().split("-")
30
+ // camel case, separate out first
31
+ const first = parts.shift()
32
+ return [first]
33
+ .concat(parts.map(part => capitaliseFirstLetter(part)))
34
+ .join("")
35
+ }
36
+
37
+ addHelp(help: string) {
38
+ this.help = help
39
+ return this
40
+ }
41
+
42
+ addSubOption(
43
+ command: string,
44
+ help: string,
45
+ func?: Function,
46
+ extras: any[] = []
47
+ ) {
48
+ this.opts.push({ command, help, func, extras })
49
+ return this
50
+ }
51
+
52
+ configure(program: any) {
53
+ const thisCmd = this
54
+ let command = program.command(thisCmd.command)
55
+ if (this.help) {
56
+ command = command.description(getHelpDescription(thisCmd.help!))
57
+ }
58
+ for (let opt of thisCmd.opts) {
59
+ command = command.option(opt.command, getSubHelpDescription(opt.help))
60
+ }
61
+ command.helpOption(
62
+ "--help",
63
+ getSubHelpDescription(`Get help with ${this.command} options`)
64
+ )
65
+ command.action(async (options: Record<string, string>) => {
66
+ try {
67
+ let executed = false,
68
+ found = false
69
+ for (let opt of thisCmd.opts) {
70
+ let lookup = opt.command.split(" ")[0].replace("--", "")
71
+ // need to handle how commander converts watch-plugin-dir to watchPluginDir
72
+ lookup = this.convertToCommander(lookup)
73
+ found = !executed && !!options[lookup]
74
+ if (found && opt.func) {
75
+ const input =
76
+ Object.keys(options).length > 1 ? options : options[lookup]
77
+ await opt.func(input)
78
+ executed = true
79
+ }
80
+ }
81
+ if (found && !executed) {
82
+ console.log(
83
+ error(`${Object.keys(options)[0]} is an option, not an operation.`)
84
+ )
85
+ } else if (!executed) {
86
+ console.log(error(`Unknown ${this.command} option.`))
87
+ command.help()
88
+ }
89
+ } catch (err: any) {
90
+ console.log(error(err))
91
+ }
92
+ })
93
+ }
94
+ }
@@ -0,0 +1,49 @@
1
+ const fs = require("fs")
2
+ const path = require("path")
3
+ const os = require("os")
4
+ const { error } = require("../utils")
5
+
6
+ export class ConfigManager {
7
+ path: string
8
+
9
+ constructor() {
10
+ this.path = path.join(os.homedir(), ".budibase.json")
11
+ if (!fs.existsSync(this.path)) {
12
+ fs.writeFileSync(this.path, "{}")
13
+ }
14
+ }
15
+
16
+ get config() {
17
+ try {
18
+ return JSON.parse(fs.readFileSync(this.path, "utf8"))
19
+ } catch (err) {
20
+ console.log(
21
+ error(
22
+ "Error parsing configuration file. Please check your .budibase.json is valid."
23
+ )
24
+ )
25
+ return {}
26
+ }
27
+ }
28
+
29
+ set config(json: any) {
30
+ fs.writeFileSync(this.path, JSON.stringify(json))
31
+ }
32
+
33
+ getValue(key: string) {
34
+ return this.config[key]
35
+ }
36
+
37
+ setValue(key: string, value: any) {
38
+ this.config = {
39
+ ...this.config,
40
+ [key]: value,
41
+ }
42
+ }
43
+
44
+ removeKey(key: string) {
45
+ const updated = { ...this.config }
46
+ delete updated[key]
47
+ this.config = updated
48
+ }
49
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,112 @@
1
+ import chalk from "chalk"
2
+ import fs from "fs"
3
+ import path from "path"
4
+ import { join } from "path"
5
+ import fetch from "node-fetch"
6
+ const progress = require("cli-progress")
7
+
8
+ export function downloadFile(url: string, filePath: string) {
9
+ return new Promise((resolve, reject) => {
10
+ filePath = path.resolve(filePath)
11
+ fetch(url, {
12
+ method: "GET",
13
+ })
14
+ .then(response => {
15
+ const writer = fs.createWriteStream(filePath)
16
+ if (response.body) {
17
+ response.body.pipe(writer)
18
+ response.body.on("end", resolve)
19
+ response.body.on("error", reject)
20
+ } else {
21
+ throw new Error(
22
+ `Unable to retrieve docker-compose file - ${response.status}`
23
+ )
24
+ }
25
+ })
26
+ .catch(err => {
27
+ throw err
28
+ })
29
+ })
30
+ }
31
+
32
+ export async function httpCall(url: string, method: string) {
33
+ const response = await fetch(url, {
34
+ method,
35
+ })
36
+ return response.body
37
+ }
38
+
39
+ export function getHelpDescription(str: string) {
40
+ return chalk.cyan(str)
41
+ }
42
+
43
+ export function getSubHelpDescription(str: string) {
44
+ return chalk.green(str)
45
+ }
46
+
47
+ export function error(err: string | number) {
48
+ process.exitCode = -1
49
+ return chalk.red(`Error - ${err}`)
50
+ }
51
+
52
+ export function success(str: string) {
53
+ return chalk.green(str)
54
+ }
55
+
56
+ export function info(str: string) {
57
+ return chalk.cyan(str)
58
+ }
59
+
60
+ export function logErrorToFile(file: string, error: string) {
61
+ fs.writeFileSync(path.resolve(`./${file}`), `Budibase Error\n${error}`)
62
+ }
63
+
64
+ export function parseEnv(env: string) {
65
+ const lines = env.toString().split("\n")
66
+ let result: Record<string, string> = {}
67
+ for (const line of lines) {
68
+ const match = line.match(/^([^=:#]+?)[=:](.*)/)
69
+ if (match) {
70
+ result[match[1].trim()] = match[2].trim()
71
+ }
72
+ }
73
+ return result
74
+ }
75
+
76
+ export function progressBar(total: number) {
77
+ const bar = new progress.SingleBar({}, progress.Presets.shades_classic)
78
+ bar.start(total, 0)
79
+ return bar
80
+ }
81
+
82
+ export function checkSlashesInUrl(url: string) {
83
+ return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
84
+ }
85
+
86
+ export function moveDirectory(oldPath: string, newPath: string) {
87
+ const files = fs.readdirSync(oldPath)
88
+ // check any file exists already
89
+ for (let file of files) {
90
+ if (fs.existsSync(join(newPath, file))) {
91
+ throw new Error(
92
+ "Unable to remove top level directory - some skeleton files already exist."
93
+ )
94
+ }
95
+ }
96
+ for (let file of files) {
97
+ fs.renameSync(join(oldPath, file), join(newPath, file))
98
+ }
99
+ fs.rmdirSync(oldPath)
100
+ }
101
+
102
+ export function capitaliseFirstLetter(str: string) {
103
+ return str.charAt(0).toUpperCase() + str.slice(1)
104
+ }
105
+
106
+ export function stringifyToDotEnv(json: Record<string, string | number>) {
107
+ let str = ""
108
+ for (let [key, value] of Object.entries(json)) {
109
+ str += `${key}=${value}\n`
110
+ }
111
+ return str
112
+ }
package/start.sh ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ dir="$(dirname -- "$(readlink -f "${BASH_SOURCE}")")"
3
+ ${dir}/node_modules/ts-node/dist/bin.js ${dir}/src/index.ts $@
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "commonjs",
5
+ "lib": ["es2020"],
6
+ "strict": true,
7
+ "noImplicitAny": true,
8
+ "esModuleInterop": true,
9
+ "resolveJsonModule": true,
10
+ "incremental": true,
11
+ "types": [ "node", "jest" ],
12
+ "outDir": "dist",
13
+ "skipLibCheck": true
14
+ },
15
+ "include": [
16
+ "src/**/*"
17
+ ],
18
+ "exclude": [
19
+ "node_modules",
20
+ "dist",
21
+ "**/*.spec.ts",
22
+ "**/*.spec.js"
23
+ ]
24
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "./tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "declaration": true,
6
+ "sourceMap": true,
7
+ "baseUrl": ".",
8
+ "paths": {
9
+ "@budibase/types": ["../types/src"],
10
+ "@budibase/backend-core": ["../backend-core/src"],
11
+ "@budibase/backend-core/*": ["../backend-core/*"]
12
+ }
13
+ },
14
+ "ts-node": {
15
+ "require": ["tsconfig-paths/register"],
16
+ "swc": true
17
+ },
18
+
19
+ "include": ["src/**/*", "package.json"],
20
+ "exclude": ["node_modules", "dist"]
21
+ }