2023-03-02 19:21:45 +01:00
|
|
|
import { lookpath } from "lookpath"
|
|
|
|
import fs from "fs"
|
|
|
|
import * as makeFiles from "./makeFiles"
|
|
|
|
import { logErrorToFile, downloadFile, error } from "../utils"
|
|
|
|
import yaml from "yaml"
|
|
|
|
import { DockerCompose } from "./types"
|
2022-09-28 20:11:22 +02:00
|
|
|
|
|
|
|
const ERROR_FILE = "docker-error.log"
|
2023-03-03 11:03:33 +01:00
|
|
|
const COMPOSE_URL =
|
|
|
|
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml"
|
2022-09-28 20:11:22 +02:00
|
|
|
|
2023-03-09 14:52:35 +01:00
|
|
|
function composeFilename() {
|
|
|
|
return COMPOSE_URL.split("/").slice(-1)[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getServiceImage(service: string) {
|
|
|
|
const filename = composeFilename()
|
|
|
|
try {
|
|
|
|
const { services } = getServices(filename)
|
|
|
|
const serviceKey = Object.keys(services).find(name =>
|
|
|
|
name.includes(service)
|
|
|
|
)
|
|
|
|
if (serviceKey) {
|
|
|
|
return services[serviceKey].image
|
|
|
|
} else {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setServiceImage(service: string, image: string) {
|
|
|
|
const filename = composeFilename()
|
|
|
|
if (!fs.existsSync(filename)) {
|
|
|
|
throw new Error(
|
|
|
|
`File ${filename} not found, cannot update ${service} image.`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
const current = getServiceImage(service)!
|
|
|
|
let contents = fs.readFileSync(filename, "utf8")
|
|
|
|
contents = contents.replace(`image: ${current}`, `image: ${image}`)
|
|
|
|
fs.writeFileSync(filename, contents)
|
|
|
|
}
|
|
|
|
|
2023-03-03 11:03:33 +01:00
|
|
|
export async function downloadDockerCompose() {
|
2023-03-09 14:52:35 +01:00
|
|
|
const filename = composeFilename()
|
2023-03-03 11:03:33 +01:00
|
|
|
try {
|
2023-03-09 14:52:35 +01:00
|
|
|
await downloadFile(COMPOSE_URL, `./${filename}`)
|
2023-03-03 11:03:33 +01:00
|
|
|
} catch (err) {
|
|
|
|
console.error(error(`Failed to retrieve compose file - ${err}`))
|
2022-09-28 20:11:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
export async function checkDockerConfigured() {
|
2022-09-28 20:11:22 +02:00
|
|
|
const error =
|
|
|
|
"docker/docker-compose has not been installed, please follow instructions at: https://docs.budibase.com/docs/docker-compose"
|
|
|
|
const docker = await lookpath("docker")
|
|
|
|
const compose = await lookpath("docker-compose")
|
2023-10-06 00:03:34 +02:00
|
|
|
const composeV2 = await lookpath("docker compose")
|
|
|
|
if (!docker || (!compose && !composeV2)) {
|
2022-09-28 20:11:22 +02:00
|
|
|
throw error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
export function checkInitComplete() {
|
2022-09-28 20:11:22 +02:00
|
|
|
if (
|
|
|
|
!fs.existsSync(makeFiles.ENV_PATH) &&
|
|
|
|
!fs.existsSync(makeFiles.COMPOSE_PATH)
|
|
|
|
) {
|
|
|
|
throw "Please run the hosting --init command before any other hosting command."
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
export async function handleError(func: Function) {
|
2022-09-28 20:11:22 +02:00
|
|
|
try {
|
|
|
|
await func()
|
2023-03-02 19:21:45 +01:00
|
|
|
} catch (err: any) {
|
2022-09-28 20:11:22 +02:00
|
|
|
if (err && err.err) {
|
|
|
|
logErrorToFile(ERROR_FILE, err.err)
|
|
|
|
}
|
|
|
|
throw `Failed to start - logs written to file: ${ERROR_FILE}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
export function getServices(path: string) {
|
2023-03-09 14:52:35 +01:00
|
|
|
if (!fs.existsSync(path)) {
|
|
|
|
throw new Error(`No yaml found at path: ${path}`)
|
|
|
|
}
|
2022-09-28 20:11:22 +02:00
|
|
|
const dockerYaml = fs.readFileSync(path, "utf8")
|
|
|
|
const parsedYaml = yaml.parse(dockerYaml)
|
2022-09-29 15:00:16 +02:00
|
|
|
return { yaml: parsedYaml, services: parsedYaml.services }
|
2022-09-28 20:11:22 +02:00
|
|
|
}
|
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
export function getAppService(path: string) {
|
|
|
|
const { yaml, services } = getServices(path),
|
2022-09-28 20:11:22 +02:00
|
|
|
serviceList = Object.keys(services)
|
|
|
|
let service
|
|
|
|
if (services["app-service"]) {
|
|
|
|
service = services["app-service"]
|
|
|
|
} else if (serviceList.length === 1) {
|
|
|
|
service = services[serviceList[0]]
|
|
|
|
}
|
2022-09-29 15:00:16 +02:00
|
|
|
return { yaml, service }
|
2022-09-28 20:11:22 +02:00
|
|
|
}
|
2022-09-29 16:38:54 +02:00
|
|
|
|
2023-03-02 19:21:45 +01:00
|
|
|
export function updateDockerComposeService(
|
|
|
|
updateFn: (service: DockerCompose) => void
|
|
|
|
) {
|
2022-09-29 16:38:54 +02:00
|
|
|
const opts = ["docker-compose.yaml", "docker-compose.yml"]
|
|
|
|
const dockerFilePath = opts.find(name => fs.existsSync(name))
|
|
|
|
if (!dockerFilePath) {
|
|
|
|
console.log(error("Unable to locate docker-compose YAML."))
|
|
|
|
return
|
|
|
|
}
|
2023-03-02 19:21:45 +01:00
|
|
|
const { yaml: parsedYaml, service } = getAppService(dockerFilePath)
|
2022-09-29 16:38:54 +02:00
|
|
|
if (!service) {
|
|
|
|
console.log(
|
|
|
|
error(
|
|
|
|
"Unable to locate service within compose file, is it a valid Budibase configuration?"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
updateFn(service)
|
|
|
|
fs.writeFileSync(dockerFilePath, yaml.stringify(parsedYaml))
|
|
|
|
}
|