🌳
pt0/deployF/viteAppF/viteAppActionsAI.mts
3import type { ViteAppCfg } from './viteAppCfgAI.mts'
4import type { Ptenv } from '../../sharedF/ptenvF.mts'
20export const devserver = async () => {
21 const appCfg = getAppCfg() as ViteAppCfg
22 const {srcDir, svcPortNo, devCmd = 'pnpm dev'} = appCfg
24 await maybeKillExisting(svcPortNo!)
26 console.log(`Starting vite dev server in ${srcDir} on http://127.0.0.1:${svcPortNo}`)
28 const ac = new AbortController()
29 const cleanup = () => { ac.abort(); process.exit(0) }
30 process.on('SIGINT', cleanup)
31 process.on('SIGTERM', cleanup)
33 const {isSuccess} = await liveSpawn({
34 cmd: `${devCmd} --port ${svcPortNo} --host 127.0.0.1`,
35 cwd: srcDir,
36 signal: ac.signal,
37 })
38 if (!isSuccess) process.exit(1)
40devserver.cliDescript = 'start vite dev server with HMR'
41devserver.cliSchema = killExistingCliSchema
43const startVitePreview = async ({srcDir, svcPortNo}: {srcDir?: string, svcPortNo?: number}) => {
44 const ac = new AbortController()
45 const serverPromise = liveSpawn({
46 cmd: `pnpm preview --port ${svcPortNo} --host 127.0.0.1`,
47 cwd: srcDir,
48 signal: ac.signal,
49 quiet: true,
50 })
52 const maxWaitMs = 15000, intervalMs = 100
53 const start = Date.now()
54 while (Date.now() - start < maxWaitMs) {
55 try {
56 const res = await fetch(`http://127.0.0.1:${svcPortNo}/`, {method: 'HEAD'})
57 if (res.ok) break
58 } catch {}
59 await new Promise(r => setTimeout(r, intervalMs))
60 }
62 return async () => { ac.abort(); await serverPromise.catch(() => {}) }
65const runHealthSuite = async ({ptenv, stopServerRef}: any) => {
66 const {srcDir, svcPortNo, kube_extHostname} = getAppCfg() as ViteAppCfg
68 if (ptenv === ptenvLocal && !stopServerRef.current) {
69 stopServerRef.current = await startVitePreview({srcDir, svcPortNo})
70 }
72 const baseUrl = ptenv === ptenvLocal ? `http://127.0.0.1:${svcPortNo}` : `https://${kube_extHostname}`
73 const testResults = []
75 let res, body
76 try {
77 res = await fetch(`${baseUrl}/`)
78 body = await res.text()
79 } catch (err) {
80 testResults.push({idx: 0, name: 'index returns 200', passed: false, msg: (err as Error).message})
81 return {passed: false, message: 'fetch failed', testResults}
82 }
84 const status200 = res.status === 200
85 testResults.push({idx: 0, name: 'index returns 200', passed: status200, msg: status200 ? 'ok' : `status ${res.status}`})
87 const hasHtml = body.length > 0 && (body.includes('<!DOCTYPE') || body.includes('<html'))
88 testResults.push({idx: 1, name: 'response is HTML', passed: hasHtml, msg: hasHtml ? 'ok' : `body length ${body.length}`})
90 const allPassed = testResults.every(t => t.passed)
91 return {passed: allPassed, message: allPassed ? 'health checks passed' : 'health checks failed', testResults}
94const viteTestConfig = () => ({
95 allSuites: ['health'],
96 localAppCfg: getAppCfg(),
97 suiteConfigs: [{name: 'health', runner: runHealthSuite, deps: []}],
98})
100export const runtests = mkRuntests(viteTestConfig())
101;(runtests as any).cliDescript = 'run health check tests'
103export let doViteAppApplyRef: ((cfg: ViteAppCfg) => Promise<void>) | null = null
104export const setDoViteAppApplyRef = (fn: typeof doViteAppApplyRef) => { doViteAppApplyRef = fn }
106export const testdeploy = async () => {
107 const config = viteTestConfig()
108 const epPath = toPtRelPath(getAppCfg()?.importMetaUrl || '')
110 const totalStart = Date.now()
111 const phases: Record<string, number> = {}
113 const t1 = Date.now()
114 const localOpts = defaultRunTestsOpts(config.allSuites, ptenvLocal)
115 const localResult = await runTestsCore({config, opts: localOpts})
116 phases.runtests_local = elapsedSec(t1)
117 if (!localResult.allPassed) {
118 console.log(chalkRed('local tests failed, aborting'))
119 process.exit(1)
120 }
122 console.log(chalkCyan('apply'))
123 const t2 = Date.now()
124 const appCfg = getAppCfg()
125 actionCtx.enterWith({action: 'apply'})
126 mutateAppCfg(appCfg, {action: 'apply'})
127 assertDefined(doViteAppApplyRef)
128 await doViteAppApplyRef(appCfg)
129 phases.apply = elapsedSec(t2)
131 const t3 = Date.now()
132 const deployedOpts = { ...defaultRunTestsOpts(config.allSuites, ptenvTestprod), isTestdeploy: true }
133 const deployedResult = await runTestsCore({config, opts: deployedOpts})
134 phases.runtests_deployed = elapsedSec(t3)
135 if (!deployedResult.allPassed) {
136 console.log(chalkRed('deployed tests failed'))
137 process.exit(1)
138 }
140 if (localResult.totalTests > 0 || deployedResult.totalTests > 0) {
142 ep: epPath,
143 action: 'testdeploy',
144 ts: new Date().toISOString(),
145 durationSec: elapsedSec(totalStart),
146 success: true,
147 gitSha: getGitShaFull(),
148 phases,
149 ptenv: ptenvTestprod,
150 suites: deployedResult.ranSuites,
151 })
152 }
154 console.log(chalkGreen('testdeploy complete'))
156testdeploy.cliDescript = testdeployDescript
157testdeploy.cliSchema = applyCli