diff --git a/config/hwlab-g14-lanes.yaml b/config/hwlab-g14-lanes.yaml new file mode 100644 index 00000000..7029e570 --- /dev/null +++ b/config/hwlab-g14-lanes.yaml @@ -0,0 +1,176 @@ +requiredNoProxy: + - hyueapi.com + - .hyueapi.com + +nodes: + G14: + route: G14 + kubeRoute: G14:k3s + sourceWorkspace: /root/hwlab + gitopsRoot: deploy/gitops/g14 + networkProfile: g14-ci-egress + downloadProfile: g14-default + +lanes: + v02: + node: G14 + minor: 2 + version: v0.2 + sourceBranch: v0.2 + workspace: /root/hwlab-v02 + cicdRepo: /root/hwlab-v02-cicd.git + cicdRepoLock: /tmp/hwlab-v02-cicd-repo.lock + app: hwlab-g14-v02 + pipeline: hwlab-v02-ci-image-publish + pipelineRunPrefix: hwlab-v02-ci-poll + serviceAccountName: hwlab-v02-tekton-runner + controlPlaneFieldManager: unidesk-hwlab-v02-control-plane + git: + url: git@github.com:pikasTech/HWLAB.git + readUrl: http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git + writeUrl: http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git + gitopsBranch: v0.2-gitops + catalogPath: deploy/artifact-catalog.v02.json + runtime: + path: deploy/gitops/g14/runtime-v02 + namespace: hwlab-v02 + renderDir: runtime-v02 + tektonDir: tekton-v02 + argoApplicationFile: application-v02.yaml + registryPrefix: 127.0.0.1:5000/hwlab + baseImage: 127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim + serviceIds: + - hwlab-cloud-api + - hwlab-cloud-web + - hwlab-gateway + - hwlab-edge-proxy + - hwlab-agent-skills + public: + webUrl: http://74.48.78.17:19666 + apiUrl: http://74.48.78.17:19667 + v03: + node: G14 + minor: 3 + version: v0.3 + sourceBranch: v0.3 + workspace: /root/hwlab-v03 + cicdRepo: /root/hwlab-v03-cicd.git + cicdRepoLock: /tmp/hwlab-v03-cicd-repo.lock + app: hwlab-g14-v03 + pipeline: hwlab-v03-ci-image-publish + pipelineRunPrefix: hwlab-v03-ci-poll + serviceAccountName: hwlab-v03-tekton-runner + controlPlaneFieldManager: unidesk-hwlab-v03-control-plane + git: + url: git@github.com:pikasTech/HWLAB.git + readUrl: http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git + writeUrl: http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git + gitopsBranch: v0.3-gitops + catalogPath: deploy/artifact-catalog.v03.json + runtime: + path: deploy/gitops/g14/runtime-v03 + namespace: hwlab-v03 + renderDir: runtime-v03 + tektonDir: tekton-v03 + argoApplicationFile: application-v03.yaml + registryPrefix: 127.0.0.1:5000/hwlab + baseImage: 127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim + serviceIds: + - hwlab-cloud-api + - hwlab-cloud-web + - hwlab-gateway + - hwlab-edge-proxy + - hwlab-agent-skills + public: + webUrl: http://74.48.78.17:19766 + apiUrl: http://74.48.78.17:19767 + +networkProfiles: + g14-ci-egress: + proxy: + http: http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789 + https: http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789 + all: http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789 + noProxy: + - localhost + - 127.0.0.1 + - ::1 + - host.docker.internal + - .svc + - .cluster.local + - ci-git-mirror + - ci-git-mirror.unidesk-ci + - ci-git-mirror.unidesk-ci.svc + - ci-git-mirror.unidesk-ci.svc.cluster.local + - g14-provider-egress-proxy + - g14-provider-egress-proxy.unidesk + - g14-provider-egress-proxy.unidesk.svc + - g14-provider-egress-proxy.unidesk.svc.cluster.local + - g14-tcp-egress-gateway + - g14-tcp-egress-gateway.unidesk + - g14-tcp-egress-gateway.unidesk.svc + - g14-tcp-egress-gateway.unidesk.svc.cluster.local + - code-queue-ci-read + - code-queue-ci-read.unidesk-ci + - code-queue-ci-read.unidesk-ci.svc + - code-queue-ci-read.unidesk-ci.svc.cluster.local + - backend-core + - oa-event-flow + - database + dockerBuildProxy: + http: http://127.0.0.1:18789 + https: http://127.0.0.1:18789 + all: http://127.0.0.1:18789 + noProxy: + - localhost + - 127.0.0.1 + - ::1 + - host.docker.internal + - registry.npmjs.org + - .registry.npmjs.org + g14-node-local-vpn: + proxy: + http: http://127.0.0.1:10808 + https: http://127.0.0.1:10808 + all: socks5h://127.0.0.1:10808 + socks5: socks5h://127.0.0.1:10808 + noProxy: + - localhost + - 127.0.0.1 + - ::1 + - .svc + - .cluster.local + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + dockerBuildProxy: + http: http://127.0.0.1:10808 + https: http://127.0.0.1:10808 + all: socks5h://127.0.0.1:10808 + noProxy: + - localhost + - 127.0.0.1 + - ::1 + - host.docker.internal + - 127.0.0.1:5000 + +downloadProfiles: + g14-default: + git: + proxyMode: inherit + retries: 3 + npm: + registry: https://registry.npmjs.org/ + retries: 3 + fetchTimeoutSeconds: 120 + pip: + indexUrl: https://pypi.org/simple + retries: 3 + timeoutSeconds: 120 + docker: + registryMirrors: [] + pullRetries: 3 + curl: + retries: 3 + connectTimeoutSeconds: 10 + maxTimeSeconds: 120 diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 78711c2e..327330ef 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -52,6 +52,7 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 DEV/PROD 滚动、P - `agentrun v01 control-plane status|trigger-current|refresh|cleanup-runs|cleanup-released-pvs [--dry-run|--confirm]` 是 AgentRun `v0.1` 在 G14 k3s 的受控 Tekton/Argo 入口。`status` 只读汇总固定 source worktree commit、对应 commit-pinned PipelineRun、GitOps latest、Argo Application、`agentrun-v01` manager source commit、`planArtifacts.summary`、env image result 和 git mirror 摘要,并报告 manager/Argo/GitOps 是否对齐当前 source commit。默认输出是 compact commander 视图:`summary` 给出 source、PipelineRun、Argo、manager image、git mirror 和 `aligned` 结论;`timings` 给出 `sourceMs`、`runtimeMs`、`gitMirrorMs` 和 `totalMs`;远端 stdout/stderr tail 默认省略,失败时仍展开必要 tail,完整 tail 用 `--full`,原始 git mirror cache 用 `--raw`。`status` 聚合 source 后会并行读取 runtime 和 git mirror,并向 stderr 输出 `agentrun.control-plane.status.progress` JSON 事件,覆盖 `source`、`runtime`、`git-mirror` 的 started/succeeded/failed 和 elapsedMs,避免 10s 以上状态聚合期间无可见进展;`trigger-current` 先快进 `G14:/root/agentrun-v01` 到 `origin/v0.1`,检查 `devops-infra` mirror 的 `localV01` 是否等于目标 source commit,必要时先执行受控 mirror sync,再创建 `agentrun-v01-ci-` PipelineRun。confirmed trigger 只提交 CI/CD 工作并返回后续 `status` 命令,不等待完整 PipelineRun;同名 PipelineRun 运行中或已成功时拒绝重复触发,只允许失败态重建或首次创建。`refresh` 只对 `argocd/agentrun-g14-v01` 执行 hard refresh,用于 GitOps promotion 已完成但 Argo 仍停留旧 revision 时的受控同步入口;它不直接 patch runtime workload。`cleanup-runs` 只清理 `agentrun-ci` 中已完成且超过 `--min-age-minutes` 的 `agentrun-v01-ci-*` PipelineRun,通过 Tekton ownerRef 回收临时 workspace PVC;dry-run 必须输出候选 PipelineRun、owned PVC、active mount 保护、local-path 实际估算 bytes 和 confirm 命令。`cleanup-released-pvs` 只处理 `agentrun-ci`、`local-path`、`Delete` reclaim policy 的 `Released` PV,用于 PipelineRun 删除后残留 PV 的二次回收;它不触碰 runtime namespace、业务 PVC、Secret、registry storage 或 GitOps desired state。AgentRun 运行时和 SPEC 事实来源仍在 AgentRun 仓库,UniDesk 只维护受控运维入口。 - `agentrun v01 git-mirror status|sync|flush [--dry-run|--confirm]` 是 AgentRun `v0.1` 使用 `devops-infra` git mirror/relay 的受控维护入口。`status` 默认返回 read/write URL、`localV01`、`githubV01`、`localGitops`、`githubGitops`、`pendingFlush`、`githubInSync` 和 exact full-SHA shallow fetch 摘要,不默认展开完整 cache stdout;需要探测 tail 时用 `--full`,需要原始 cache 输出时用 `--raw`。`sync` 创建 manual Job,把 GitHub `v0.1` 和 `v0.1-gitops` refs 拉入 `/cache/pikasTech/agentrun.git`;`flush` 把本地 `v0.1-gitops` 快进推回 GitHub。confirmed `sync`/`flush` 默认创建 `.state/jobs/` 异步 job 并立刻返回 `job.id`、`statusCommand` 和日志路径;只有现场同步调试才显式加 `--wait`。该入口与 HWLAB v0.2 mirror 共用 `devops-infra` 服务和 cache PVC,但 repo path、refs、status 文件和 CLI 命令彼此独立。 - `hwlab g14 control-plane status|apply --lane v02 [--dry-run|--confirm]` 是 HWLAB `v0.2` 加法 lane 的受控 Tekton/Argo 控制面维护入口,source commit 只来自 G14 专用 bare repo `/root/hwlab-v02-cicd.git` 的 `refs/remotes/origin/v0.2`;`/root/hwlab-v02` 只作为人工开发和短连接源码工具 workspace 被观测,dirty/stale 状态必须输出为 isolated warning 而不能阻塞 CI/CD。该入口面向 branch `v0.2`、namespace `hwlab-ci` 和 Argo application `hwlab-g14-v02`;默认 `status` 只读汇总最新 source head 的 pipeline、RBAC/ServiceAccount、Argo、当前 commit PipelineRun、当前 PipelineRun 的 TaskRun 条件摘要、最近 PipelineRun 摘要、活跃 PipelineRun、遗留 v02 CronJob 清理状态、commit alignment,以及 19666/19667 的 Cloud Web 静态资源和 API live 探针。分支被后续提交推进后,要复查已完成 run 时使用 `status --lane v02 --pipeline-run hwlab-v02-ci-poll-`;已知完整 source SHA 但不想依赖最新 head 时使用 `status --lane v02 --source-commit `。定点 `status` 输出 `statusTarget.mode` 和 `targetValidation`,只检查指定 PipelineRun/source commit 的证据;`targetValidation.state=passed` 表示该目标已满足 PipelineRun succeeded、Argo `Synced/Healthy`、19666/19667 探针、Git mirror flushed,并且该 run 的 `planArtifacts.rolloutServices` 运行时 source commit 对齐;`planArtifacts.reusedServices` 作为 runtime/provenance 证据呈现,但不能被强制要求等于目标 source commit。`targetValidation.state=superseded` 表示该目标已成功且 runtime 已被同一分支后续成功 PipelineRun 取代,`falseGreenGuard` 在该状态下应标为 superseded/not-applicable。两种状态都不得因为 `origin/v0.2` 后续推进而把历史 run 判为失败;默认不带定点参数时仍严格判定最新 source head alignment。TaskRun 摘要的 `performance` 字段会把超过 120s 的 build TaskRun 标为慢任务、超过 180s 标为 critical warning,用于暴露 env reuse/git mirror 命中率回归,但不作为阻断门禁;CI/CD 性能验收应同时看 `planArtifacts.summary`、`taskRuns.performance.warningCount` 和 PipelineRun duration,纯 CLI/文档或无 runtime 重建需求的后续提交应稳定表现为 `build=0 reuse=` 且无 build TaskRun warning,首次引入或切换 env image 时允许只构建必要 env image 一次。`webAssets` 必须直接给出 `readonly-rpc` 删除、sidebar/workspace/event panel 关键 CSS、`/app.js` 是否可读取和字节数、`/health/live` 与 API revision;`apiRevision` 是 cloud-api 服务自身 revision,Cloud Web 静态资源变更时允许它与 source commit 不同,不能把这种差异误判成 Cloud Web 未发布。默认只读取必要字段,禁止把完整 PipelineRun spec、Tekton 内联脚本、历史大对象或整份 CSS/HTML/JS 展开到默认输出;`apply` 先自动 fetch `/root/hwlab-v02-cicd.git` 并从 commit-pinned detached worktree 执行 render check,再经 `G14:k3s` server-side apply `tekton-v02/rbac.yaml`、`pipeline.yaml`、`argocd/project.yaml` 和 `argocd/application-v02.yaml`,confirmed apply 会删除遗留 v02 CronJob,但不会应用 runtime-v02 workload、Secret 或数据迁移。 +- `hwlab g14 control-plane status|apply|trigger-current --lane v03` 使用 `/root/unidesk/config/hwlab-g14-lanes.yaml` 生成 runtime lane spec。该 YAML 同时声明 `nodes.G14`、`lanes.v02/v03`、`networkProfiles` 和 `downloadProfiles`;status 默认输出 `expected.configPath`、node、network/download profile、有效 `NO_PROXY` 和 Docker build proxy 摘要,confirmed trigger 创建的 PipelineRun annotation 记录 node/profile id。新增 `v0.4+` lane 时应先加 YAML,再让 GitOps render、CI prepare-source/catalog fetch、runtime env 注入和 Secret/DB/bootstrap helper 消费同一 spec;不要在脚本、CI manifest 或 helper 默认值里新增散落的 G14 代理、下载源或端口硬编码。`hyueapi.com` / `.hyueapi.com` 是强制保留的 `NO_PROXY` 条目,lane 覆盖不得移除。 - `hwlab g14 control-plane trigger-current --lane v02 [--dry-run|--confirm]` 是 v02 标准手动触发入口:先自动 fetch `/root/hwlab-v02-cicd.git`,解析当前 `origin/v0.2` full SHA,创建 commit-pinned `hwlab-v02-ci-poll-` PipelineRun;读 Git 走 `git-mirror-http.devops-infra.svc.cluster.local`,GitOps promotion 写 `git-mirror-write.devops-infra.svc.cluster.local`;confirmed trigger 在创建 PipelineRun 前会先按当前 source commit 在 G14 临时 detached worktree 中 render,再 server-side apply v02 Tekton RBAC、Pipeline 与 Argo Application,避免 CI/CD 脚本或 runtime-ready 逻辑已合并但集群仍执行旧 Pipeline 定义;该 render 不要求固定 `/root/hwlab-v02` 工作树 clean,也不得因 `.worktree/` 或其他并行未提交修改阻塞;同名 PipelineRun 存在时默认复用现有状态,不删除重建,失败 run 的重试策略必须显式设计,不能恢复默认 delete/create。 创建 PipelineRun 前会读取 `devops-infra` mirror refs,若 `localV02` 未等于当前 source commit,则自动执行一次受控 manual `git-mirror sync` Job 并复核 ref,复核失败时停止触发,避免 Tekton `prepare-source` 已知失败;services 参数只包含 v02 runtime service matrix,`hwlab-cli` 是固定 repo 短连接源码工具,不进入 PipelineRun service build。 `--dry-run` 只报告是否会 pre-sync,不创建 Job;confirmed trigger 默认创建 `.state/jobs/` 异步 job 并立刻返回 `job.id`、`statusCommand`、stdout/stderr 路径,避免 git mirror pre-sync 或 PipelineRun 创建期间长时间阻塞;`--wait` 路径也必须向 stderr 输出 `hwlab.v02.trigger.progress` JSON 事件,覆盖 `control-plane-refresh`、`git-mirror-pre-sync` 和 `create-pipelinerun`,避免异步 job 长时间只有启动命令而无法判断卡点;默认 JSON 必须对 `manifest_b64`、长脚本和远端 stdout/stderr 做有界摘要,保留长度与 hash,最终 trigger 结果只返回阶段摘要和关键 tail,完整内容通过 job stdout/stderr 文件渐进披露;只有现场同步调试才显式加 `--wait`;旧 `rerun-current` 只作为输入别名保留。PipelineRun `Completed`、Argo `Synced/Healthy` 和 `webAssets.ok=true` 只证明 G14 runtime 已更新;交付收口还必须用 `hwlab g14 git-mirror status` 查看 `cache.summary.pendingFlush`,若为 true,继续执行受控 `hwlab g14 git-mirror flush --confirm` 并用 job status 轮询到 `pendingFlush=false`。 diff --git a/docs/reference/g14.md b/docs/reference/g14.md index 50fd6f7f..29326b0e 100644 --- a/docs/reference/g14.md +++ b/docs/reference/g14.md @@ -69,6 +69,8 @@ The `v0.2` CI/CD integration must be additive: add a manual UniDesk trigger, ded For current G14/v0.2, `deploy/deploy.yaml` is the single human-authored deploy/runtime config source. `deploy/deploy.json` is not a v0.2 compatibility source and must not be recreated for this lane. YAML and JSON parsing are centralized in the HWLAB repo's format-agnostic config layer: `scripts/src/structured-config.mjs` handles file format parsing/writing, while `scripts/src/deploy-config.mjs` owns deploy-config defaults and shape. Renderers, planners, smoke scripts and CLIs should consume that layer through `readStructuredFile` / `writeStructuredFile` / `readDeployConfig` or equivalent helpers; do not scatter direct YAML parser imports or ad hoc `readFile` + `YAML.parse` calls. Legacy D601 HWLAB CD still has its own `deploy/deploy.json` desired-state documented in `docs/reference/hwlab.md`; that legacy path is not the G14/v0.2 authority. +On the UniDesk control-plane side, HWLAB G14 runtime lane expansion is sourced from `/root/unidesk/config/hwlab-g14-lanes.yaml`. That YAML owns `nodes`, `lanes`, `networkProfiles` and `downloadProfiles` for the UniDesk trigger/status/apply layer: `lanes.v03.node` points at `nodes.G14`, while proxy URLs, `NO_PROXY`, git/npm/pip/docker/curl retry/download defaults and Docker build proxy settings live under profile objects. G14 must remain a node config value, not a hardcoded code path such as `g14Proxy` or `g14GitOps`; future `v0.4+` lanes should be added by adding YAML entries and then consuming the generated spec. `hyueapi.com` and `.hyueapi.com` are required `NO_PROXY` entries and must remain present in effective runtime and Docker build proxy environments. + The `devops-infra` git mirror/relay remains manual and CLI-controlled, not CronJob-driven. The standard `v0.2` delivery trigger is `bun scripts/cli.ts hwlab g14 monitor-prs --lane v02`: it watches base=`v0.2` PRs, waits for GitHub preflight/CI readiness, auto-merges only ready and non-conflicting PRs, then drives the same controlled CD path and comments pending/blocked/succeeded/failed/timeout state back to the PR. The lower-level `bun scripts/cli.ts hwlab g14 control-plane trigger-current --lane v02 --confirm` remains the manual recovery or diagnosis entry; it must fetch `/root/hwlab-v02-cicd.git`, resolve the current `origin/v0.2` source commit, check the mirror's `localV02` ref before creating the PipelineRun, run one bounded manual `git-mirror sync` Job when the mirror is stale, and only continue after the mirror ref matches the current source commit. Use `hwlab g14 git-mirror sync --confirm` directly only for explicit mirror maintenance or diagnosis. After a `v0.2` PipelineRun completes, treat runtime rollout and remote GitOps persistence as two separate checks. `hwlab g14 control-plane status --lane v02` is the runtime check: it must show the expected source commit, PipelineRun completed, Argo `Synced/Healthy`, public 19666/19667 probes passing, and Cloud Web asset probes such as `/app.js` readable. `hwlab g14 git-mirror status` is the persistence check: `cache.summary.pendingFlush` must be false and `cache.summary.githubInSync` true before declaring GitOps fully flushed back to GitHub. The PR monitor performs this flush automatically for its own merged PRs and records the result in the PR comment. Manual operators should run `bun scripts/cli.ts hwlab g14 git-mirror flush --confirm` and poll the returned job with `bun scripts/cli.ts job status --tail-bytes 12000` only when they used lower-level manual trigger/status paths or when the monitor reports a flush failure; do not replace this with raw `kubectl`, native `git push`, or a long SSH wait. diff --git a/scripts/hwlab-g14-contract-test.ts b/scripts/hwlab-g14-contract-test.ts index 56f3b7e0..9d990617 100644 --- a/scripts/hwlab-g14-contract-test.ts +++ b/scripts/hwlab-g14-contract-test.ts @@ -1,5 +1,5 @@ -import { activeV02PipelineRuns, g14ObservabilityQueryAssertion, gitMirrorFlushJobManifest, gitMirrorStatusSummary, gitMirrorSyncJobManifest, gitMirrorV02SyncRequirement, hwlabG14Help, hwlabG14MonitorStateFileName, parseGitMirrorStatusRefs, parseK8sCpuMillicores, parseK8sMemoryMiB, parsePipelineTaskRunMetrics, parseV02TriggerSnapshot, rolloutRecordBody, semanticChangelogBullets, summarizeV02CdStatus, v02CloseoutVerdict, v02CommitAlignment, v02ControlPlaneRefreshScriptHash, v02ControlPlaneRenderScript, v02ExistingPipelineRunReuseDecision, v02FalseGreenGuard, v02GitMirrorPreSyncWaitMs, v02LatestOnlyTargetValidation, v02PipelineServiceIds, v02PrAutomationCommentBody, v02ReusableGitMirrorPreSyncMarker, v02ReusableRefreshMarker, v02StatusHistoryPolicy, v02TaskRunPerformanceSummary } from "./src/hwlab-g14"; -import { hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec } from "./src/hwlab-g14-lanes"; +import { activeV02PipelineRuns, g14ObservabilityQueryAssertion, gitMirrorFlushJobManifest, gitMirrorStatusSummary, gitMirrorSyncJobManifest, gitMirrorV02SyncRequirement, hwlabG14Help, hwlabG14MonitorStateFileName, parseGitMirrorStatusRefs, parseK8sCpuMillicores, parseK8sMemoryMiB, parsePipelineTaskRunMetrics, parseV02TriggerSnapshot, rolloutRecordBody, runtimeLanePipelineRunManifest, semanticChangelogBullets, summarizeV02CdStatus, v02CloseoutVerdict, v02CommitAlignment, v02ControlPlaneRefreshScriptHash, v02ControlPlaneRenderScript, v02ExistingPipelineRunReuseDecision, v02FalseGreenGuard, v02GitMirrorPreSyncWaitMs, v02LatestOnlyTargetValidation, v02PipelineServiceIds, v02PrAutomationCommentBody, v02ReusableGitMirrorPreSyncMarker, v02ReusableRefreshMarker, v02StatusHistoryPolicy, v02TaskRunPerformanceSummary } from "./src/hwlab-g14"; +import { hwlabRequiredNoProxyEntries, hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec } from "./src/hwlab-g14-lanes"; import { runCommand } from "./src/command"; function assertCondition(condition: unknown, message: string, detail: unknown = {}): void { @@ -33,7 +33,9 @@ assertCondition( && hwlabG14MonitorStateFileName({ lane: "v02", once: true, dryRun: true }) === "latest-v02-once-dry-run-job.json", "v0.2 PR monitor state pointers must not overwrite the legacy G14 monitor pointers", ); -const hwlabHelpUsage = Array.isArray(hwlabG14Help().usage) ? hwlabG14Help().usage.map(String) : []; +const hwlabHelp = hwlabG14Help(); +const hwlabHelpUsage = Array.isArray(hwlabHelp.usage) ? hwlabHelp.usage.map((line: unknown) => String(line)) : []; +const hwlabHelpJson = JSON.stringify(hwlabHelp); assertCondition( hwlabHelpUsage.some((line) => line.includes("control-plane status --lane v02 --pipeline-run")) && hwlabHelpUsage.some((line) => line.includes("control-plane status --lane v02 --source-commit")) @@ -49,13 +51,16 @@ assertCondition( hwlabHelpUsage.some((line) => line.includes("control-plane status --lane v03")) && hwlabHelpUsage.some((line) => line.includes("control-plane apply --lane v03 --dry-run")) && hwlabHelpUsage.some((line) => line.includes("control-plane trigger-current --lane v03 --dry-run")) - && JSON.stringify(hwlabG14Help()).includes("runtime lane v02/v03"), + && hwlabHelpJson.includes("runtime lane v02/v03"), "v0.3 control-plane help must expose the runtime lane bootstrap status/apply/trigger entrypoints", hwlabHelpUsage, ); const v03LaneSpec = hwlabRuntimeLaneSpec("v03"); assertCondition( JSON.stringify(hwlabRuntimeLaneIds()) === JSON.stringify(["v02", "v03"]) + && hwlabRuntimeLaneConfigPath() === "config/hwlab-g14-lanes.yaml" + && v03LaneSpec.nodeId === "G14" + && v03LaneSpec.nodeRoute === "G14" && v03LaneSpec.sourceBranch === "v0.3" && v03LaneSpec.gitopsBranch === "v0.3-gitops" && v03LaneSpec.workspace === "/root/hwlab-v03" @@ -64,17 +69,43 @@ assertCondition( && v03LaneSpec.runtimeRenderDir === "runtime-v03" && v03LaneSpec.pipeline === "hwlab-v03-ci-image-publish" && v03LaneSpec.publicWebUrl.endsWith(":19766") - && v03LaneSpec.publicApiUrl.endsWith(":19767"), - "runtime lane spec must make v0.3 expansion a config-driven lane instead of scattered literals", + && v03LaneSpec.publicApiUrl.endsWith(":19767") + && v03LaneSpec.networkProfileId === "g14-ci-egress" + && v03LaneSpec.downloadProfileId === "g14-default" + && v03LaneSpec.networkProfile.proxy.noProxy.includes("hyueapi.com") + && v03LaneSpec.networkProfile.proxy.noProxy.includes(".hyueapi.com") + && v03LaneSpec.networkProfile.dockerBuildProxy.noProxy.includes("hyueapi.com") + && v03LaneSpec.downloadProfile.git.proxyMode === "inherit" + && v03LaneSpec.downloadProfile.npm.registry === "https://registry.npmjs.org/" + && JSON.stringify(hwlabRequiredNoProxyEntries()) === JSON.stringify(["hyueapi.com", ".hyueapi.com"]), + "runtime lane spec must make v0.3 node, network, and download expansion config-driven instead of scattered literals", v03LaneSpec, ); +assertCondition( + hwlabHelpJson.includes("config/hwlab-g14-lanes.yaml") + && hwlabHelpJson.includes("g14-ci-egress") + && hwlabHelpJson.includes("g14-default") + && hwlabHelpJson.includes("hyueapi.com"), + "G14 HWLAB help must expose the runtime lane YAML and required network/download profile identifiers", + hwlabHelp, +); +const v03PipelineRunManifest = runtimeLanePipelineRunManifest(v03LaneSpec, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +const v03PipelineRunAnnotations = record(record(v03PipelineRunManifest.metadata).annotations); +assertCondition( + v03PipelineRunAnnotations["hwlab.pikastech.local/node"] === "G14" + && v03PipelineRunAnnotations["hwlab.pikastech.local/network-profile"] === "g14-ci-egress" + && v03PipelineRunAnnotations["hwlab.pikastech.local/download-profile"] === "g14-default" + && String(v03PipelineRunAnnotations["hwlab.pikastech.local/no-proxy-required"]).includes("hyueapi.com"), + "runtime lane PipelineRun manifest must preserve node/network/download profile provenance", + v03PipelineRunAnnotations, +); assertCondition( hwlabHelpUsage.some((line) => line.includes("monitor-prs --lane v02")) && hwlabHelpUsage.some((line) => line.includes("monitor-prs --lane v02 --status")) - && JSON.stringify(hwlabG14Help()).includes("v02-pr-comment-signatures.json") - && JSON.stringify(hwlabG14Help()).includes("latest-only"), + && hwlabHelpJson.includes("v02-pr-comment-signatures.json") + && hwlabHelpJson.includes("latest-only"), "v0.2 PR monitor help must expose the auto CI/CD lane, status query, latest-only CD, and dedupe comment state", - hwlabG14Help(), + hwlabHelp, ); assertCondition( hwlabHelpUsage.some((line) => line.includes("git-mirror apply --lane v02 --confirm")) diff --git a/scripts/src/hwlab-g14-lanes.ts b/scripts/src/hwlab-g14-lanes.ts index bf245b0e..e840ca0b 100644 --- a/scripts/src/hwlab-g14-lanes.ts +++ b/scripts/src/hwlab-g14-lanes.ts @@ -1,12 +1,65 @@ +import { readFileSync } from "node:fs"; +import { rootPath } from "./config"; + export type HwlabRuntimeLane = "v02" | "v03"; -export interface HwlabRuntimeLaneConfig { - readonly lane: HwlabRuntimeLane; - readonly minor: number; +export interface HwlabRuntimeNodeSpec { + readonly id: string; + readonly route: string; + readonly kubeRoute: string; + readonly sourceWorkspace: string; + readonly gitopsRoot: string; + readonly networkProfileId: string; + readonly downloadProfileId: string; +} + +export interface HwlabProxySpec { + readonly http: string; + readonly https: string; + readonly all: string; + readonly socks5?: string; + readonly noProxy: readonly string[]; +} + +export interface HwlabNetworkProfileSpec { + readonly id: string; + readonly proxy: HwlabProxySpec; + readonly dockerBuildProxy: HwlabProxySpec; +} + +export interface HwlabDownloadProfileSpec { + readonly id: string; + readonly git: { + readonly proxyMode: "inherit" | "direct" | "none"; + readonly retries: number; + }; + readonly npm: { + readonly registry: string; + readonly retries: number; + readonly fetchTimeoutSeconds: number; + }; + readonly pip: { + readonly indexUrl: string; + readonly retries: number; + readonly timeoutSeconds: number; + }; + readonly docker: { + readonly registryMirrors: readonly string[]; + readonly pullRetries: number; + }; + readonly curl: { + readonly retries: number; + readonly connectTimeoutSeconds: number; + readonly maxTimeSeconds: number; + }; } export interface HwlabRuntimeLaneSpec { readonly lane: HwlabRuntimeLane; + readonly nodeId: string; + readonly nodeRoute: string; + readonly nodeKubeRoute: string; + readonly gitopsRoot: string; readonly minor: number; readonly version: string; readonly sourceBranch: string; @@ -33,62 +86,279 @@ export interface HwlabRuntimeLaneSpec { readonly serviceIds: readonly string[]; readonly publicWebUrl: string; readonly publicApiUrl: string; + readonly networkProfileId: string; + readonly downloadProfileId: string; + readonly networkProfile: HwlabNetworkProfileSpec; + readonly downloadProfile: HwlabDownloadProfileSpec; } -export const HWLAB_RUNTIME_LANE_CONFIGS = [ - { lane: "v02", minor: 2 }, - { lane: "v03", minor: 3 }, -] as const satisfies readonly HwlabRuntimeLaneConfig[]; +export const HWLAB_G14_LANE_CONFIG_PATH = "config/hwlab-g14-lanes.yaml"; -const HWLAB_GIT_URL = "git@github.com:pikasTech/HWLAB.git"; -const HWLAB_GIT_READ_URL = "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git"; -const HWLAB_GIT_WRITE_URL = "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git"; -const HWLAB_REGISTRY_PREFIX = "127.0.0.1:5000/hwlab"; -const HWLAB_BASE_IMAGE = "127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim"; -const HWLAB_SERVICE_IDS = [ - "hwlab-cloud-api", - "hwlab-cloud-web", - "hwlab-gateway", - "hwlab-edge-proxy", - "hwlab-agent-skills", -] as const; +interface HwlabLaneConfig { + readonly id: HwlabRuntimeLane; + readonly node: string; + readonly minor: number; + readonly version: string; + readonly sourceBranch: string; + readonly workspace: string; + readonly cicdRepo: string; + readonly cicdRepoLock: string; + readonly app: string; + readonly pipeline: string; + readonly pipelineRunPrefix: string; + readonly serviceAccountName: string; + readonly controlPlaneFieldManager: string; + readonly git: { readonly url: string; readonly readUrl: string; readonly writeUrl: string }; + readonly gitopsBranch: string; + readonly catalogPath: string; + readonly runtime: { readonly path: string; readonly namespace: string; readonly renderDir: string }; + readonly tektonDir: string; + readonly argoApplicationFile: string; + readonly registryPrefix: string; + readonly baseImage: string; + readonly serviceIds: readonly string[]; + readonly public: { readonly webUrl: string; readonly apiUrl: string }; +} -function buildRuntimeLaneSpec(config: HwlabRuntimeLaneConfig): HwlabRuntimeLaneSpec { - const version = `v0.${config.minor}`; - const lane = config.lane; +interface HwlabG14LaneConfig { + readonly requiredNoProxy: readonly string[]; + readonly nodes: Record; + readonly lanes: Record; + readonly networkProfiles: Record; + readonly downloadProfiles: Record; +} + +function asRecord(value: unknown, path: string): Record { + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${path} must be an object`); + return value as Record; +} + +function stringField(obj: Record, key: string, path: string): string { + const value = obj[key]; + if (typeof value !== "string" || value.length === 0) throw new Error(`${path}.${key} must be a non-empty string`); + return value; +} + +function numberField(obj: Record, key: string, path: string): number { + const value = obj[key]; + if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) throw new Error(`${path}.${key} must be a positive integer`); + return value; +} + +function stringArrayField(obj: Record, key: string, path: string): string[] { + const value = obj[key]; + if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.length === 0)) { + throw new Error(`${path}.${key} must be an array of non-empty strings`); + } + return [...value] as string[]; +} + +function optionalStringField(obj: Record, key: string, path: string): string | undefined { + const value = obj[key]; + if (value === undefined) return undefined; + if (typeof value !== "string" || value.length === 0) throw new Error(`${path}.${key} must be a non-empty string`); + return value; +} + +function sortedRecordEntries(value: unknown, path: string): Array<[string, Record]> { + return Object.entries(asRecord(value, path)).map(([key, item]) => [key, asRecord(item, `${path}.${key}`)]); +} + +function unique(values: readonly string[]): string[] { + return [...new Set(values)]; +} + +function proxyConfig(raw: Record, id: string, key: "proxy" | "dockerBuildProxy", requiredNoProxy: readonly string[]): HwlabProxySpec { + const proxy = asRecord(raw[key], `networkProfiles.${id}.${key}`); return { - lane, - minor: config.minor, + http: stringField(proxy, "http", `networkProfiles.${id}.${key}`), + https: stringField(proxy, "https", `networkProfiles.${id}.${key}`), + all: stringField(proxy, "all", `networkProfiles.${id}.${key}`), + socks5: optionalStringField(proxy, "socks5", `networkProfiles.${id}.${key}`), + noProxy: unique([...stringArrayField(proxy, "noProxy", `networkProfiles.${id}.${key}`), ...requiredNoProxy]), + }; +} + +function networkProfileConfig(id: string, raw: Record, requiredNoProxy: readonly string[]): HwlabNetworkProfileSpec { + return { + id, + proxy: proxyConfig(raw, id, "proxy", requiredNoProxy), + dockerBuildProxy: proxyConfig(raw, id, "dockerBuildProxy", requiredNoProxy), + }; +} + +function downloadProfileConfig(id: string, raw: Record): HwlabDownloadProfileSpec { + const git = asRecord(raw.git, `downloadProfiles.${id}.git`); + const npm = asRecord(raw.npm, `downloadProfiles.${id}.npm`); + const pip = asRecord(raw.pip, `downloadProfiles.${id}.pip`); + const docker = asRecord(raw.docker, `downloadProfiles.${id}.docker`); + const curl = asRecord(raw.curl, `downloadProfiles.${id}.curl`); + const proxyMode = stringField(git, "proxyMode", `downloadProfiles.${id}.git`); + if (proxyMode !== "inherit" && proxyMode !== "direct" && proxyMode !== "none") { + throw new Error(`downloadProfiles.${id}.git.proxyMode must be inherit, direct, or none`); + } + return { + id, + git: { proxyMode, retries: numberField(git, "retries", `downloadProfiles.${id}.git`) }, + npm: { + registry: stringField(npm, "registry", `downloadProfiles.${id}.npm`), + retries: numberField(npm, "retries", `downloadProfiles.${id}.npm`), + fetchTimeoutSeconds: numberField(npm, "fetchTimeoutSeconds", `downloadProfiles.${id}.npm`), + }, + pip: { + indexUrl: stringField(pip, "indexUrl", `downloadProfiles.${id}.pip`), + retries: numberField(pip, "retries", `downloadProfiles.${id}.pip`), + timeoutSeconds: numberField(pip, "timeoutSeconds", `downloadProfiles.${id}.pip`), + }, + docker: { + registryMirrors: stringArrayField(docker, "registryMirrors", `downloadProfiles.${id}.docker`), + pullRetries: numberField(docker, "pullRetries", `downloadProfiles.${id}.docker`), + }, + curl: { + retries: numberField(curl, "retries", `downloadProfiles.${id}.curl`), + connectTimeoutSeconds: numberField(curl, "connectTimeoutSeconds", `downloadProfiles.${id}.curl`), + maxTimeSeconds: numberField(curl, "maxTimeSeconds", `downloadProfiles.${id}.curl`), + }, + }; +} + +function nodeConfig(id: string, raw: Record): HwlabRuntimeNodeSpec { + return { + id, + route: stringField(raw, "route", `nodes.${id}`), + kubeRoute: stringField(raw, "kubeRoute", `nodes.${id}`), + sourceWorkspace: stringField(raw, "sourceWorkspace", `nodes.${id}`), + gitopsRoot: stringField(raw, "gitopsRoot", `nodes.${id}`), + networkProfileId: stringField(raw, "networkProfile", `nodes.${id}`), + downloadProfileId: stringField(raw, "downloadProfile", `nodes.${id}`), + }; +} + +function isSupportedLaneId(id: string): id is HwlabRuntimeLane { + return id === "v02" || id === "v03"; +} + +function laneConfig(id: HwlabRuntimeLane, raw: Record): HwlabLaneConfig { + const git = asRecord(raw.git, `lanes.${id}.git`); + const runtime = asRecord(raw.runtime, `lanes.${id}.runtime`); + const publicUrls = asRecord(raw.public, `lanes.${id}.public`); + const minor = numberField(raw, "minor", `lanes.${id}`); + const version = stringField(raw, "version", `lanes.${id}`); + if (version !== `v0.${minor}`) throw new Error(`lanes.${id}.version must equal v0.${minor}`); + return { + id, + node: stringField(raw, "node", `lanes.${id}`), + minor, version, - sourceBranch: version, - workspace: `/root/hwlab-${lane}`, - cicdRepo: `/root/hwlab-${lane}-cicd.git`, - cicdRepoLock: `/tmp/hwlab-${lane}-cicd-repo.lock`, - app: `hwlab-g14-${lane}`, - pipeline: `hwlab-${lane}-ci-image-publish`, - pipelineRunPrefix: `hwlab-${lane}-ci-poll`, - serviceAccountName: `hwlab-${lane}-tekton-runner`, - controlPlaneFieldManager: `unidesk-hwlab-${lane}-control-plane`, - gitUrl: HWLAB_GIT_URL, - gitReadUrl: HWLAB_GIT_READ_URL, - gitWriteUrl: HWLAB_GIT_WRITE_URL, - gitopsBranch: `${version}-gitops`, - catalogPath: `deploy/artifact-catalog.${lane}.json`, - runtimePath: `deploy/gitops/g14/runtime-${lane}`, - runtimeNamespace: `hwlab-${lane}`, - runtimeRenderDir: `runtime-${lane}`, - tektonDir: `tekton-${lane}`, - argoApplicationFile: `application-${lane}.yaml`, - registryPrefix: HWLAB_REGISTRY_PREFIX, - baseImage: HWLAB_BASE_IMAGE, - serviceIds: HWLAB_SERVICE_IDS, - publicWebUrl: `http://74.48.78.17:${19466 + config.minor * 100}`, - publicApiUrl: `http://74.48.78.17:${19467 + config.minor * 100}`, + sourceBranch: stringField(raw, "sourceBranch", `lanes.${id}`), + workspace: stringField(raw, "workspace", `lanes.${id}`), + cicdRepo: stringField(raw, "cicdRepo", `lanes.${id}`), + cicdRepoLock: stringField(raw, "cicdRepoLock", `lanes.${id}`), + app: stringField(raw, "app", `lanes.${id}`), + pipeline: stringField(raw, "pipeline", `lanes.${id}`), + pipelineRunPrefix: stringField(raw, "pipelineRunPrefix", `lanes.${id}`), + serviceAccountName: stringField(raw, "serviceAccountName", `lanes.${id}`), + controlPlaneFieldManager: stringField(raw, "controlPlaneFieldManager", `lanes.${id}`), + git: { + url: stringField(git, "url", `lanes.${id}.git`), + readUrl: stringField(git, "readUrl", `lanes.${id}.git`), + writeUrl: stringField(git, "writeUrl", `lanes.${id}.git`), + }, + gitopsBranch: stringField(raw, "gitopsBranch", `lanes.${id}`), + catalogPath: stringField(raw, "catalogPath", `lanes.${id}`), + runtime: { + path: stringField(runtime, "path", `lanes.${id}.runtime`), + namespace: stringField(runtime, "namespace", `lanes.${id}.runtime`), + renderDir: stringField(runtime, "renderDir", `lanes.${id}.runtime`), + }, + tektonDir: stringField(raw, "tektonDir", `lanes.${id}`), + argoApplicationFile: stringField(raw, "argoApplicationFile", `lanes.${id}`), + registryPrefix: stringField(raw, "registryPrefix", `lanes.${id}`), + baseImage: stringField(raw, "baseImage", `lanes.${id}`), + serviceIds: stringArrayField(raw, "serviceIds", `lanes.${id}`), + public: { + webUrl: stringField(publicUrls, "webUrl", `lanes.${id}.public`), + apiUrl: stringField(publicUrls, "apiUrl", `lanes.${id}.public`), + }, + }; +} + +function readHwlabG14LaneConfig(): HwlabG14LaneConfig { + const path = rootPath(HWLAB_G14_LANE_CONFIG_PATH); + const raw = readFileSync(path, "utf8"); + const parsed = asRecord(Bun.YAML.parse(raw) as unknown, HWLAB_G14_LANE_CONFIG_PATH); + const requiredNoProxy = stringArrayField(parsed, "requiredNoProxy", HWLAB_G14_LANE_CONFIG_PATH); + for (const required of ["hyueapi.com", ".hyueapi.com"]) { + if (!requiredNoProxy.includes(required)) throw new Error(`${HWLAB_G14_LANE_CONFIG_PATH}.requiredNoProxy must include ${required}`); + } + const nodes = Object.fromEntries(sortedRecordEntries(parsed.nodes, "nodes").map(([id, item]) => [id, nodeConfig(id, item)])); + const networkProfiles = Object.fromEntries( + sortedRecordEntries(parsed.networkProfiles, "networkProfiles").map(([id, item]) => [id, networkProfileConfig(id, item, requiredNoProxy)]), + ); + const downloadProfiles = Object.fromEntries( + sortedRecordEntries(parsed.downloadProfiles, "downloadProfiles").map(([id, item]) => [id, downloadProfileConfig(id, item)]), + ); + const lanes = Object.fromEntries(sortedRecordEntries(parsed.lanes, "lanes").map(([id, item]) => { + if (!isSupportedLaneId(id)) throw new Error(`lanes.${id} is not supported by this CLI build`); + return [id, laneConfig(id, item)]; + })) as Record; + for (const node of Object.values(nodes)) { + if (networkProfiles[node.networkProfileId] === undefined) throw new Error(`nodes.${node.id}.networkProfile references missing profile ${node.networkProfileId}`); + if (downloadProfiles[node.downloadProfileId] === undefined) throw new Error(`nodes.${node.id}.downloadProfile references missing profile ${node.downloadProfileId}`); + } + for (const lane of Object.values(lanes)) { + if (nodes[lane.node] === undefined) throw new Error(`lanes.${lane.id}.node references missing node ${lane.node}`); + } + return { requiredNoProxy, nodes, lanes, networkProfiles, downloadProfiles }; +} + +const HWLAB_G14_LANE_CONFIG = readHwlabG14LaneConfig(); + +function buildRuntimeLaneSpec(config: HwlabLaneConfig): HwlabRuntimeLaneSpec { + const node = HWLAB_G14_LANE_CONFIG.nodes[config.node]; + const networkProfile = HWLAB_G14_LANE_CONFIG.networkProfiles[node.networkProfileId]; + const downloadProfile = HWLAB_G14_LANE_CONFIG.downloadProfiles[node.downloadProfileId]; + return { + lane: config.id, + nodeId: node.id, + nodeRoute: node.route, + nodeKubeRoute: node.kubeRoute, + gitopsRoot: node.gitopsRoot, + minor: config.minor, + version: config.version, + sourceBranch: config.sourceBranch, + workspace: config.workspace, + cicdRepo: config.cicdRepo, + cicdRepoLock: config.cicdRepoLock, + app: config.app, + pipeline: config.pipeline, + pipelineRunPrefix: config.pipelineRunPrefix, + serviceAccountName: config.serviceAccountName, + controlPlaneFieldManager: config.controlPlaneFieldManager, + gitUrl: config.git.url, + gitReadUrl: config.git.readUrl, + gitWriteUrl: config.git.writeUrl, + gitopsBranch: config.gitopsBranch, + catalogPath: config.catalogPath, + runtimePath: config.runtime.path, + runtimeNamespace: config.runtime.namespace, + runtimeRenderDir: config.runtime.renderDir, + tektonDir: config.tektonDir, + argoApplicationFile: config.argoApplicationFile, + registryPrefix: config.registryPrefix, + baseImage: config.baseImage, + serviceIds: config.serviceIds, + publicWebUrl: config.public.webUrl, + publicApiUrl: config.public.apiUrl, + networkProfileId: networkProfile.id, + downloadProfileId: downloadProfile.id, + networkProfile, + downloadProfile, }; } const RUNTIME_LANE_SPECS = Object.fromEntries( - HWLAB_RUNTIME_LANE_CONFIGS.map((config) => [config.lane, buildRuntimeLaneSpec(config)]), + Object.values(HWLAB_G14_LANE_CONFIG.lanes).map((config) => [config.id, buildRuntimeLaneSpec(config)]), ) as Record; export function isHwlabRuntimeLane(value: string): value is HwlabRuntimeLane { @@ -100,5 +370,17 @@ export function hwlabRuntimeLaneSpec(lane: HwlabRuntimeLane): HwlabRuntimeLaneSp } export function hwlabRuntimeLaneIds(): HwlabRuntimeLane[] { - return HWLAB_RUNTIME_LANE_CONFIGS.map((config) => config.lane); + return Object.keys(RUNTIME_LANE_SPECS) as HwlabRuntimeLane[]; +} + +export function hwlabRuntimeNodeIds(): string[] { + return Object.keys(HWLAB_G14_LANE_CONFIG.nodes); +} + +export function hwlabRuntimeLaneConfigPath(): string { + return HWLAB_G14_LANE_CONFIG_PATH; +} + +export function hwlabRequiredNoProxyEntries(): string[] { + return [...HWLAB_G14_LANE_CONFIG.requiredNoProxy]; } diff --git a/scripts/src/hwlab-g14.ts b/scripts/src/hwlab-g14.ts index 7a2c48c9..72771134 100644 --- a/scripts/src/hwlab-g14.ts +++ b/scripts/src/hwlab-g14.ts @@ -4,7 +4,7 @@ import { createHash, randomBytes } from "node:crypto"; import { repoRoot, rootPath, type Config } from "./config"; import { runCommand } from "./command"; import { readJob, startJob } from "./jobs"; -import { hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec } from "./hwlab-g14-lanes"; +import { hwlabRequiredNoProxyEntries, hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec } from "./hwlab-g14-lanes"; const HWLAB_REPO = "pikasTech/HWLAB"; const G14_SOURCE_BRANCH = "G14"; @@ -3208,7 +3208,7 @@ function deleteV02ObsoleteCronJobs(dryRun: boolean): CommandJsonResult { ], 60_000); } -function runtimeLanePipelineRunManifest(spec: HwlabRuntimeLaneSpec, sourceCommit: string): Record { +export function runtimeLanePipelineRunManifest(spec: HwlabRuntimeLaneSpec, sourceCommit: string): Record { const pipelineRun = runtimeLanePipelineRunName(spec, sourceCommit); return { apiVersion: "tekton.dev/v1", @@ -3225,6 +3225,10 @@ function runtimeLanePipelineRunManifest(spec: HwlabRuntimeLaneSpec, sourceCommit annotations: { "hwlab.pikastech.local/source-branch": spec.sourceBranch, "hwlab.pikastech.local/gitops-branch": spec.gitopsBranch, + "hwlab.pikastech.local/node": spec.nodeId, + "hwlab.pikastech.local/network-profile": spec.networkProfileId, + "hwlab.pikastech.local/download-profile": spec.downloadProfileId, + "hwlab.pikastech.local/no-proxy-required": hwlabRequiredNoProxyEntries().join(","), "hwlab.pikastech.local/triggered-by": "unidesk-cli", }, }, @@ -3607,6 +3611,9 @@ function runtimeLaneControlPlaneStatus(spec: HwlabRuntimeLaneSpec, target: V02Co sourceCommit, expected: { sourceRepo: spec.cicdRepo, + configPath: hwlabRuntimeLaneConfigPath(), + node: spec.nodeId, + nodeRoute: spec.nodeRoute, workspace: spec.workspace, branch: spec.sourceBranch, namespace: CI_NAMESPACE, @@ -3616,6 +3623,14 @@ function runtimeLaneControlPlaneStatus(spec: HwlabRuntimeLaneSpec, target: V02Co argoApplication: spec.app, gitopsBranch: spec.gitopsBranch, runtimePath: spec.runtimePath, + networkProfile: { + id: spec.networkProfileId, + httpProxy: spec.networkProfile.proxy.http, + noProxy: spec.networkProfile.proxy.noProxy.join(","), + dockerBuildHttpProxy: spec.networkProfile.dockerBuildProxy.http, + dockerBuildNoProxy: spec.networkProfile.dockerBuildProxy.noProxy.join(","), + }, + downloadProfile: spec.downloadProfile, }, sourceHead: { ok: shellSectionOk(sections.sourceHead), @@ -8470,6 +8485,7 @@ export function hwlabG14Help(): Record { repo: HWLAB_REPO, base: G14_SOURCE_BRANCH, v02Base: V02_SOURCE_BRANCH, + runtimeLaneConfig: hwlabRuntimeLaneConfigPath(), runtimeLanes: hwlabRuntimeLaneIds(), provider: G14_PROVIDER, workspace: G14_WORKSPACE, @@ -8480,6 +8496,10 @@ export function hwlabG14Help(): Record { devApplication: DEV_APP, v02Application: V02_APP, v03Application: hwlabRuntimeLaneSpec("v03").app, + v03Node: hwlabRuntimeLaneSpec("v03").nodeId, + v03NetworkProfile: hwlabRuntimeLaneSpec("v03").networkProfileId, + v03DownloadProfile: hwlabRuntimeLaneSpec("v03").downloadProfileId, + requiredNoProxy: hwlabRequiredNoProxyEntries(), briefIndexIssue: G14_BRIEF_INDEX_ISSUE, observabilityNamespace: G14_OBSERVABILITY_NAMESPACE, prometheusOperatorVersion: G14_PROMETHEUS_OPERATOR_VERSION,