1import { ptDir } from '../serverF/ptDirF.mts'2import { setEnvConf, type envConfType } from '../serverF/envConfF.mts'3import { dockerApproot } from './constantsF.mts'4import { dockerBuildAndRet } from './dockerF/dockerImageF.mts'5import { klusterCtx, type KlusterCfg } from './k8sF/ctxF/klusterCtxF.mts'6import { fileExists } from '../serverF/libFs/mkdirpF.mts'7import { throPtErr } from '../sharedF/throwErrorF/sharedErrF.mts'8import { replaceStrict } from '../sharedF/moDashF/replaceStrictF.mts'9import { chainPromiseCalls } from '../sharedF/moDashF/promF/moPromisesF.mts'10import { appCfgCtx, type AppCfg } from './k8sF/ctxF/appCfgCtxF.mts'11import { absPathToPtPath } from '../serverF/pathF/absPathToPtPathF.mts'12import * as _ from 'lodash-es'13import path from 'path'14import { getEnvLines } from './dockerF/dockerBaseImgF.mts'15import { mkJobDeployment } from './k8sF/mkJobDeployF.mts'16import { getDefaultEnv } from './getDefaultEnvF.mts'17import { buildEnvConf } from './buildEnvConfF.mts'18import { cliHelp } from './cliF/cliHelpF.mts'19import { resourcesActionsH } from './k8sF/isActionSupportedF.mts'20import { deployActionGensDf } from './dockerF/deployActionGensDfF.mts'21import { decAppCfgResGitSha } from './decAppCfgResGitShaF.mts'22import { imageFromAppCfg } from './dockerF/imageFromDfA.mts'23import { epsToPathsGitCached } from './dockerF/dfFromMadgeF.mts'24import { defaultAppCfg } from './defaultAppCfgF.mts'25import { deriveDbNodePorts } from './deriveDbNodePortsAI.mts'26import { getDeployActions, ptDeployActionsCtx } from './ptDeployActions/doPtDeployActionF.mts'27import { doPtDeployAction } from './ptDeployActions/do1PtDeployActionF.mts'28import { zipsecrets } from './ptDeployActions/zipsecretsActionAI.mts'29import { dockerActions } from './ptDeployActions/dockerActionsAI.mts'30import { exceptions } from './ptDeployActions/exceptionsActionAI.mts'31import { getDockerBaseStr } from './dockerF/getDockerBaseStrF.mts'32import { guardPendingMigrations } from './dbF/guardPendingMigrationsAI.mts'33import { assertDefined } from '../sharedF/assertsF/assertDefinedF.mts'34import { cliFlag } from '../serverF/cliArgAI.mts'35import { getHeadGitSha } from './libF/gitF/gitRepoF.mts'36import { recordApplyTiming, printActionEta, isDeployAction, recordActionResult, currentBuilder } from './testsF/actionResultsAI.mts'37import { applyHistoryGuard } from './testsF/recordTestResultAI.mts'38import { availActionsCtx } from './ctxF/availActionsCtxF.mts'39import { initCliTimer } from './cliF/cliTimerCtxF.mts'40import { getAction } from './ctxF/actionCtxF.mts'41import { sleep } from '../sharedF/sleepF.mts'42import { checkPodsHealthy } from './k8sF/checkPodsHealthyAI.mts'43import { chalkRed, chalkGreen } from '../serverF/libChalkF.mts'44import { elapsedSec } from '../sharedF/timeF/luxon0NowF.mts'45import { toPtRelPath } from '../serverF/pathF/absPathToPtPathF.mts'46import type { absFileDirPath } from '../ptDirF.mts'47import type { DockFile } from './dockerF/dockFileF.mts'48import { ciBasePrefix } from '../sharedF/ciBasePrefixAI.mts'49import { isApplyishAction } from './k8sF/applyishActionF.mts'50export { ciBasePrefix }52export const eptMjsRunner = async (appConfig: AppCfg & Record<string, unknown>) => {53 let {54 extraDockerSetupLine, fromImgName,55 inclPtPathA = [], extraActions,56 } = appConfig as Record<string, any>57 const {importMetaUrl, nameNoCiBase} = appConfig as Record<string, any>60 appConfig = defaultAppCfg({inclServer: false, ...appConfig})62 if (isDeployAction(action!)) deriveDbNodePorts(appConfig)64 appConfig.jobType ||= {kind: 'Deployment'}66 assertDefined(importMetaUrl)68 const {dockreg_host, cluster_name, jobType, aptPkgNames, secretNames, appPath} = appConfig as Record<string, any>69 appCfgCtx.enterWith(appConfig); klusterCtx.enterWith(appConfig as KlusterCfg)70 ptDeployActionsCtx.enterWith({...dockerActions, zipsecrets, exceptions, ...extraActions})71 if (extraActions) {72 availActionsCtx.enterWith({...availActionsCtx.getStore(), ...extraActions})73 }75 const jobName = nameNoCiBase || `${ciBasePrefix}${appConfig.name}`77 const actionsH = {...resourcesActionsH(), ...getDeployActions()} as Record<string, any>78 const actionNames = Object.keys(actionsH)80 if (!actionNames.includes(action!) || wantsHelp) {82 return83 }86 await applyHistoryGuard(importMetaUrl)87 return88 }90 const runFileAbsPath = (appConfig as Record<string, any>).runFilePath92 if (!await fileExists(runFileAbsPath as absFileDirPath)) {94 }96 const epPtPathsA = [absPathToPtPath(runFileAbsPath as absFileDirPath), ...inclPtPathA as string[]]97 const nextConfigPtPath = `${appPath}/next.config.mjs`98 if (await fileExists(`${ptDir}/${nextConfigPtPath}` as absFileDirPath)) {99 epPtPathsA.push(nextConfigPtPath)100 }101 Object.assign(appConfig, {epPtPathsA, usesExplicitEpPtPaths: true})103 const git_sha = await getHeadGitSha()105 Object.assign(appConfig, {git_sha})106 appCfgCtx.enterWith(appConfig)108 const dockerEnv = {...getDefaultEnv(), NODE_ENV: (appConfig.envConf as Record<string, any>)?.nextIsDev ? 'development' : 'production'}110 if (deployActionGensDf({action})) {111 const bundle = !!appConfig.bundle112 const nativeInstallPackages = appConfig.nativeInstallPackages || []113 const afterYarnInstallDockPathsA = bundle ? [] : await epsToPathsGitCached({epPtPathsA})114 const dockFile = await getDockerBaseStr({115 afterYarnInstallDockPathsA,116 appPath, fromImgName,117 extraDockerSetupLine, aptPkgNames,118 ...(bundle && {bundle, entrypointAbsPath: runFileAbsPath as string, bundleName: appConfig.name, nativeInstallPackages}),119 })120 dockFile.pushLine(getEnvLines({dockerEnv}))122 Object.assign(appConfig, {dockerFilesA: dockFile})123 }125 setEnvConf(appConfig.envConf as envConfType)126 if (isDeployAction(action!)) await guardPendingMigrations({appCfg: appConfig as Record<string, any>})128 if (isApplyishAction(action)) {130 printActionEta(importMetaUrl, action)131 const {dockerFilesA} = appConfig132 await chainPromiseCalls(_.castArray(dockerFilesA) as DockFile[], async (dockFile) => {133 await dockerBuildAndRet({dfToBuild: dockFile, dfLabel: nameNoCiBase || 'mjsrunner'})134 })135 }137 let {envConf} = appConfig138 envConf = buildEnvConf({envConf: envConf as Record<string, string>, action: action!, cluster_name: cluster_name as string, name: appConfig.name!, importMetaUrl: importMetaUrl as string})139 Object.assign(appConfig, {envConf})141 const appdir = `${dockerApproot}/${appConfig.appPath}`142 const taskCmd = appConfig.bundle143 ? `date && cd ${dockerApproot} && node bundle.mjs runn`144 : `date && cd ${appdir} && node ${145 path.relative(appdir, replaceStrict(runFileAbsPath as string, ptDir, dockerApproot))146 } runn`148 const {image} = await imageFromAppCfg(appConfig)149 const {getVolMntsForPvc} = appConfig as Record<string, any>150 const resourceTmpl = await mkJobDeployment({151 appConfig, envLocal: {...dockerEnv, ...((appConfig as Record<string, any>).downEnv || {})},152 name: jobName, cluster_name: cluster_name as string, image: image!,153 envConf: envConf as Record<string, string>, dockreg_host, git_sha, taskCmd, jobType,154 secretNames, action,155 getVolMntsForPvc,156 })158 Object.assign(appConfig, {resourceTmpl})160 appConfig = await decAppCfgResGitSha({appConfig})162 appCfgCtx.enterWith(appConfig)163 const startTime = Date.now()164 if (action !== 'testdeploy') await doPtDeployAction()165 if (action === 'apply') {166 recordApplyTiming({ep: importMetaUrl, startTime, gitSha: appConfig.git_sha!})167 }168 if (action === 'testdeploy') {169 const applySec = elapsedSec(startTime)170 console.log('waiting 30s for pod stability...')171 const t2 = Date.now()173 const health = await checkPodsHealthy({name: jobName, cluster_name: cluster_name as string})174 if (!health.healthy) {176 process.exit(1)177 }178 const phases = {apply: applySec, healthCheck: elapsedSec(t2)}179 recordActionResult({ep: toPtRelPath(importMetaUrl), action: 'testdeploy', ts: new Date().toISOString(), durationSec: elapsedSec(startTime), success: true, gitSha: appConfig.git_sha!, phases, ...(currentBuilder() !== 'docker' && {builder: currentBuilder()})})180 console.log(chalkGreen('testdeploy complete — pod healthy'))181 }182}