feat: add JD01 YAML-first deployment support
This commit is contained in:
@@ -45,7 +45,12 @@ export function nodeRuntimeGitMirrorJobName(mirror: NodeRuntimeGitMirrorTargetSp
|
||||
return `${prefix}-${Date.now().toString(36)}`.slice(0, 63);
|
||||
}
|
||||
|
||||
export function nodeRuntimeGitMirrorJobManifest(mirror: NodeRuntimeGitMirrorTargetSpec, action: "sync" | "flush", jobName: string): Record<string, unknown> {
|
||||
export function nodeRuntimeGitMirrorJobManifest(
|
||||
mirror: NodeRuntimeGitMirrorTargetSpec,
|
||||
action: "sync" | "flush",
|
||||
jobName: string,
|
||||
options: { discardStaleGitops?: boolean } = {},
|
||||
): Record<string, unknown> {
|
||||
const volumes: Record<string, unknown>[] = [
|
||||
{ name: "cache", ...(mirror.cacheHostPath === null ? { persistentVolumeClaim: { claimName: mirror.cachePvcName } } : { hostPath: { path: mirror.cacheHostPath, type: "DirectoryOrCreate" } }) },
|
||||
{ name: "script", configMap: { name: mirror.syncConfigMapName, defaultMode: 0o755 } },
|
||||
@@ -88,6 +93,7 @@ export function nodeRuntimeGitMirrorJobManifest(mirror: NodeRuntimeGitMirrorTarg
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
...(mirror.egressProxy.mode === "host-route" ? { hostNetwork: true, dnsPolicy: "ClusterFirstWithHostNet" } : {}),
|
||||
restartPolicy: "Never",
|
||||
volumes,
|
||||
containers: [{
|
||||
@@ -95,7 +101,11 @@ export function nodeRuntimeGitMirrorJobManifest(mirror: NodeRuntimeGitMirrorTarg
|
||||
image: mirror.toolsImage,
|
||||
imagePullPolicy: mirror.toolsImagePullPolicy,
|
||||
command: [action === "sync" ? "/script/sync.sh" : "/script/flush.sh"],
|
||||
env: [...nodeRuntimeGitMirrorProxyEnv(mirror), ...nodeRuntimeGitMirrorGithubTransportEnv(mirror)],
|
||||
env: [
|
||||
...nodeRuntimeGitMirrorProxyEnv(mirror),
|
||||
...nodeRuntimeGitMirrorGithubTransportEnv(mirror),
|
||||
...(options.discardStaleGitops === true ? [{ name: "UNIDESK_GIT_MIRROR_DISCARD_STALE_GITOPS", value: "true" }] : []),
|
||||
],
|
||||
volumeMounts,
|
||||
}],
|
||||
},
|
||||
@@ -107,7 +117,7 @@ export function nodeRuntimeGitMirrorJobManifest(mirror: NodeRuntimeGitMirrorTarg
|
||||
export function nodeRuntimeGitMirrorProxyEnv(mirror: NodeRuntimeGitMirrorTargetSpec): Record<string, unknown>[] {
|
||||
const proxy = mirror.egressProxy;
|
||||
if (proxy.mode === "direct") return [];
|
||||
const proxyUrl = `http://${proxy.serviceName}.${proxy.namespace}.svc.cluster.local:${proxy.port}`;
|
||||
const proxyUrl = proxy.mode === "host-route" ? proxy.proxyUrl : `http://${proxy.serviceName}.${proxy.namespace}.svc.cluster.local:${proxy.port}`;
|
||||
const noProxy = proxy.noProxy.join(",");
|
||||
return [
|
||||
{ name: "HTTP_PROXY", value: proxyUrl },
|
||||
@@ -1101,7 +1111,7 @@ export function renderNodeRuntimeControlPlaneOnNode(spec: HwlabRuntimeLaneSpec,
|
||||
"};",
|
||||
"if (overlay.runtimeStore !== undefined) doc.lanes[overlay.lane].runtimeStore = overlay.runtimeStore;",
|
||||
"if (overlay.codeAgentRuntime !== undefined) doc.lanes[overlay.lane].codeAgentRuntime = overlay.codeAgentRuntime;",
|
||||
"if (overlay.gitMirror !== undefined) doc.lanes[overlay.lane].gitMirror = overlay.gitMirror;",
|
||||
"if (overlay.deployYamlGitMirror !== undefined) doc.lanes[overlay.lane].gitMirror = overlay.deployYamlGitMirror;",
|
||||
"fs.writeFileSync(path, YAML.stringify(doc));",
|
||||
"NODE",
|
||||
"if [ -f scripts/gitops-render.mjs ]; then render_script=scripts/gitops-render.mjs; else echo 'render script missing: scripts/gitops-render.mjs' >&2; exit 43; fi",
|
||||
@@ -1186,7 +1196,7 @@ export function renderNodeRuntimeControlPlaneLocal(spec: HwlabRuntimeLaneSpec, s
|
||||
"};",
|
||||
"if (overlay.runtimeStore !== undefined) doc.lanes[overlay.lane].runtimeStore = overlay.runtimeStore;",
|
||||
"if (overlay.codeAgentRuntime !== undefined) doc.lanes[overlay.lane].codeAgentRuntime = overlay.codeAgentRuntime;",
|
||||
"if (overlay.gitMirror !== undefined) doc.lanes[overlay.lane].gitMirror = overlay.gitMirror;",
|
||||
"if (overlay.deployYamlGitMirror !== undefined) doc.lanes[overlay.lane].gitMirror = overlay.deployYamlGitMirror;",
|
||||
"fs.writeFileSync(path, YAML.stringify(doc));",
|
||||
"NODE",
|
||||
"if [ -f scripts/gitops-render.mjs ]; then render_script=scripts/gitops-render.mjs; else echo 'render script missing: scripts/gitops-render.mjs' >&2; exit 43; fi",
|
||||
@@ -1283,6 +1293,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" runtimeStore: overlay.runtimeStore,",
|
||||
" codeAgentRuntime: overlay.codeAgentRuntime,",
|
||||
" observability: overlay.observability,",
|
||||
" deployYamlGitMirror: overlay.deployYamlGitMirror,",
|
||||
" runtimeImageRewrites: overlay.runtimeImageRewrites,",
|
||||
" dockerProxyHttp: overlay.dockerProxyHttp,",
|
||||
" dockerProxyHttps: overlay.dockerProxyHttps,",
|
||||
@@ -1332,6 +1343,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
"};",
|
||||
"if (overlay.runtimeStore !== undefined) doc.lanes[overlay.lane].runtimeStore = overlay.runtimeStore;",
|
||||
"if (overlay.codeAgentRuntime !== undefined) doc.lanes[overlay.lane].codeAgentRuntime = overlay.codeAgentRuntime;",
|
||||
"if (overlay.deployYamlGitMirror !== undefined) doc.lanes[overlay.lane].gitMirror = overlay.deployYamlGitMirror;",
|
||||
"fs.writeFileSync(file, YAML.stringify(doc));",
|
||||
"console.error(JSON.stringify({ event: 'unidesk-deploy-yaml-overlay', ok: true, lane: overlay.lane, httpProxy: overlay.dockerProxyHttp, noProxyCount: overlay.dockerNoProxyList.length }));",
|
||||
"NODE_UNIDESK_DEPLOY_YAML_OVERLAY`;",
|
||||
@@ -2200,6 +2212,117 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
" if (!changed) throw new Error(`generated git-mirror ConfigMap ${configMapName} was not found in ${gitMirrorFile}`);",
|
||||
" fs.writeFileSync(gitMirrorFile, docs.map((doc) => YAML.stringify(doc).trimEnd()).join('\\n---\\n') + '\\n');",
|
||||
"}",
|
||||
"function patchGitMirrorHostRouteYaml() {",
|
||||
" const mirror = overlay.gitMirror || {};",
|
||||
" const proxy = mirror.egressProxy || {};",
|
||||
" if (proxy.mode !== 'host-route') return;",
|
||||
" if (!YAML) throw new Error('yaml module is required to patch git-mirror host-route proxy');",
|
||||
" const requireString = (pathName, value) => {",
|
||||
" if (typeof value !== 'string' || value.length === 0) throw new Error('overlay.' + pathName + ' is required for git-mirror host-route proxy');",
|
||||
" return value;",
|
||||
" };",
|
||||
" const proxyUrl = requireString('gitMirror.egressProxy.proxyUrl', proxy.proxyUrl);",
|
||||
" const configMapName = requireString('gitMirror.syncConfigMapName', mirror.syncConfigMapName);",
|
||||
" let proxyEndpoint;",
|
||||
" try { proxyEndpoint = new URL(proxyUrl); } catch (error) { throw new Error(`overlay.gitMirror.egressProxy.proxyUrl is invalid: ${error.message}`); }",
|
||||
" if (proxyEndpoint.protocol !== 'http:') throw new Error('overlay.gitMirror.egressProxy.proxyUrl must use http:// for host-route');",
|
||||
" const proxyHost = proxyEndpoint.hostname;",
|
||||
" const proxyPort = Number(proxyEndpoint.port || '80');",
|
||||
" if (!proxyHost || !Number.isInteger(proxyPort) || proxyPort < 1 || proxyPort > 65535) throw new Error('overlay.gitMirror.egressProxy.proxyUrl must include a valid host and port');",
|
||||
" const noProxy = Array.isArray(proxy.noProxy) ? proxy.noProxy.join(',') : '';",
|
||||
" const envEntries = [",
|
||||
" ['HTTP_PROXY', proxyUrl],",
|
||||
" ['HTTPS_PROXY', proxyUrl],",
|
||||
" ['ALL_PROXY', proxyUrl],",
|
||||
" ['http_proxy', proxyUrl],",
|
||||
" ['https_proxy', proxyUrl],",
|
||||
" ['all_proxy', proxyUrl],",
|
||||
" ['NO_PROXY', noProxy],",
|
||||
" ['no_proxy', noProxy],",
|
||||
" ];",
|
||||
" function readDocs(file) { return YAML.parseAllDocuments(fs.readFileSync(file, 'utf8')).map((document) => document.toJS()).filter((doc) => doc !== null); }",
|
||||
" function flattenDocs(docs) {",
|
||||
" const manifests = [];",
|
||||
" for (const doc of docs) {",
|
||||
" if (doc && typeof doc === 'object' && doc.kind === 'List' && Array.isArray(doc.items)) manifests.push(...doc.items);",
|
||||
" else manifests.push(doc);",
|
||||
" }",
|
||||
" return manifests;",
|
||||
" }",
|
||||
" function setContainerEnv(container, name, value) {",
|
||||
" if (!container || typeof container !== 'object') return false;",
|
||||
" container.env = Array.isArray(container.env) ? container.env : [];",
|
||||
" let item = container.env.find((env) => env && env.name === name);",
|
||||
" if (!item) { item = { name }; container.env.push(item); }",
|
||||
" const changed = item.value !== value || item.valueFrom !== undefined;",
|
||||
" item.value = value;",
|
||||
" delete item.valueFrom;",
|
||||
" return changed;",
|
||||
" }",
|
||||
" function patchProxyScript(script, key) {",
|
||||
" let next = String(script || '');",
|
||||
" if (next.length === 0) throw new Error(`generated git-mirror ConfigMap ${configMapName} missing ${key}`);",
|
||||
" next = next.replace(/node \\/tmp\\/hwlab-github-proxy-connect\\.(?:mjs|cjs) \\S+ \\d+/g, `node /tmp/hwlab-github-proxy-connect.mjs ${proxyHost} ${proxyPort}`);",
|
||||
" const missing = [];",
|
||||
" for (const [name, value] of envEntries) {",
|
||||
" let replaced = false;",
|
||||
" const exportPattern = new RegExp(`(^|\\\\n)([ \\\\t]*export ${escapeRegExp(name)}=)[^\\\\n]*`, 'g');",
|
||||
" next = next.replace(exportPattern, (_match, prefix, left) => { replaced = true; return `${prefix}${left}${shellSingle(value)}`; });",
|
||||
" if (!replaced) missing.push([name, value]);",
|
||||
" }",
|
||||
" if (missing.length > 0) {",
|
||||
" const block = missing.map(([name, value]) => `export ${name}=${shellSingle(value)}`).join('\\\\n');",
|
||||
" if (next.includes('\\\\nset -eu\\\\n')) next = next.replace('\\\\nset -eu\\\\n', `\\\\nset -eu\\\\n${block}\\\\n`);",
|
||||
" else next = `${block}\\\\n${next}`;",
|
||||
" }",
|
||||
" next = next.replace(/mode=node-global/g, 'mode=host-route');",
|
||||
" return next;",
|
||||
" }",
|
||||
" const gitMirrorFile = path.join(renderDir, 'devops-infra', 'git-mirror.yaml');",
|
||||
" if (!fs.existsSync(gitMirrorFile)) throw new Error(`generated git-mirror manifest missing: ${gitMirrorFile}`);",
|
||||
" const docs = readDocs(gitMirrorFile);",
|
||||
" const manifests = flattenDocs(docs);",
|
||||
" let configMapChanged = false;",
|
||||
" let workloadChanged = false;",
|
||||
" const workloadNames = new Set([mirror.serviceReadName, mirror.serviceWriteName].filter((value) => typeof value === 'string' && value.length > 0));",
|
||||
" for (const doc of manifests) {",
|
||||
" if (!doc || typeof doc !== 'object') continue;",
|
||||
" if (doc.kind === 'ConfigMap' && doc.metadata && doc.metadata.name === configMapName) {",
|
||||
" doc.data = doc.data || {};",
|
||||
" doc.data['sync.sh'] = patchProxyScript(doc.data['sync.sh'], 'sync.sh');",
|
||||
" doc.data['flush.sh'] = patchProxyScript(doc.data['flush.sh'], 'flush.sh');",
|
||||
" configMapChanged = true;",
|
||||
" continue;",
|
||||
" }",
|
||||
" if (!['Deployment', 'StatefulSet', 'DaemonSet', 'ReplicaSet', 'ReplicationController'].includes(doc.kind)) continue;",
|
||||
" const name = doc.metadata && doc.metadata.name ? String(doc.metadata.name) : '';",
|
||||
" const labels = doc.metadata && doc.metadata.labels && typeof doc.metadata.labels === 'object' ? doc.metadata.labels : {};",
|
||||
" if (!workloadNames.has(name) && labels['app.kubernetes.io/name'] !== 'git-mirror' && !name.includes('git-mirror')) continue;",
|
||||
" doc.spec = doc.spec || {};",
|
||||
" doc.spec.template = doc.spec.template || {};",
|
||||
" doc.spec.template.metadata = doc.spec.template.metadata || {};",
|
||||
" doc.spec.template.metadata.annotations = doc.spec.template.metadata.annotations || {};",
|
||||
" doc.spec.template.metadata.annotations['unidesk.ai/git-mirror-egress-proxy'] = 'host-route';",
|
||||
" doc.spec.template.metadata.annotations['unidesk.ai/git-mirror-host-proxy-config-ref'] = String(proxy.hostProxyConfigRef || '');",
|
||||
" doc.spec.template.spec = doc.spec.template.spec || {};",
|
||||
" if (proxy.podHostNetwork) {",
|
||||
" doc.spec.template.spec.hostNetwork = true;",
|
||||
" doc.spec.template.spec.dnsPolicy = 'ClusterFirstWithHostNet';",
|
||||
" } else {",
|
||||
" delete doc.spec.template.spec.hostNetwork;",
|
||||
" delete doc.spec.template.spec.dnsPolicy;",
|
||||
" }",
|
||||
" for (const group of ['containers', 'initContainers']) {",
|
||||
" for (const container of Array.isArray(doc.spec.template.spec[group]) ? doc.spec.template.spec[group] : []) {",
|
||||
" for (const [envName, envValue] of envEntries) setContainerEnv(container, envName, envValue);",
|
||||
" }",
|
||||
" }",
|
||||
" workloadChanged = true;",
|
||||
" }",
|
||||
" if (!configMapChanged) throw new Error(`generated git-mirror ConfigMap ${configMapName} was not found in ${gitMirrorFile}`);",
|
||||
" if (!workloadChanged) throw new Error(`generated git-mirror workload for host-route proxy was not found in ${gitMirrorFile}`);",
|
||||
" fs.writeFileSync(gitMirrorFile, docs.map((doc) => YAML.stringify(doc).trimEnd()).join('\\n---\\n') + '\\n');",
|
||||
"}",
|
||||
"const structured = patchStructuredPipeline();",
|
||||
"function replaceParamDefault(name, value) {",
|
||||
" const namePattern = escapeRegExp(name);",
|
||||
@@ -2253,6 +2376,7 @@ export function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
"if (text.includes('/yaml/-/yaml-') || text.includes('bun add --no-save --ignore-scripts') || text.includes('npm install --package-lock=false --no-save')) { throw new Error(`generated pipeline still downloads yaml during prepare-source in ${pipelinePath}`); }",
|
||||
"if (text.includes('npm run gitops:ts:check')) { throw new Error(`generated pipeline still uses npm gitops:ts:check gate in ${pipelinePath}`); }",
|
||||
"fs.writeFileSync(pipelinePath, text);",
|
||||
"patchGitMirrorHostRouteYaml();",
|
||||
"patchGitMirrorTransportYaml();",
|
||||
"function patchArgoYaml(filePath) {",
|
||||
" if (!YAML || !fs.existsSync(filePath)) return;",
|
||||
|
||||
Reference in New Issue
Block a user