1import { chalkGreen, chalkRed, chalkCyan } from '../../serverF/libChalkF.mts'2import { runTestsCore, defaultRunTestsOpts } from './runTestsCoreAI.mts'3import { appCfgCtx, 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'8import { elapsedSec } from '../../sharedF/timeF/luxon0NowF.mts'9import type { RunTestsCoreConfig } from './runTestsCoreAI.mts'10import { recordActionResult, getGitShaFull, currentBuilder, printTestdeployEta } from './actionResultsAI.mts'11import { toPtRelPath } from '../../serverF/pathF/absPathToPtPathF.mts'12import { applyAndWaitRollout } from '../../nextF/deployF/mkNextDeploymentF.mts'14import { testdeployCli, testdeployDescript } from '../cliF/cliSchemaSetsF.mts'15import { parseCli } from '../../serverF/cliArgAI.mts'16import { printTestHistory } from './recordTestResultAI.mts'17import { cliValidatedCtx } from '../ctxF/cliValidatedCtxF.mts'18import { assertDefined } from '../../sharedF/assertsF/assertDefinedF.mts'20export type TestdeployConfig = {21 getTestConfig: () => Promise<RunTestsCoreConfig>22}24const getEpPath = () => toPtRelPath(getAppCfg()?.importMetaUrl || '')26export const mkTestdeploy = (cfg: TestdeployConfig) => {27 const testdeploy = async () => {29 if (cli.history) {30 printTestHistory({ ep: getAppCfg()?.importMetaUrl, actions: ['runtests', 'apply'] })31 return32 }34 printTestdeployEta(getAppCfg()?.importMetaUrl || '')36 const { getTestConfig } = cfg37 const config = await getTestConfig()38 const epPath = getEpPath()40 const totalStart = Date.now()41 const phases: Record<string, number> = {}43 const t1 = Date.now()44 const localOpts = defaultRunTestsOpts(config.allSuites, 'testlocal')45 const localResult = await runTestsCore({ config, opts: localOpts })46 phases.runtests_local = elapsedSec(t1)47 if (!localResult.allPassed) {49 process.exit(1)50 }53 const t2 = Date.now()56 assertDefined(appCfg.cluster_name, {appCfg})57 mutateAppCfg(appCfg, {action: 'apply' as const})58 actionCtx.enterWith({action: 'apply'}) // ctx:clear59 klusterCtx.enterWith(appCfg as KlusterCfg) // ctx:clear60 cliValidatedCtx.enterWith({validated: true}) // ctx:clear62 await applyAndWaitRollout(appCfg)63 await syncOtherNextAppResources()65 phases.apply = elapsedSec(t2)67 if (config.preDeployedTests) await config.preDeployedTests()69 const t3 = Date.now()70 const deployedOpts = { ...defaultRunTestsOpts(config.allSuites, 'testprod'), isTestdeploy: true }71 const deployedResult = await runTestsCore({ config, opts: deployedOpts })72 phases.runtests_deployed = elapsedSec(t3)74 if (!deployedResult.allPassed) {76 process.exit(1)77 }79 const testsActuallyRan = localResult.totalTests > 0 || deployedResult.totalTests > 080 if (testsActuallyRan) {81 const builder = currentBuilder()84 ep: epPath,85 action: 'testdeploy',86 ts: new Date().toISOString(),87 durationSec: elapsedSec(totalStart),88 success: true,89 gitSha: getGitShaFull(),90 phases,91 ptenv: 'testprod',92 suites: deployedResult.ranSuites,93 ...(builder !== 'docker' && {builder}),94 ...(imageReused && {imageReused}),95 })96 }98 console.log(chalkGreen('testdeploy complete'))99 }101 testdeploy.cliDescript = testdeployDescript102 testdeploy.cliSchema = testdeployCli103 testdeploy.needsLazyDocker = true // prep paths + lazy builder, actual build during apply phase104 return testdeploy105}