@enspirit/emb
Version:
A replacement for our Makefile-for-monorepos
68 lines (67 loc) • 2.27 kB
JavaScript
import { execa } from 'execa';
export const DefaultGetContainerOptions = {
mustBeRunning: true,
mustBeUnique: true,
};
export class DockerComposeClient {
monorepo;
services;
constructor(monorepo) {
this.monorepo = monorepo;
}
async init() {
await this.loadContainers();
}
async isService(component) {
await this.init();
return this.services?.has(component);
}
async getContainer(serviceName, options = DefaultGetContainerOptions) {
const getOptions = { ...DefaultGetContainerOptions, ...options };
await this.loadContainers();
const service = this.services?.get(serviceName);
if (!service) {
throw new Error(`No compose service found with name ${serviceName}`);
}
const containers = getOptions.mustBeRunning
? service.containers.filter((c) => c.State === 'running')
: service.containers;
if (containers.length === 0) {
throw new Error(`No container found for service ${serviceName}`);
}
if (containers.length > 1 && getOptions.mustBeUnique) {
throw new Error(`Multiple containers found`);
}
return containers[0].ID;
}
async loadContainers(force = false) {
if (this.services && !force) {
return;
}
const { stdout: servicesOutput } = await execa({
cwd: this.monorepo.rootDir,
}) `docker compose config --services`;
const services = servicesOutput
.split('\n')
.map((l) => l.trim())
.reduce((services, name) => {
services.set(name, { name, containers: [] });
return services;
}, new Map());
const { stdout } = await execa({
cwd: this.monorepo.rootDir,
}) `docker compose ps -a --format json`;
if (!stdout.trim()) {
this.services = services;
return;
}
this.services = stdout
.split('\n')
.map((l) => JSON.parse(l))
.reduce((services, entry) => {
const svc = services.get(entry.Service);
svc?.containers.push(entry);
return services;
}, services);
}
}