🌳
pt0/deployF/dockerF/dockImplCapsAI.mts
1import { execSync } from 'child_process'
3type DockBuildCaps = { supportsProgress: boolean }
5const capsMemo: Record<string, DockBuildCaps> = {}
6const buildPrefixMemo: Record<string, string> = {}
8// Resolve the build command prefix. Homebrew's docker CLI ships without the buildx plugin,
9// so `docker build` falls back to the legacy builder which rejects RUN --mount. The buildx
10// binary is still on PATH as `docker-buildx` and works standalone.
11export const getDockBuildPrefix = (dockName: string): string => {
12 if (!buildPrefixMemo[dockName]) {
13 buildPrefixMemo[dockName] = (() => {
14 if (dockName !== 'docker') return `${dockName} build`
15 try {
16 execSync('docker buildx version', {stdio: ['ignore', 'pipe', 'ignore']})
17 return 'docker buildx build'
18 } catch { /* plugin not registered */ } // catch:userapproved
19 try {
20 execSync('which docker-buildx', {stdio: ['ignore', 'pipe', 'ignore']})
21 return 'docker-buildx build'
22 } catch { /* no buildx at all — legacy builder */ } // catch:userapproved
23 return 'docker build'
24 })()
25 }
26 return buildPrefixMemo[dockName]
29// Detect the configured builder's capabilities by parsing `<build prefix> --help`.
30// Help-parse is the most direct signal of what the binary actually accepts, robust across
31// docker-legacy (no --progress), docker-BuildKit/buildx, and podman — no need to name the impl.
32export const getDockBuildCaps = (dockName: string): DockBuildCaps => {
33 if (!capsMemo[dockName]) {
34 let supportsProgress = false
35 try {
36 const help = execSync(`${getDockBuildPrefix(dockName)} --help`, {stdio: ['ignore', 'pipe', 'ignore']}).toString()
37 supportsProgress = help.includes('--progress')
38 } catch { /* binary missing/unrecognized — assume no --progress */ } // catch:userapproved
39 capsMemo[dockName] = {supportsProgress}
40 }
41 return capsMemo[dockName]