🌳
pt0/deployF/dockerF/dockerTarPathsF.mts
7import * as _ from 'lodash-es'
25import { getTarCmd } from './getTarCmdF.mts'
27import path from 'path'
30import { tsAbsPath } from '../../ptDirF.mts'
32export const dockerTarPaths = async ({dockerPathsA}: {dockerPathsA: string[]}) => {
33 const action = getAction()
34 if (isDeployAction(action)) {
35 const uncommittedInTar = getUncommittedInPathSet(dockerPathsA)
36 assertEmpty(uncommittedInTar, {uncommittedInTar})
37 }
38 const wpManReplaceTokH = getDeployWpReplaceTokens() || {}
40 if (isVeryVerbose) {
41 betLog('dockerTarPaths', {wpManReplaceTokH})
42 }
44 const patchExtRe = /(\.\w+)$/
46 // TODO: uncertain if regex escaping is needed for GNU tar --transform patterns
47 // Worked fine without escaping before 8629639d9b, defaulting to false
48 // If deploys fail on Linux (codeserv), try setting this to true
49 const escapePatternForGnuTar = false
50 const maybeEscape = (str: string) => escapePatternForGnuTar
51 ? str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
52 : str
54 const getCmdA = (tarFilePtPath: string) => {
55 assertDefined(tarFilePtPath)
56 return [
58 '-zcvf', `${ptDir}/${tarFilePtPath}`, '-C', ptDir,
59 '--transform', `s#${maybeEscape(prodPatchedInfix)}##`,
60 ..._.chain(wpManReplaceTokH).map((toStr, fromStr) => {
61 return ['--transform', `s#${maybeEscape(fromStr)}#${toStr}#`]
62 }).flatten().value(),
63 ]
64 }
66 const startAt = _.now()
68 const pathsGitSha = await getHeadGitSha({filterPathList: dockerPathsA})
69 const gitSliceSha = calcReqHash([
70 pathsGitSha,
71 ...getCmdA('calcHash'),
72 ..._.sortBy(dockerPathsA),
73 '1',
74 ])
76 const gitignoredPaths = getGitignoredInPathSet(dockerPathsA)
77 if (gitignoredPaths.length) {
78 const gitignoredSet = new Set(gitignoredPaths)
79 dockerPathsA = dockerPathsA.filter(p => !gitignoredSet.has(p))
80 }
82 const {pathsThatDontImportDevA, pathsThatImportDevDecA} = await filterDevOnlyPaths(dockerPathsA as import('../../ptDirF.mts').absFileDirPath[])
83 const patchedFilesH: Record<string, string> = {}
84 const patchPathsA = await allPromCalls(pathsThatImportDevDecA, async ({path, newContents}: {path: string, newContents: string}) => {
85 const newPath = path.replace(patchExtRe, prodPatchedInfix + '$1')
86 patchedFilesH[newPath] = newContents
87 await write1File(tsAbsPath(newPath), newContents)
88 return newPath
89 })
90 dockerPathsA = [...pathsThatDontImportDevA, ...patchPathsA]
92 // Merge caller-provided generated file content (for kaniko prebuild)
93 const {inclContentH} = getAppCfg() || {}
94 if (inclContentH) {
95 for (const [ptPath, content] of Object.entries(inclContentH as Record<string, string>)) {
96 patchedFilesH[ptPath] = content
97 await write1File(tsAbsPath(ptPath), content)
98 if (!dockerPathsA.includes(ptPath)) dockerPathsA.push(ptPath)
99 }
100 }
102 const tarFileFileName = `ptdeploy2slice-${dockerPathsA.length}cnt-${gitSliceSha}.tar.gz`
103 const tarFilePtPath = `tmpdockertars/${tarFileFileName}`
105 const msStr = `${_.now() - startAt}ms`
106 if (await fileExists(tsAbsPath(tarFilePtPath))) {
107 if (isVeryVerbose) {
108 console.log(chalkGreen('cacheHit docker tar!'), msStr, tarFilePtPath)
109 }
110 } else {
111 mkdirParentP(tarFilePtPath)
112 const cmdA = getCmdA(tarFilePtPath)
113 if (isVeryVerbose) {
114 console.log(chalkYellow('writing docker tar..'), msStr, tarFilePtPath)
115 }
117 ...cmdA,
118 ...dockerPathsA,
119 ], {env: {
120 ...process.env,
121 GZIP: '-n',
122 }})
123 }
125 // Build tree-structured comments showing import relationships
126 const {importMetaUrl, deployEp, madgeH, madgeEpPtPathsA} = getAppCfg() || {}
127 const epLabel = deployEp || (importMetaUrl && path.basename(absPathToPtPath(tsAbsPath(getImportMetaUrlPath(importMetaUrl))))) || 'unknown'
129 const commentLinesA = (madgeH && madgeEpPtPathsA)
130 ? formatFullImportTree({madgeH, epA: madgeEpPtPathsA, epLabel, pathsA: dockerPathsA, maxLines: dockerPathsA.length + 5})
131 : _.map(dockerPathsA, (p) => `# ${p}`)
133 const dfCpTarStr = [
134 ..._.map(commentLinesA, line => line.startsWith('#') ? line : `# ${line}`),
135 `ADD ${tarFilePtPath} .`,
136 `#^ ${humanBytes(await fileSizeBytes(tsAbsPath(tarFilePtPath)))}`,
137 ].join("\n") + "\n"
139 // tarSpec for kaniko prebuild - contains everything needed to regenerate the tar
140 const tarSpec = {
141 tarPath: tarFilePtPath,
142 filesA: dockerPathsA,
143 patchedFilesH: _.isEmpty(patchedFilesH) ? undefined : patchedFilesH,
144 wpManReplaceTokH: _.isEmpty(wpManReplaceTokH) ? undefined : wpManReplaceTokH,
145 }
147 return {dfCpTarStr, dockerPathsA, tarSpec}