1import { chalkGreen, chalkRed, chalkCyan } from '../../serverF/libChalkF.mts'2import { runTestsCore, defaultRunTestsOpts, type RunTestsCoreConfig } from './runTestsCoreAI.mts'3import { getAppCfg } from '../k8sF/ctxF/appCfgCtxF.mts'4import { klusterCtx, type KlusterCfg } from '../k8sF/ctxF/klusterCtxF.mts'5import { actionCtx } from '../ctxF/actionCtxF.mts'6import { mutateAppCfg } from '../mutateAppCfgF.mts'7import { elapsedSec } from '../../sharedF/timeF/luxon0NowF.mts'8import { recordActionResult, getGitShaFull, currentBuilder, printTestdeployEta } from './actionResultsAI.mts'9import { toPtRelPath } from '../../serverF/pathF/absPathToPtPathF.mts'10import { applyAndWaitRollout } from '../../nextF/deployF/mkNextDeploymentF.mts'12import { testdeployCli, testdeployDescript } from '../cliF/cliSchemaSetsF.mts'13import { parseCli } from '../../serverF/cliArgAI.mts'14import { printTestHistory } from './recordTestResultAI.mts'15import { cliValidatedCtx } from '../ctxF/cliValidatedCtxF.mts'16import { secretsMapToA, dbSecretsToSecretsMapping } from '../defaultAppCfgF.mts'17import { getAnyHostname } from '../../nextF/deployF/hostnameF.mts'18import { doSetProcessEnv } from '../libF/doSetEnvF.mts'20export type DualDeployConfig = {21 getTestConfig: () => Promise<RunTestsCoreConfig>22 smokeOverlay: object23 prodOverlay: object24 smokeSuites: string[]25 prodVerifySuites: string[]26}28const getEpPath = () => toPtRelPath(getAppCfg()?.importMetaUrl || '')30const applyOverlay = async (overlay: object) => {32 mutateAppCfg(appCfg, overlay, {action: 'apply' as const})33 mutateAppCfg(appCfg, {kube_extHostname: getAnyHostname(appCfg as any)})34 const deployedHn = getAnyHostname(appCfg as any)35 if (deployedHn) mutateAppCfg(appCfg, {deployedBaseUrl: `https://${deployedHn}`})36 const dbSecrets = (appCfg as any).envConf?.dbSecrets || {}37 const dbSecretsMapping = dbSecretsToSecretsMapping(dbSecrets)38 ;(appCfg as any).envConf.secretsMapping = { ...(appCfg as any).envConf.secretsMapping, ...dbSecretsMapping }39 mutateAppCfg(appCfg, {secretNames: secretsMapToA((appCfg as any).envConf.secretsMapping)})40 doSetProcessEnv({envLocal: (appCfg as any).envConf, envConf: (appCfg as any).envConf})41 actionCtx.enterWith({action: 'apply'}) // ctx:clear42 klusterCtx.enterWith(appCfg as KlusterCfg) // ctx:clear43 cliValidatedCtx.enterWith({validated: true}) // ctx:clear44 await applyAndWaitRollout(appCfg)45 await syncOtherNextAppResources()46}48export const dualDeployDescript = 'smoke apply → smoke testprod → prod apply → prod verify'50export const mkDualDeploy = (cfg: DualDeployConfig) => {51 const testdeploy = async () => {53 if (cli.history) {54 printTestHistory({ ep: getAppCfg()?.importMetaUrl, actions: ['runtests', 'apply'] })55 return56 }58 printTestdeployEta(getAppCfg()?.importMetaUrl || '')60 const config = await cfg.getTestConfig()61 const epPath = getEpPath()62 const totalStart = Date.now()63 const phases: Record<string, number> = {}66 const t2 = Date.now()67 await applyOverlay(cfg.smokeOverlay)68 phases.smoke_apply = elapsedSec(t2)70 if (config.preDeployedTests) await config.preDeployedTests()72 const t3 = Date.now()73 const smokeOpts = { ...defaultRunTestsOpts(cfg.smokeSuites, 'testprod'), isTestdeploy: true }74 const smokeResult = await runTestsCore({ config, opts: smokeOpts })75 phases.smoke_testprod = elapsedSec(t3)76 if (!smokeResult.allPassed) {78 process.exit(1)79 }82 const t4 = Date.now()83 await applyOverlay(cfg.prodOverlay)84 phases.prod_apply = elapsedSec(t4)86 const t5 = Date.now()87 const prodVerifyOpts = { ...defaultRunTestsOpts(cfg.prodVerifySuites, 'testprod'), isTestdeploy: true }88 const prodVerifyResult = await runTestsCore({ config, opts: prodVerifyOpts })89 phases.prod_verify = elapsedSec(t5)90 if (!prodVerifyResult.allPassed) {92 process.exit(1)93 }95 const testsActuallyRan = smokeResult.totalTests > 0 || prodVerifyResult.totalTests > 096 if (testsActuallyRan) {97 const builder = currentBuilder()100 ep: epPath,101 action: 'testdeploy',102 ts: new Date().toISOString(),103 durationSec: elapsedSec(totalStart),104 success: true,105 gitSha: getGitShaFull(),106 phases,107 ptenv: 'testprod',108 suites: { ...smokeResult.ranSuites, ...prodVerifyResult.ranSuites },109 ...(builder !== 'docker' && {builder}),110 ...(imageReused && {imageReused}),111 })112 }114 console.log(chalkGreen('testdeploy complete'))115 }117 testdeploy.cliDescript = dualDeployDescript118 testdeploy.cliSchema = testdeployCli119 testdeploy.needsLazyDocker = true120 return testdeploy121}