UNPKG

@enspirit/emb

Version:

A replacement for our Makefile-for-monorepos

104 lines (103 loc) 3.96 kB
import { fdir as Fdir } from 'fdir'; import { stat, statfs } from 'node:fs/promises'; import { join } from 'node:path'; import pMap from 'p-map'; import { SentinelFileBasedBuilder } from '../../monorepo/index.js'; import { GitPrerequisitePlugin } from '../../prerequisites/index.js'; import { ResourceFactory, } from '../../monorepo/resources/ResourceFactory.js'; import { BuildImageOperation } from '../operations/index.js'; class DockerImageResourceBuilder extends SentinelFileBasedBuilder { buildContext; dockerContext; constructor(buildContext) { super(buildContext); this.buildContext = buildContext; this.dockerContext = this.config?.context ? this.config.context[0] === '/' ? buildContext.monorepo.join(this.config.context) : buildContext.component.join(this.config.context) : buildContext.monorepo.join(buildContext.component.rootDir); } async getReference() { const configTag = this.config?.tag; let imageNamePart; let tagPart; if (configTag && configTag.includes(':')) { // config.tag contains both image name and tag (e.g., "myimage:v1.0.0") const colonIndex = configTag.lastIndexOf(':'); imageNamePart = configTag.slice(0, colonIndex); tagPart = configTag.slice(colonIndex + 1); } else { // config.tag is just an image name or undefined imageNamePart = configTag || this.component.name; tagPart = this.monorepo.defaults.docker?.tag || 'latest'; } const imageName = [this.monorepo.name, imageNamePart].join('/'); return this.monorepo.expand(`${imageName}:${tagPart}`); } get monorepo() { return this.buildContext.monorepo; } get config() { return this.buildContext.config.params; } get component() { return this.buildContext.component; } async _build(_resource, out) { // Ensure the folder exists await statfs(this.dockerContext); const crawler = new Fdir(); const sources = await crawler .withRelativePaths() .crawl(this.dockerContext) .withPromise(); const buildParams = { context: this.dockerContext, dockerfile: this.config?.dockerfile || 'Dockerfile', src: sources, buildArgs: await this.monorepo.expand({ ...this.monorepo.defaults.docker?.buildArgs, ...this.config?.buildArgs, }), tag: await this.getReference(), labels: await this.monorepo.expand({ ...this.config?.labels, 'emb/project': this.monorepo.name, 'emb/component': this.component.name, 'emb/flavor': this.monorepo.currentFlavor, }), target: this.config?.target, }; return { input: buildParams, operation: new BuildImageOperation(out), }; } async _mustBuild() { const plugin = new GitPrerequisitePlugin(); const sources = await plugin.collect(this.dockerContext); const lastUpdated = await this.lastUpdatedInfo(sources); if (!lastUpdated) { return; } return { mtime: lastUpdated.time.getTime() }; } async lastUpdatedInfo(sources) { const stats = await pMap(sources, async (s) => { const stats = await stat(join(this.dockerContext, s.path)); return { time: stats.mtime, path: s.path, }; }, { concurrency: 30 }); if (stats.length === 0) { return; } return stats.reduce((last, entry) => { return last.time > entry.time ? last : entry; }, stats[0]); } } ResourceFactory.register('docker/image', DockerImageResourceBuilder);