// // Wrapper around `container-compose`. // // container-compose has no `ls`, so projects are discovered from the // com.docker.compose.project labels it stamps on containers, and compose files // are discovered from the workspace. // const { exec } = require("../Utilities/Process"); const SEARCH_PATHS = [ "/opt/homebrew/bin/container-compose", "/usr/local/bin/container-compose" ]; const FILE_NAMES = [ "compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml", "container-compose.yaml", "container-compose.yml" ]; class ComposeCLI { static resolve() { let configured = nova.config.get("apple_container.cli.compose", "string"); if (configured && configured.trim().length > 0) { return [configured.trim(), []]; } for (let path of SEARCH_PATHS) { if (nova.fs.access(path, nova.fs.X_OK)) { return [path, []]; } } return ["/usr/bin/env", ["container-compose"]]; } static path() { let [path, leading] = this.resolve(); return leading.length > 0 ? "container-compose" : path; } // Compose files in the workspace root, in the order compose itself would // prefer them. static workspaceFiles() { if (!nova.workspace.path) { return []; } let found = []; for (let name of FILE_NAMES) { let path = nova.path.join(nova.workspace.path, name); if (nova.fs.access(path, nova.fs.R_OK)) { found.push(path); } } return found; } // The compose file commands act on: the workspace setting when present, // otherwise the first file found in the workspace. static defaultFile() { let configured = nova.workspace.config.get("apple_container.compose.file", "string"); if (configured && configured.trim().length > 0) { return configured.trim(); } return this.workspaceFiles()[0] || null; } // Mirrors container-compose: the compose file's `name:` when set, otherwise // the containing directory with dots replaced, since dots are not valid in // container names. static projectName(file) { if (!file) { return null; } try { let contents = nova.fs.open(file, "r").read(); let match = contents.match(/^name:\s*["']?([^"'\s#]+)["']?\s*$/m); if (match) { return match[1]; } } catch (error) { // Fall through to the directory name. } return nova.path.basename(nova.path.dirname(file)).replace(/\./g, "_"); } static profiles() { let configured = nova.workspace.config.get("apple_container.compose.profiles", "string"); if (!configured) { return []; } return configured .split(/\s+/) .filter((profile) => profile.length > 0) .reduce((args, profile) => args.concat(["--profile", profile]), []); } static upArgs(file, services = []) { let args = ["up", "--file", file]; if (nova.config.get("apple_container.compose.detach", "boolean") !== false) { args.push("--detach"); } return args.concat(this.profiles(), services); } static downArgs(file, services = []) { return ["down", "--file", file].concat(this.profiles(), services); } static buildArgs(file, services = []) { return ["build", "--file", file].concat(this.profiles(), services); } static run(args, options = {}) { let [path, leading] = this.resolve(); return exec(path, leading.concat(args), options); } } module.exports = { ComposeCLI };