From 2fe40c637a9163ff91c40ea03418d075e1b5356f Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 25 May 2026 08:59:01 +0800 Subject: [PATCH] feat: split G14 GitOps promotion branch --- .gitignore | 3 + deploy/gitops/g14/README.md | 10 - deploy/gitops/g14/argocd/application.yaml | 34 - deploy/gitops/g14/argocd/project.yaml | 32 - deploy/gitops/g14/registry/registry.yaml | 113 -- .../g14/runtime/code-agent-codex-config.yaml | 20 - .../gitops/g14/runtime/health-contract.yaml | 30 - deploy/gitops/g14/runtime/kustomization.yaml | 12 - deploy/gitops/g14/runtime/namespace.yaml | 16 - deploy/gitops/g14/runtime/services.yaml | 492 ------ deploy/gitops/g14/runtime/workloads.yaml | 1533 ----------------- deploy/gitops/g14/source.json | 11 - deploy/gitops/g14/tekton/pipeline.yaml | 326 ---- .../gitops/g14/tekton/pipelinerun.sample.yaml | 74 - deploy/gitops/g14/tekton/rbac.yaml | 93 - docs/reference/g14-gitops-cicd.md | 15 +- scripts/g14-gitops-render.mjs | 297 +++- 17 files changed, 311 insertions(+), 2800 deletions(-) delete mode 100644 deploy/gitops/g14/README.md delete mode 100644 deploy/gitops/g14/argocd/application.yaml delete mode 100644 deploy/gitops/g14/argocd/project.yaml delete mode 100644 deploy/gitops/g14/registry/registry.yaml delete mode 100644 deploy/gitops/g14/runtime/code-agent-codex-config.yaml delete mode 100644 deploy/gitops/g14/runtime/health-contract.yaml delete mode 100644 deploy/gitops/g14/runtime/kustomization.yaml delete mode 100644 deploy/gitops/g14/runtime/namespace.yaml delete mode 100644 deploy/gitops/g14/runtime/services.yaml delete mode 100644 deploy/gitops/g14/runtime/workloads.yaml delete mode 100644 deploy/gitops/g14/source.json delete mode 100644 deploy/gitops/g14/tekton/pipeline.yaml delete mode 100644 deploy/gitops/g14/tekton/pipelinerun.sample.yaml delete mode 100644 deploy/gitops/g14/tekton/rbac.yaml diff --git a/.gitignore b/.gitignore index 316c5f3a..9cf004b1 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ yarn-error.log* # Deploy/runtime state *.local.json .state/ + +# G14 generated GitOps desired state is stored on the G14-gitops branch. +/deploy/gitops/g14/ diff --git a/deploy/gitops/g14/README.md b/deploy/gitops/g14/README.md deleted file mode 100644 index 3be39ae6..00000000 --- a/deploy/gitops/g14/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# HWLAB G14 GitOps CI/CD - -This directory is generated from `CI.json`, `deploy/deploy.json`, and `deploy/k8s/*` by `scripts/g14-gitops-render.mjs`. - -- Target: G14 k3s only. -- CI: Tekton Pipeline `hwlab-ci/hwlab-g14-ci-image-publish`. -- Artifact contract: images are tagged by source git commit `bec4109` and pushed to `127.0.0.1:5000/hwlab`. -- CD: Argo CD Application `argocd/hwlab-g14-dev` consumes `deploy/gitops/g14/runtime`. -- D601 boundary: this path does not target D601, does not use UniDesk Code Queue, and does not change D601 production traffic. -- Concurrency: CI builds are per source commit and do not acquire the legacy DEV CD Lease; Argo CD reconciles the Git desired state. diff --git a/deploy/gitops/g14/argocd/application.yaml b/deploy/gitops/g14/argocd/application.yaml deleted file mode 100644 index 778fdb63..00000000 --- a/deploy/gitops/g14/argocd/application.yaml +++ /dev/null @@ -1,34 +0,0 @@ -{ - "apiVersion": "argoproj.io/v1alpha1", - "kind": "Application", - "metadata": { - "name": "hwlab-g14-dev", - "namespace": "argocd", - "labels": { - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/gitops-target": "g14" - } - }, - "spec": { - "project": "hwlab-g14", - "source": { - "repoURL": "git@github.com:pikasTech/HWLAB.git", - "targetRevision": "G14", - "path": "deploy/gitops/g14/runtime" - }, - "destination": { - "server": "https://kubernetes.default.svc", - "namespace": "hwlab-dev" - }, - "syncPolicy": { - "automated": { - "prune": false, - "selfHeal": true - }, - "syncOptions": [ - "CreateNamespace=true", - "ApplyOutOfSyncOnly=true" - ] - } - } -} diff --git a/deploy/gitops/g14/argocd/project.yaml b/deploy/gitops/g14/argocd/project.yaml deleted file mode 100644 index 1b2ec374..00000000 --- a/deploy/gitops/g14/argocd/project.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{ - "apiVersion": "argoproj.io/v1alpha1", - "kind": "AppProject", - "metadata": { - "name": "hwlab-g14", - "namespace": "argocd" - }, - "spec": { - "description": "HWLAB G14 GitOps project; D601 remains outside this project.", - "sourceRepos": [ - "git@github.com:pikasTech/HWLAB.git" - ], - "destinations": [ - { - "server": "https://kubernetes.default.svc", - "namespace": "hwlab-dev" - } - ], - "clusterResourceWhitelist": [ - { - "group": "", - "kind": "Namespace" - } - ], - "namespaceResourceWhitelist": [ - { - "group": "*", - "kind": "*" - } - ] - } -} diff --git a/deploy/gitops/g14/registry/registry.yaml b/deploy/gitops/g14/registry/registry.yaml deleted file mode 100644 index f29ffc9e..00000000 --- a/deploy/gitops/g14/registry/registry.yaml +++ /dev/null @@ -1,113 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "List", - "items": [ - { - "apiVersion": "v1", - "kind": "Namespace", - "metadata": { - "name": "hwlab-ci" - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-registry", - "namespace": "hwlab-ci", - "labels": { - "app.kubernetes.io/name": "hwlab-registry" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-registry" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-registry" - } - }, - "spec": { - "hostNetwork": true, - "dnsPolicy": "ClusterFirstWithHostNet", - "containers": [ - { - "name": "registry", - "image": "registry:2.8.3", - "imagePullPolicy": "IfNotPresent", - "ports": [ - { - "name": "registry", - "containerPort": 5000, - "hostPort": 5000 - } - ], - "env": [ - { - "name": "REGISTRY_STORAGE_DELETE_ENABLED", - "value": "true" - } - ], - "volumeMounts": [ - { - "name": "storage", - "mountPath": "/var/lib/registry" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/v2/", - "port": "registry" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/v2/", - "port": "registry" - } - } - } - ], - "volumes": [ - { - "name": "storage", - "hostPath": { - "path": "/var/lib/hwlab/registry", - "type": "DirectoryOrCreate" - } - } - ] - } - } - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-registry", - "namespace": "hwlab-ci", - "labels": { - "app.kubernetes.io/name": "hwlab-registry" - } - }, - "spec": { - "selector": { - "app.kubernetes.io/name": "hwlab-registry" - }, - "ports": [ - { - "name": "registry", - "port": 5000, - "targetPort": "registry" - } - ] - } - } - ] -} diff --git a/deploy/gitops/g14/runtime/code-agent-codex-config.yaml b/deploy/gitops/g14/runtime/code-agent-codex-config.yaml deleted file mode 100644 index 21e18f6f..00000000 --- a/deploy/gitops/g14/runtime/code-agent-codex-config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": { - "name": "hwlab-code-agent-codex-config", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-code-agent-codex-config", - "hwlab.pikastech.local/service-id": "hwlab-cloud-api", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "data": { - "config.toml": "model_provider = \"OpenAI\"\nmodel = \"gpt-5.4\"\nreview_model = \"gpt-5.4\"\nmodel_reasoning_effort = \"xhigh\"\ndisable_response_storage = true\nnetwork_access = \"enabled\"\nmodel_context_window = 1000000\nmodel_auto_compact_token_limit = 900000\n\n[model_providers.OpenAI]\nname = \"OpenAI\"\nbase_url = \"https://hyueapi.com\"\nwire_api = \"responses\"\nrequires_openai_auth = true\n\n[model_providers.crs]\nrequest_max_retries = 1000\nstream_max_retries = 1000\nname = \"crs\"\nbase_url = \"https://api.gptclubapi.xyz/openai\"\nwire_api = \"responses\"\nrequires_openai_auth = true\nenv_key = \"CRS_OAI_KEY\"\n" - } -} diff --git a/deploy/gitops/g14/runtime/health-contract.yaml b/deploy/gitops/g14/runtime/health-contract.yaml deleted file mode 100644 index 98869f1e..00000000 --- a/deploy/gitops/g14/runtime/health-contract.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": { - "name": "hwlab-dev-health-contract", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-dev-health-contract", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "data": { - "endpoint": "http://hwlab-edge-proxy.hwlab-dev.svc.cluster.local:6667", - "cloud-api": "GET /health/live through http://hwlab-edge-proxy.hwlab-dev.svc.cluster.local:6667", - "cloud-api-db": "DEV DB config gate requires HWLAB_CLOUD_DB_URL from Secret hwlab-cloud-api-dev-db/database-url and HWLAB_CLOUD_DB_SSL_MODE=disable. Runtime readiness dials the redacted host parsed from the Secret URL and reports endpointSource=secret-url-host. cloud-api-db is an optional desired alias only until this repo owns Service plus Endpoint or EndpointSlice manifests and rollout/apply contract; health reports env presence, env injection, redacted connection attempt/result, and liveConnected without exposing secret values", - "cloud-web": "GET /health/live on http://hwlab-cloud-web.hwlab-dev.svc.cluster.local:8080; consumes cloud-api only", - "agent": "hwlab-agent-mgr readiness gates worker template creation; hwlab-agent-worker is suspended until a session is scheduled", - "sim": "hwlab-gateway-simu:7101 and hwlab-box-simu:7201 expose /health/live for DEV smoke", - "patch-panel": "hwlab-patch-panel:7301 exposes /health/live and owns patch panel state", - "router": "hwlab-router:7401 exposes /health/live and owns edge reachability routing", - "tunnel-client": "disabled for G14 GitOps; no FRP or production traffic tunnel is required", - "edge-proxy": "hwlab-edge-proxy:6667 exposes /health/live and proxies DEV endpoint traffic to hwlab-cloud-api", - "runtime-substitute-policy": "Do not replace HWLAB runtime with UniDesk backend, provider-gateway, or microservice proxy" - } -} diff --git a/deploy/gitops/g14/runtime/kustomization.yaml b/deploy/gitops/g14/runtime/kustomization.yaml deleted file mode 100644 index c46bd528..00000000 --- a/deploy/gitops/g14/runtime/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{ - "apiVersion": "kustomize.config.k8s.io/v1beta1", - "kind": "Kustomization", - "namespace": "hwlab-dev", - "resources": [ - "namespace.yaml", - "code-agent-codex-config.yaml", - "services.yaml", - "health-contract.yaml", - "workloads.yaml" - ] -} diff --git a/deploy/gitops/g14/runtime/namespace.yaml b/deploy/gitops/g14/runtime/namespace.yaml deleted file mode 100644 index 4c49f088..00000000 --- a/deploy/gitops/g14/runtime/namespace.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Namespace", - "metadata": { - "name": "hwlab-dev", - "labels": { - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - } -} diff --git a/deploy/gitops/g14/runtime/services.yaml b/deploy/gitops/g14/runtime/services.yaml deleted file mode 100644 index b7288983..00000000 --- a/deploy/gitops/g14/runtime/services.yaml +++ /dev/null @@ -1,492 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "List", - "items": [ - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-cloud-api", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-cloud-api", - "hwlab.pikastech.local/service-id": "hwlab-cloud-api", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-cloud-api" - }, - "ports": [ - { - "name": "http", - "port": 6667, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-gateway-simu-1", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-gateway-simu", - "hwlab.pikastech.local/service-id": "hwlab-gateway-simu", - "hwlab.pikastech.local/instance-id": "gwsimu_1", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "statefulset.kubernetes.io/pod-name": "hwlab-gateway-simu-0" - }, - "ports": [ - { - "name": "http", - "port": 7101, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-gateway-simu-2", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-gateway-simu", - "hwlab.pikastech.local/service-id": "hwlab-gateway-simu", - "hwlab.pikastech.local/instance-id": "gwsimu_2", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "statefulset.kubernetes.io/pod-name": "hwlab-gateway-simu-1" - }, - "ports": [ - { - "name": "http", - "port": 7101, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-cloud-web", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-cloud-web", - "hwlab.pikastech.local/service-id": "hwlab-cloud-web", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-cloud-web" - }, - "ports": [ - { - "name": "http", - "port": 8080, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-box-simu-1", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-box-simu", - "hwlab.pikastech.local/service-id": "hwlab-box-simu", - "hwlab.pikastech.local/instance-id": "res_boxsimu_1", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "statefulset.kubernetes.io/pod-name": "hwlab-box-simu-0" - }, - "ports": [ - { - "name": "http", - "port": 7201, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-box-simu-2", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-box-simu", - "hwlab.pikastech.local/service-id": "hwlab-box-simu", - "hwlab.pikastech.local/instance-id": "res_boxsimu_2", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "statefulset.kubernetes.io/pod-name": "hwlab-box-simu-1" - }, - "ports": [ - { - "name": "http", - "port": 7201, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-gateway", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-gateway", - "hwlab.pikastech.local/service-id": "hwlab-gateway", - "hwlab.pikastech.local/activation": "manual", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-gateway" - }, - "ports": [ - { - "name": "http", - "port": 7001, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-cli", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-cli", - "hwlab.pikastech.local/service-id": "hwlab-cli", - "hwlab.pikastech.local/activation": "manual", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-cli" - }, - "ports": [ - { - "name": "http", - "port": 7501, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-agent-skills", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-agent-skills", - "hwlab.pikastech.local/service-id": "hwlab-agent-skills", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-agent-skills" - }, - "ports": [ - { - "name": "http", - "port": 7430, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-agent-mgr", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-agent-mgr", - "hwlab.pikastech.local/service-id": "hwlab-agent-mgr", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-agent-mgr" - }, - "ports": [ - { - "name": "http", - "port": 7410, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-gateway-simu", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-gateway-simu", - "hwlab.pikastech.local/service-id": "hwlab-gateway-simu", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-gateway-simu" - }, - "ports": [ - { - "name": "http", - "port": 7101, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-box-simu", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-box-simu", - "hwlab.pikastech.local/service-id": "hwlab-box-simu", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-box-simu" - }, - "ports": [ - { - "name": "http", - "port": 7201, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-patch-panel", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-patch-panel", - "hwlab.pikastech.local/service-id": "hwlab-patch-panel", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-patch-panel" - }, - "ports": [ - { - "name": "http", - "port": 7301, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-router", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-router", - "hwlab.pikastech.local/service-id": "hwlab-router", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-router" - }, - "ports": [ - { - "name": "http", - "port": 7401, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-tunnel-client", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-tunnel-client", - "hwlab.pikastech.local/service-id": "hwlab-tunnel-client", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-tunnel-client" - }, - "ports": [ - { - "name": "http", - "port": 7402, - "targetPort": "http" - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "name": "hwlab-edge-proxy", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-edge-proxy", - "hwlab.pikastech.local/service-id": "hwlab-edge-proxy", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "type": "ClusterIP", - "selector": { - "app.kubernetes.io/name": "hwlab-edge-proxy" - }, - "ports": [ - { - "name": "http", - "port": 6667, - "targetPort": "http" - } - ] - } - } - ] -} diff --git a/deploy/gitops/g14/runtime/workloads.yaml b/deploy/gitops/g14/runtime/workloads.yaml deleted file mode 100644 index c80cf67b..00000000 --- a/deploy/gitops/g14/runtime/workloads.yaml +++ /dev/null @@ -1,1533 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "List", - "items": [ - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-cloud-api", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-cloud-api", - "hwlab.pikastech.local/service-id": "hwlab-cloud-api", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-cloud-api", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-cloud-api", - "hwlab.pikastech.local/service-id": "hwlab-cloud-api", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-cloud-api", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 6667 - } - ], - "env": [ - { - "name": "HWLAB_ENVIRONMENT", - "value": "dev" - }, - { - "name": "HWLAB_PUBLIC_ENDPOINT", - "value": "http://hwlab-edge-proxy.hwlab-dev.svc.cluster.local:6667" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-cloud-api:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN", - "value": "unidesk-backend,provider-gateway,microservice-proxy" - }, - { - "name": "HWLAB_CLOUD_DB_URL", - "valueFrom": { - "secretKeyRef": { - "name": "hwlab-cloud-api-dev-db", - "key": "database-url", - "optional": true - } - } - }, - { - "name": "HWLAB_CLOUD_DB_SSL_MODE", - "value": "disable" - }, - { - "name": "HWLAB_CLOUD_DB_CONTRACT", - "value": "dev-redacted-presence-only" - }, - { - "name": "HWLAB_CLOUD_RUNTIME_ADAPTER", - "value": "postgres" - }, - { - "name": "HWLAB_CLOUD_RUNTIME_DURABLE", - "value": "true" - }, - { - "name": "HWLAB_M3_IO_CONTROL_ENABLED", - "value": "true" - }, - { - "name": "HWLAB_M3_GATEWAY_SIMU_1_URL", - "value": "http://hwlab-gateway-simu-1.hwlab-dev.svc.cluster.local:7101" - }, - { - "name": "HWLAB_M3_GATEWAY_SIMU_2_URL", - "value": "http://hwlab-gateway-simu-2.hwlab-dev.svc.cluster.local:7101" - }, - { - "name": "HWLAB_M3_PATCH_PANEL_URL", - "value": "http://hwlab-patch-panel.hwlab-dev.svc.cluster.local:7301" - }, - { - "name": "HWLAB_CODE_AGENT_PROVIDER", - "value": "codex-stdio" - }, - { - "name": "HWLAB_CODE_AGENT_MODEL", - "value": "gpt-5.5" - }, - { - "name": "HWLAB_CODE_AGENT_TIMEOUT_MS", - "value": "150000" - }, - { - "name": "HWLAB_CODE_AGENT_OPENAI_BASE_URL", - "value": "http://172.26.26.227:17680/v1/responses" - }, - { - "name": "HWLAB_CODE_AGENT_HWLAB_API_BASE_URL", - "value": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667" - }, - { - "name": "HWLAB_CODE_AGENT_CODEX_STDIO_ENABLED", - "value": "1" - }, - { - "name": "HWLAB_CODE_AGENT_CODEX_STDIO_SUPERVISOR", - "value": "repo-owned" - }, - { - "name": "HWLAB_CODE_AGENT_WORKSPACE", - "value": "/workspace/hwlab" - }, - { - "name": "HWLAB_CODE_AGENT_CODEX_WORKSPACE", - "value": "/workspace/hwlab" - }, - { - "name": "HWLAB_CODE_AGENT_CODEX_SANDBOX", - "value": "danger-full-access" - }, - { - "name": "HWLAB_CODE_AGENT_CODEX_COMMAND", - "value": "/app/node_modules/.bin/codex" - }, - { - "name": "CODEX_HOME", - "value": "/codex-home" - }, - { - "name": "NO_PROXY", - "value": "hyueapi.com,.hyueapi.com,hyui.com,.hyui.com,127.0.0.1,localhost,::1,api.minimaxi.com,.minimaxi.com" - }, - { - "name": "no_proxy", - "value": "hyueapi.com,.hyueapi.com,hyui.com,.hyui.com,127.0.0.1,localhost,::1,api.minimaxi.com,.minimaxi.com" - }, - { - "name": "HWLAB_CODE_AGENT_SKILLS_DIRS", - "value": "/app/skills:/root/.agents/skills:/home/ubuntu/.agents/skills" - }, - { - "name": "OPENAI_API_KEY", - "valueFrom": { - "secretKeyRef": { - "name": "hwlab-code-agent-provider", - "key": "openai-api-key", - "optional": true - } - } - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "volumeMounts": [ - { - "name": "hwlab-code-agent-workspace", - "mountPath": "/workspace" - }, - { - "name": "hwlab-code-agent-codex-home", - "mountPath": "/codex-home" - }, - { - "name": "hwlab-code-agent-codex-config", - "mountPath": "/codex-home/config.toml", - "subPath": "config.toml", - "readOnly": true - }, - { - "name": "hwlab-code-agent-codex-auth", - "mountPath": "/codex-home/auth.json", - "subPath": "auth.json", - "readOnly": true - } - ], - "imagePullPolicy": "IfNotPresent" - } - ], - "volumes": [ - { - "name": "hwlab-code-agent-workspace", - "emptyDir": {} - }, - { - "name": "hwlab-code-agent-codex-home", - "emptyDir": {} - }, - { - "name": "hwlab-code-agent-codex-config", - "configMap": { - "name": "hwlab-code-agent-codex-config", - "items": [ - { - "key": "config.toml", - "path": "config.toml" - } - ] - } - }, - { - "name": "hwlab-code-agent-codex-auth", - "secret": { - "secretName": "hwlab-code-agent-codex-auth", - "items": [ - { - "key": "auth.json", - "path": "auth.json" - } - ] - } - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-cloud-web", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-cloud-web", - "hwlab.pikastech.local/service-id": "hwlab-cloud-web", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-cloud-web", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-cloud-web", - "hwlab.pikastech.local/service-id": "hwlab-cloud-web", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-cloud-web", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 8080 - } - ], - "env": [ - { - "name": "HWLAB_API_BASE_URL", - "value": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667" - }, - { - "name": "HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS", - "value": "180000" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-cloud-web:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_PUBLIC_ENDPOINT", - "value": "http://hwlab-cloud-web.hwlab-dev.svc.cluster.local:8080" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-agent-mgr", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-agent-mgr", - "hwlab.pikastech.local/service-id": "hwlab-agent-mgr", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-agent-mgr", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-agent-mgr", - "hwlab.pikastech.local/service-id": "hwlab-agent-mgr", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-agent-mgr", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7410 - } - ], - "env": [ - { - "name": "HWLAB_AGENT_SESSION_MODE", - "value": "control-plane" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "hwlab-agent-worker-template", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-agent-worker", - "hwlab.pikastech.local/service-id": "hwlab-agent-worker", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "argocd.argoproj.io/sync-options": "Force=true,Replace=true" - } - }, - "spec": { - "suspend": true, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-agent-worker", - "hwlab.pikastech.local/service-id": "hwlab-agent-worker", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "restartPolicy": "Never", - "containers": [ - { - "name": "hwlab-agent-worker", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:bec4109", - "env": [ - { - "name": "HWLAB_AGENT_SESSION_MODE", - "value": "session-scoped" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-agent-worker:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-gateway", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-gateway", - "hwlab.pikastech.local/service-id": "hwlab-gateway", - "hwlab.pikastech.local/activation": "manual", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 0, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-gateway", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-gateway", - "hwlab.pikastech.local/service-id": "hwlab-gateway", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-gateway", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7001 - } - ], - "env": [ - { - "name": "HWLAB_GATEWAY_MODE", - "value": "hardware-boundary" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-gateway:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "StatefulSet", - "metadata": { - "name": "hwlab-gateway-simu", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-gateway-simu", - "hwlab.pikastech.local/service-id": "hwlab-gateway-simu", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "serviceName": "hwlab-gateway-simu", - "replicas": 2, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-gateway-simu", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-gateway-simu", - "hwlab.pikastech.local/service-id": "hwlab-gateway-simu", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-gateway-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7101 - } - ], - "env": [ - { - "name": "HWLAB_GATEWAY_ID", - "value": "gwsimu_1,gwsimu_2" - }, - { - "name": "HWLAB_BOX_IDS", - "value": "boxsimu_1,boxsimu_2" - }, - { - "name": "HWLAB_BOX_RESOURCES", - "value": "res_boxsimu_1,res_boxsimu_2" - }, - { - "name": "HWLAB_BOX_URLS", - "value": "http://hwlab-box-simu-1.hwlab-dev.svc.cluster.local:7201,http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "StatefulSet", - "metadata": { - "name": "hwlab-box-simu", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-box-simu", - "hwlab.pikastech.local/service-id": "hwlab-box-simu", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "serviceName": "hwlab-box-simu", - "replicas": 2, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-box-simu", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-box-simu", - "hwlab.pikastech.local/service-id": "hwlab-box-simu", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-box-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7201 - } - ], - "env": [ - { - "name": "HWLAB_BOX_ID", - "value": "boxsimu_1,boxsimu_2" - }, - { - "name": "HWLAB_BOX_RESOURCE_ID", - "value": "res_boxsimu_1,res_boxsimu_2" - }, - { - "name": "HWLAB_GATEWAY_SESSION_ID", - "value": "gws_gwsimu_1,gws_gwsimu_2" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-box-simu:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-patch-panel", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-patch-panel", - "hwlab.pikastech.local/service-id": "hwlab-patch-panel", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-patch-panel", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-patch-panel", - "hwlab.pikastech.local/service-id": "hwlab-patch-panel", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-patch-panel", - "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7301 - } - ], - "env": [ - { - "name": "HWLAB_PATCH_PANEL_ENDPOINT_MAP", - "value": "{\"res_boxsimu_2\":\"http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201\"}" - }, - { - "name": "HWLAB_PATCH_PANEL_WIRING_CONFIG", - "value": "{\"wiringConfigId\":\"wir_m3_do1_di1\",\"projectId\":\"prj_m3_hardware_loop\",\"gatewaySessionId\":\"gws_gwsimu_1\",\"name\":\"M3 simulated DO1 to DI1 patch wiring\",\"status\":\"active\",\"connections\":[{\"from\":{\"resourceId\":\"res_boxsimu_1\",\"port\":\"DO1\"},\"to\":{\"resourceId\":\"res_boxsimu_2\",\"port\":\"DI1\"},\"mode\":\"exclusive\"}],\"constraints\":{\"propagation\":\"patch-panel-only\",\"localLoopbackSubstituteAllowed\":false},\"createdAt\":\"2026-05-21T00:00:00.000Z\",\"updatedAt\":\"2026-05-21T00:00:00.000Z\"}" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-patch-panel:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-router", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-router", - "hwlab.pikastech.local/service-id": "hwlab-router", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-router", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-router", - "hwlab.pikastech.local/service-id": "hwlab-router", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-router", - "image": "127.0.0.1:5000/hwlab/hwlab-router:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7401 - } - ], - "env": [ - { - "name": "HWLAB_EDGE_PROXY_SERVICE", - "value": "hwlab-edge-proxy" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-router:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-tunnel-client", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-tunnel-client", - "hwlab.pikastech.local/service-id": "hwlab-tunnel-client", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 0, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-tunnel-client", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-tunnel-client", - "hwlab.pikastech.local/service-id": "hwlab-tunnel-client", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-tunnel-client", - "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7402 - } - ], - "env": [ - { - "name": "HWLAB_TUNNEL_MODE", - "value": "disabled-g14-gitops" - }, - { - "name": "HWLAB_FRP_SERVER_ADDR", - "value": "" - }, - { - "name": "HWLAB_FRP_PUBLIC_PORT", - "value": "0" - }, - { - "name": "HWLAB_FRP_WEB_PUBLIC_PORT", - "value": "0" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-edge-proxy", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-edge-proxy", - "hwlab.pikastech.local/service-id": "hwlab-edge-proxy", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-edge-proxy", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-edge-proxy", - "hwlab.pikastech.local/service-id": "hwlab-edge-proxy", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-edge-proxy", - "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 6667 - } - ], - "env": [ - { - "name": "HWLAB_EDGE_LISTEN", - "value": "0.0.0.0:6667" - }, - { - "name": "HWLAB_EDGE_UPSTREAM", - "value": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667" - }, - { - "name": "HWLAB_EDGE_PROXY_TIMEOUT_MS", - "value": "180000" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_PUBLIC_ENDPOINT", - "value": "http://hwlab-edge-proxy.hwlab-dev.svc.cluster.local:6667" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "hwlab-cli-template", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-cli", - "hwlab.pikastech.local/service-id": "hwlab-cli", - "hwlab.pikastech.local/activation": "manual", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "argocd.argoproj.io/sync-options": "Force=true,Replace=true" - } - }, - "spec": { - "suspend": true, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-cli", - "hwlab.pikastech.local/service-id": "hwlab-cli", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "restartPolicy": "Never", - "containers": [ - { - "name": "hwlab-cli", - "image": "127.0.0.1:5000/hwlab/hwlab-cli:bec4109", - "env": [ - { - "name": "HWLAB_CLI_ENDPOINT", - "value": "http://hwlab-edge-proxy.hwlab-dev.svc.cluster.local:6667" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-cli:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - }, - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hwlab-agent-skills", - "namespace": "hwlab-dev", - "labels": { - "app.kubernetes.io/name": "hwlab-agent-skills", - "hwlab.pikastech.local/service-id": "hwlab-agent-skills", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "unidesk.ai/g14-staging": "true" - }, - "annotations": { - "hwlab.pikastech.local/gitops-note": "G14 GitOps target only; production traffic remains outside this target.", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app.kubernetes.io/name": "hwlab-agent-skills", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev" - } - }, - "template": { - "metadata": { - "labels": { - "app.kubernetes.io/name": "hwlab-agent-skills", - "hwlab.pikastech.local/service-id": "hwlab-agent-skills", - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/environment": "dev", - "hwlab.pikastech.local/profile": "dev", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - }, - "annotations": { - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584", - "hwlab.pikastech.local/rendered-by": "scripts/g14-gitops-render.mjs" - } - }, - "spec": { - "containers": [ - { - "name": "hwlab-agent-skills", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:bec4109", - "ports": [ - { - "name": "http", - "containerPort": 7430 - } - ], - "env": [ - { - "name": "HWLAB_SKILLS_COMMIT_ID", - "value": "4d478a1" - }, - { - "name": "HWLAB_COMMIT_ID", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-agent-skills:bec4109" - }, - { - "name": "HWLAB_IMAGE_TAG", - "value": "bec4109" - }, - { - "name": "HWLAB_GITOPS_TARGET", - "value": "g14" - }, - { - "name": "HWLAB_GITOPS_SOURCE_COMMIT", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - } - ], - "readinessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "livenessProbe": { - "httpGet": { - "path": "/health/live", - "port": "http" - } - }, - "imagePullPolicy": "IfNotPresent" - } - ] - } - } - } - } - ] -} diff --git a/deploy/gitops/g14/source.json b/deploy/gitops/g14/source.json deleted file mode 100644 index e9a69f97..00000000 --- a/deploy/gitops/g14/source.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "kind": "hwlab-g14-gitops-source", - "sourceCommit": "bec4109af1bddd606683821c60186c0c89f72584", - "sourceShortCommit": "bec4109", - "sourceBranch": "G14", - "sourceRepo": "git@github.com:pikasTech/HWLAB.git", - "registryPrefix": "127.0.0.1:5000/hwlab", - "runtimePath": "deploy/gitops/g14/runtime", - "tektonPipeline": "hwlab-ci/hwlab-g14-ci-image-publish", - "argoApplication": "argocd/hwlab-g14-dev" -} diff --git a/deploy/gitops/g14/tekton/pipeline.yaml b/deploy/gitops/g14/tekton/pipeline.yaml deleted file mode 100644 index 28f68cce..00000000 --- a/deploy/gitops/g14/tekton/pipeline.yaml +++ /dev/null @@ -1,326 +0,0 @@ -{ - "apiVersion": "tekton.dev/v1", - "kind": "Pipeline", - "metadata": { - "name": "hwlab-g14-ci-image-publish", - "namespace": "hwlab-ci", - "labels": { - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/gitops-target": "g14" - }, - "annotations": { - "hwlab.pikastech.local/source-config": "CI.json", - "hwlab.pikastech.local/policy": "parallel-no-lock-ci-image-publish" - } - }, - "spec": { - "params": [ - { - "name": "git-url", - "type": "string", - "default": "git@github.com:pikasTech/HWLAB.git" - }, - { - "name": "source-branch", - "type": "string", - "default": "G14" - }, - { - "name": "revision", - "type": "string", - "description": "Full git commit SHA; the only source truth for image tags." - }, - { - "name": "registry-prefix", - "type": "string", - "default": "127.0.0.1:5000/hwlab" - }, - { - "name": "services", - "type": "string", - "default": "hwlab-cloud-api,hwlab-cloud-web,hwlab-agent-mgr,hwlab-agent-worker,hwlab-gateway,hwlab-gateway-simu,hwlab-box-simu,hwlab-patch-panel,hwlab-router,hwlab-tunnel-client,hwlab-edge-proxy,hwlab-cli,hwlab-agent-skills" - }, - { - "name": "base-image", - "type": "string", - "default": "node:20-bookworm-slim" - }, - { - "name": "concurrency", - "type": "string", - "default": "4" - } - ], - "workspaces": [ - { - "name": "source" - }, - { - "name": "git-ssh" - } - ], - "tasks": [ - { - "name": "ci-json", - "workspaces": [ - { - "name": "source", - "workspace": "source" - }, - { - "name": "git-ssh", - "workspace": "git-ssh" - } - ], - "taskSpec": { - "params": [ - { - "name": "git-url" - }, - { - "name": "source-branch" - }, - { - "name": "revision" - } - ], - "workspaces": [ - { - "name": "source" - }, - { - "name": "git-ssh" - } - ], - "steps": [ - { - "name": "ci-json", - "image": "node:22-bookworm-slim", - "env": [ - { - "name": "HTTP_PROXY", - "value": "http://127.0.0.1:10808" - }, - { - "name": "HTTPS_PROXY", - "value": "http://127.0.0.1:10808" - }, - { - "name": "ALL_PROXY", - "value": "socks5h://127.0.0.1:10808" - }, - { - "name": "NO_PROXY", - "value": "127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc" - }, - { - "name": "http_proxy", - "value": "http://127.0.0.1:10808" - }, - { - "name": "https_proxy", - "value": "http://127.0.0.1:10808" - }, - { - "name": "all_proxy", - "value": "socks5h://127.0.0.1:10808" - }, - { - "name": "no_proxy", - "value": "127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc" - } - ], - "script": "#!/bin/sh\nset -eu\nif ! command -v git >/dev/null 2>&1 || ! command -v python3 >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then\n export DEBIAN_FRONTEND=noninteractive\n apt-get update\n apt-get install -y --no-install-recommends git python3 openssh-client ca-certificates\n rm -rf /var/lib/apt/lists/*\nfi\nmkdir -p /root/.ssh\ncp /workspace/git-ssh/ssh-privatekey /root/.ssh/id_rsa\nchmod 600 /root/.ssh/id_rsa\nssh-keyscan github.com >> /root/.ssh/known_hosts 2>/dev/null\nrm -rf /workspace/source/repo\ngit clone --branch \"$(params.source-branch)\" \"$(params.git-url)\" /workspace/source/repo\ncd /workspace/source/repo\ngit checkout \"$(params.revision)\"\ntest \"$(git rev-parse HEAD)\" = \"$(params.revision)\"\nnpm ci --ignore-scripts\nnpx playwright install --with-deps chromium\necho 'CI.json hwlab-deploy-skeleton-ci command count=6'\necho 'CI.json command: repo-reports-guard'\nnode scripts/repo-reports-guard.mjs\necho 'CI.json command: contract-check'\nnpm run check\necho 'CI.json command: dev-artifact-publish-static-check'\nnode --check scripts/dev-artifact-publish.mjs\necho 'CI.json command: parse-deploy-json'\nnode -e \"JSON.parse(require('node:fs').readFileSync('deploy/deploy.json','utf8'))\"\necho 'CI.json command: parse-k8s-json-yaml'\nnode -e \"const fs=require('node:fs'); for (const f of fs.readdirSync('deploy/k8s/base').concat(fs.readdirSync('deploy/k8s/dev').map(x=>'../dev/'+x), fs.readdirSync('deploy/k8s/prod').map(x=>'../prod/'+x))) { if (!f.endsWith('.yaml')) continue; const p=f.startsWith('..')?'deploy/k8s/'+f.replace('../',''):'deploy/k8s/base/'+f; JSON.parse(fs.readFileSync(p,'utf8')); }\"\necho 'CI.json command: parse-frp-toml'\npython3 - <<'PY'\nimport pathlib, tomllib\nfor path in pathlib.Path('deploy/frp').glob('*.toml'):\n tomllib.loads(path.read_text())\nPY\n" - } - ] - }, - "params": [ - { - "name": "git-url", - "value": "$(params.git-url)" - }, - { - "name": "source-branch", - "value": "$(params.source-branch)" - }, - { - "name": "revision", - "value": "$(params.revision)" - } - ] - }, - { - "name": "image-publish", - "runAfter": [ - "ci-json" - ], - "workspaces": [ - { - "name": "source", - "workspace": "source" - } - ], - "taskSpec": { - "params": [ - { - "name": "revision" - }, - { - "name": "registry-prefix" - }, - { - "name": "services" - }, - { - "name": "base-image" - }, - { - "name": "concurrency" - } - ], - "workspaces": [ - { - "name": "source" - } - ], - "volumes": [ - { - "name": "docker-run", - "emptyDir": {} - } - ], - "sidecars": [ - { - "name": "docker", - "image": "docker:29-dind", - "args": [ - "--host=unix:///var/run/docker.sock", - "--storage-driver=vfs" - ], - "env": [ - { - "name": "DOCKER_TLS_CERTDIR", - "value": "" - }, - { - "name": "HTTP_PROXY", - "value": "http://127.0.0.1:10808" - }, - { - "name": "HTTPS_PROXY", - "value": "http://127.0.0.1:10808" - }, - { - "name": "ALL_PROXY", - "value": "socks5h://127.0.0.1:10808" - }, - { - "name": "NO_PROXY", - "value": "127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc" - }, - { - "name": "http_proxy", - "value": "http://127.0.0.1:10808" - }, - { - "name": "https_proxy", - "value": "http://127.0.0.1:10808" - }, - { - "name": "all_proxy", - "value": "socks5h://127.0.0.1:10808" - }, - { - "name": "no_proxy", - "value": "127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc" - } - ], - "securityContext": { - "privileged": true - }, - "volumeMounts": [ - { - "name": "docker-run", - "mountPath": "/var/run" - } - ] - } - ], - "steps": [ - { - "name": "publish", - "image": "node:22-alpine", - "env": [ - { - "name": "DOCKER_HOST", - "value": "unix:///var/run/docker.sock" - }, - { - "name": "HTTP_PROXY", - "value": "http://127.0.0.1:10808" - }, - { - "name": "HTTPS_PROXY", - "value": "http://127.0.0.1:10808" - }, - { - "name": "ALL_PROXY", - "value": "socks5h://127.0.0.1:10808" - }, - { - "name": "NO_PROXY", - "value": "127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc" - }, - { - "name": "http_proxy", - "value": "http://127.0.0.1:10808" - }, - { - "name": "https_proxy", - "value": "http://127.0.0.1:10808" - }, - { - "name": "all_proxy", - "value": "socks5h://127.0.0.1:10808" - }, - { - "name": "no_proxy", - "value": "127.0.0.1,localhost,::1,10.0.0.0/8,10.42.0.0/16,10.43.0.0/16,192.168.0.0/16,.svc,.cluster.local,kubernetes.default.svc,registry.hwlab-ci.svc,hwlab-registry.hwlab-ci.svc" - } - ], - "volumeMounts": [ - { - "name": "docker-run", - "mountPath": "/var/run" - } - ], - "script": "#!/bin/sh\nset -eu\napk add --no-cache docker-cli git openssh-client python3\nexport DOCKER_HOST=unix:///var/run/docker.sock\nexport HWLAB_CI_ARTIFACT_RUN_ID=\"$(context.pipelineRun.name)\"\nexport HWLAB_CI_ARTIFACT_RUN_OWNER=\"tekton\"\nexport HWLAB_DEV_REGISTRY_PREFIX=\"$(params.registry-prefix)\"\nexport HWLAB_DEV_REGISTRY_K8S_NATIVE=1\nif [ -n \"$(params.base-image)\" ]; then export HWLAB_DEV_BASE_IMAGE=\"$(params.base-image)\"; fi\nfor i in $(seq 1 90); do docker info >/dev/null 2>&1 && break; sleep 2; done\ndocker info >/dev/null\nif [ -n \"${HWLAB_DEV_BASE_IMAGE:-}\" ]; then\n docker pull \"$HWLAB_DEV_BASE_IMAGE\"\nfi\ncd /workspace/source/repo\ntest \"$(git rev-parse HEAD)\" = \"$(params.revision)\"\nnode scripts/dev-artifact-publish.mjs --publish --registry-prefix \"$(params.registry-prefix)\" --report /workspace/source/dev-artifacts.json --quiet-build --concurrency \"$(params.concurrency)\" --services \"$(params.services)\"\nnode scripts/refresh-artifact-catalog.mjs --target-ref HEAD --publish-report /workspace/source/dev-artifacts.json --no-write\n" - } - ] - }, - "params": [ - { - "name": "revision", - "value": "$(params.revision)" - }, - { - "name": "registry-prefix", - "value": "$(params.registry-prefix)" - }, - { - "name": "services", - "value": "$(params.services)" - }, - { - "name": "base-image", - "value": "$(params.base-image)" - }, - { - "name": "concurrency", - "value": "$(params.concurrency)" - } - ] - } - ] - } -} diff --git a/deploy/gitops/g14/tekton/pipelinerun.sample.yaml b/deploy/gitops/g14/tekton/pipelinerun.sample.yaml deleted file mode 100644 index d191612d..00000000 --- a/deploy/gitops/g14/tekton/pipelinerun.sample.yaml +++ /dev/null @@ -1,74 +0,0 @@ -{ - "apiVersion": "tekton.dev/v1", - "kind": "PipelineRun", - "metadata": { - "generateName": "hwlab-g14-ci-", - "namespace": "hwlab-ci", - "labels": { - "app.kubernetes.io/part-of": "hwlab", - "hwlab.pikastech.local/gitops-target": "g14", - "hwlab.pikastech.local/source-commit": "bec4109af1bddd606683821c60186c0c89f72584" - } - }, - "spec": { - "pipelineRef": { - "name": "hwlab-g14-ci-image-publish" - }, - "taskRunTemplate": { - "serviceAccountName": "hwlab-tekton-runner", - "podTemplate": { - "hostNetwork": true, - "dnsPolicy": "ClusterFirstWithHostNet" - } - }, - "params": [ - { - "name": "git-url", - "value": "git@github.com:pikasTech/HWLAB.git" - }, - { - "name": "source-branch", - "value": "G14" - }, - { - "name": "revision", - "value": "bec4109af1bddd606683821c60186c0c89f72584" - }, - { - "name": "registry-prefix", - "value": "127.0.0.1:5000/hwlab" - }, - { - "name": "base-image", - "value": "node:20-bookworm-slim" - }, - { - "name": "concurrency", - "value": "4" - } - ], - "workspaces": [ - { - "name": "source", - "volumeClaimTemplate": { - "spec": { - "accessModes": [ - "ReadWriteOnce" - ], - "resources": { - "requests": { - "storage": "8Gi" - } - } - } - } - }, - { - "name": "git-ssh", - "secret": { - "secretName": "hwlab-git-ssh" - } - } - ] - } -} diff --git a/deploy/gitops/g14/tekton/rbac.yaml b/deploy/gitops/g14/tekton/rbac.yaml deleted file mode 100644 index 4e2cd800..00000000 --- a/deploy/gitops/g14/tekton/rbac.yaml +++ /dev/null @@ -1,93 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "List", - "items": [ - { - "apiVersion": "v1", - "kind": "Namespace", - "metadata": { - "name": "hwlab-ci" - } - }, - { - "apiVersion": "v1", - "kind": "ServiceAccount", - "metadata": { - "name": "hwlab-tekton-runner", - "namespace": "hwlab-ci" - } - }, - { - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "Role", - "metadata": { - "name": "hwlab-tekton-runtime-observer", - "namespace": "hwlab-dev" - }, - "rules": [ - { - "apiGroups": [ - "" - ], - "resources": [ - "pods", - "services", - "configmaps" - ], - "verbs": [ - "get", - "list", - "watch" - ] - }, - { - "apiGroups": [ - "apps" - ], - "resources": [ - "deployments", - "statefulsets" - ], - "verbs": [ - "get", - "list", - "watch" - ] - }, - { - "apiGroups": [ - "batch" - ], - "resources": [ - "jobs" - ], - "verbs": [ - "get", - "list", - "watch" - ] - } - ] - }, - { - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "RoleBinding", - "metadata": { - "name": "hwlab-tekton-runtime-observer", - "namespace": "hwlab-dev" - }, - "subjects": [ - { - "kind": "ServiceAccount", - "name": "hwlab-tekton-runner", - "namespace": "hwlab-ci" - } - ], - "roleRef": { - "apiGroup": "rbac.authorization.k8s.io", - "kind": "Role", - "name": "hwlab-tekton-runtime-observer" - } - } - ] -} diff --git a/docs/reference/g14-gitops-cicd.md b/docs/reference/g14-gitops-cicd.md index bb93a9af..ffc8ddd8 100644 --- a/docs/reference/g14-gitops-cicd.md +++ b/docs/reference/g14-gitops-cicd.md @@ -7,7 +7,8 @@ G14 是 HWLAB 的旁路 DEV 集群目标。G14 CI/CD 必须只作用于 G14 k3s - Source of truth:业务版本以 Git source commit 为唯一身份;镜像 tag、OCI labels、runtime annotation 和 Argo CD desired state 都必须记录同一个 source commit。 - CI:Tekton 在 G14 k3s 内运行 `hwlab-g14-ci-image-publish` Pipeline,读取 `CI.json` 命令,按 source commit 构建镜像并推送到 G14 registry。 - Artifact:镜像使用 commit tag,例如 `127.0.0.1:5000/hwlab/hwlab-cloud-api:`;digest 由 registry 返回,CI report 只作为审计证据,不作为 CD 真相。 -- CD:Argo CD 只消费 `deploy/gitops/g14/runtime` 的 Git desired state,不重新构建镜像,不读取 D601 状态,不获取 legacy DEV CD Lease。 +- Branch split:`G14` 是源码监控分支;`G14-gitops` 是 Tekton promotion 写入的生成分支,保存 `deploy/gitops/g14/**` desired state。 +- CD:Argo CD 只消费 `G14-gitops:deploy/gitops/g14/runtime` 的 Git desired state,不重新构建镜像,不读取 D601 状态,不获取 legacy DEV CD Lease。 - 并行性:不同 source commit 的 CI build 不共享发布锁;并行安全由 immutable commit tag/digest 和 Git desired state 保证。最终运行版本由 Argo CD 当前同步的 Git revision 决定。 ## 生成入口 @@ -16,7 +17,9 @@ G14 是 HWLAB 的旁路 DEV 集群目标。G14 CI/CD 必须只作用于 G14 k3s - 读取 `CI.json`,生成 Tekton Pipeline 和 PipelineRun 样板。 - 读取 `deploy/deploy.json` 与 `deploy/k8s/*`,生成 Argo CD 可消费的 G14 runtime Kustomize path。 +- 生成 `hwlab-g14-branch-poller` CronJob:它使用 G14 集群内的 Git SSH Secret 轮询 `G14` 分支,按 source commit 创建确定命名的 Tekton PipelineRun。 - 默认输出到 `deploy/gitops/g14/`。 +- 默认 GitOps 生成分支是 `G14-gitops`;Pipeline 成功后把本次 source commit 对应的 `deploy/gitops/g14/**` 推送到该分支,避免把生成提交继续写回 `G14`。 - 默认 registry prefix 是 `127.0.0.1:5000/hwlab`,用于 G14 单节点 k3s 的 node-local registry。 - 默认 CI/CD proxy 是 G14 本机 `http://127.0.0.1:10808` / `socks5h://127.0.0.1:10808`。Tekton CI step、Docker-in-Docker sidecar 和 publish step 都注入 proxy/no_proxy;publish step 必须先在 DIND 内 `docker pull $HWLAB_DEV_BASE_IMAGE`,再进入 DEV base-image preflight、build 和 registry push,保证 npm、Playwright Chromium 安装、base image 拉取和 registry push 走 G14 本机代理边界。CI step 固定使用 Node 22 Debian 镜像,避免 Playwright 官方镜像内 Node 版本漂移影响 `node --test`。 @@ -27,9 +30,17 @@ npm run g14:gitops:render -- --source-revision npm run g14:gitops:check ``` +## Polling 触发 + +G14 不要求 GitHub webhook 或 GitHub Actions 配置。`hwlab-ci/hwlab-g14-branch-poller` CronJob 每 5 分钟通过 Git SSH 拉取 `G14` HEAD,并用 source commit 的前 12 位生成 PipelineRun 名称 `hwlab-g14-ci-poll-`。 + +如果同名 PipelineRun 已存在,poller 直接跳过;如果不存在,则创建新的 PipelineRun。这样可以用 Kubernetes 原生 CronJob、ServiceAccount、RBAC 和 Tekton API 实现无 GitHub webhook 的分支监控,同时避免同一个 commit 被反复派单。历史上的 `chore: promote G14 GitOps source ...` 生成提交会被 poller 跳过。 + +Pipeline 的标准路径是:`G14` source commit -> `CI.json` Tekton 测试 -> commit-tagged image push 到 G14 registry -> render `deploy/gitops/g14/**` -> push `G14-gitops` -> Argo CD 同步 runtime。CD 只消费已经构建好的镜像和 Git desired state,不在 Argo CD 内构建镜像。 + ## 集群资源 -- `hwlab-ci`:registry、Tekton runner RBAC、Pipeline/PipelineRun 所在 namespace。 +- `hwlab-ci`:registry、Tekton runner RBAC、Polling CronJob、Pipeline/PipelineRun 所在 namespace。 - `argocd`:Argo CD 控制面和 `hwlab-g14-dev` Application 所在 namespace。 - `hwlab-dev`:HWLAB runtime namespace,由 Argo CD 应用 `deploy/gitops/g14/runtime`。 diff --git a/scripts/g14-gitops-render.mjs b/scripts/g14-gitops-render.mjs index 8a823270..bb02837d 100644 --- a/scripts/g14-gitops-render.mjs +++ b/scripts/g14-gitops-render.mjs @@ -12,6 +12,7 @@ const defaultOutDir = "deploy/gitops/g14"; const defaultRegistryPrefix = process.env.HWLAB_G14_REGISTRY_PREFIX || "127.0.0.1:5000/hwlab"; const defaultSourceRepo = process.env.HWLAB_G14_GIT_URL || "git@github.com:pikasTech/HWLAB.git"; const defaultBranch = process.env.HWLAB_G14_BRANCH || "G14"; +const defaultGitopsBranch = process.env.HWLAB_G14_GITOPS_BRANCH || "G14-gitops"; const defaultRuntimeEndpoint = "http://hwlab-edge-proxy.hwlab-dev.svc.cluster.local:6667"; const defaultWebEndpoint = "http://hwlab-cloud-web.hwlab-dev.svc.cluster.local:8080"; const defaultProxyUrl = process.env.HWLAB_G14_PROXY_URL || "http://127.0.0.1:10808"; @@ -38,6 +39,7 @@ function parseArgs(argv) { registryPrefix: defaultRegistryPrefix, sourceRevision: null, sourceBranch: defaultBranch, + gitopsBranch: defaultGitopsBranch, sourceRepo: defaultSourceRepo, runtimeEndpoint: defaultRuntimeEndpoint, webEndpoint: defaultWebEndpoint, @@ -51,6 +53,7 @@ function parseArgs(argv) { else if (arg === "--registry-prefix") args.registryPrefix = readOption(argv, ++index, arg).replace(/\/+$/u, ""); else if (arg === "--source-revision") args.sourceRevision = readOption(argv, ++index, arg); else if (arg === "--source-branch") args.sourceBranch = readOption(argv, ++index, arg); + else if (arg === "--gitops-branch") args.gitopsBranch = readOption(argv, ++index, arg); else if (arg === "--source-repo") args.sourceRepo = readOption(argv, ++index, arg); else if (arg === "--runtime-endpoint") args.runtimeEndpoint = readOption(argv, ++index, arg); else if (arg === "--web-endpoint") args.webEndpoint = readOption(argv, ++index, arg); @@ -80,6 +83,7 @@ function usage() { ` --out DIR default: ${defaultOutDir}`, ` --source-repo URL default: ${defaultSourceRepo}`, ` --source-branch BRANCH default: ${defaultBranch}`, + ` --gitops-branch BRANCH default: ${defaultGitopsBranch}`, " --source-revision SHA source commit used for image tags and runtime annotations; default: git rev-parse HEAD", ` --registry-prefix PREFIX default: ${defaultRegistryPrefix}`, ` --runtime-endpoint URL default: ${defaultRuntimeEndpoint}`, @@ -344,6 +348,184 @@ node scripts/refresh-artifact-catalog.mjs --target-ref HEAD --publish-report /wo `; } +function gitopsPromoteScript() { + return `#!/bin/sh +set -eu +if ! command -v git >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y --no-install-recommends git openssh-client ca-certificates + rm -rf /var/lib/apt/lists/* +fi +mkdir -p /root/.ssh +cp /workspace/git-ssh/ssh-privatekey /root/.ssh/id_rsa +chmod 600 /root/.ssh/id_rsa +ssh-keyscan github.com >> /root/.ssh/known_hosts 2>/dev/null || true +export GIT_SSH_COMMAND="ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new" +cd /workspace/source/repo +test "$(git rev-parse HEAD)" = "$(params.revision)" +node scripts/g14-gitops-render.mjs --source-revision "$(params.revision)" --source-repo "$(params.git-url)" --source-branch "$(params.source-branch)" --gitops-branch "$(params.gitops-branch)" --registry-prefix "$(params.registry-prefix)" +node scripts/g14-gitops-render.mjs --source-revision "$(params.revision)" --source-repo "$(params.git-url)" --source-branch "$(params.source-branch)" --gitops-branch "$(params.gitops-branch)" --registry-prefix "$(params.registry-prefix)" --check +git config --global user.name "HWLAB G14 GitOps Bot" +git config --global user.email "hwlab-g14-gitops-bot@users.noreply.github.com" +workdir="$(mktemp -d)" +git clone --no-checkout "$(params.git-url)" "$workdir/gitops" +cd "$workdir/gitops" +if git ls-remote --exit-code --heads origin "$(params.gitops-branch)" >/dev/null 2>&1; then + git fetch origin "$(params.gitops-branch)" + git checkout -B "$(params.gitops-branch)" "origin/$(params.gitops-branch)" + rm -rf deploy/gitops/g14 +else + git checkout --orphan "$(params.gitops-branch)" + git rm -rf . >/dev/null 2>&1 || true +fi +mkdir -p deploy/gitops +cp -a /workspace/source/repo/deploy/gitops/g14 deploy/gitops/g14 +git add deploy/gitops/g14 +if git diff --cached --quiet; then + echo '{"status":"unchanged","gitopsBranch":"'"$(params.gitops-branch)"'","sourceRevision":"'"$(params.revision)"'"}' + exit 0 +fi +short="$(printf '%.7s' "$(params.revision)")" +git commit -m "chore: promote G14 GitOps source $short" +git push origin "HEAD:$(params.gitops-branch)" +echo '{"status":"pushed","gitopsBranch":"'"$(params.gitops-branch)"'","sourceRevision":"'"$(params.revision)"'"}' +`; +} + +function pollerScript() { + return `#!/bin/sh +set -eu +apk add --no-cache git openssh-client ca-certificates +mkdir -p /root/.ssh +cp /workspace/git-ssh/ssh-privatekey /root/.ssh/id_rsa +chmod 600 /root/.ssh/id_rsa +ssh-keyscan github.com >> /root/.ssh/known_hosts 2>/dev/null || true +export GIT_SSH_COMMAND="ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new" +workdir="$(mktemp -d)" +git clone --depth 1 --branch "$SOURCE_BRANCH" "$GIT_URL" "$workdir/repo" +cd "$workdir/repo" +revision="$(git rev-parse HEAD)" +subject="$(git log -1 --pretty=%s)" +case "$subject" in + "chore: promote G14 GitOps source "*) + echo "Skipping generated GitOps promotion commit $revision" + exit 0 + ;; +esac +short="$(printf '%.12s' "$revision")" +name="hwlab-g14-ci-poll-$short" +export POLLER_REVISION="$revision" +export POLLER_PIPELINERUN="$name" +export POLLER_SOURCE_SUBJECT="$subject" +echo "Checking G14 source $revision with PipelineRun $name" +node <<'NODE' +const fs = require("node:fs"); +const https = require("node:https"); + +function env(name, fallback) { + return process.env[name] || fallback; +} + +const namespace = env("POD_NAMESPACE", "hwlab-ci"); +const name = env("POLLER_PIPELINERUN", ""); +const revision = env("POLLER_REVISION", ""); +const host = process.env.KUBERNETES_SERVICE_HOST; +const port = process.env.KUBERNETES_SERVICE_PORT || "443"; +if (!name || !revision) throw new Error("poller did not resolve a PipelineRun name and revision"); +if (!host) throw new Error("KUBERNETES_SERVICE_HOST is not available"); + +const token = fs.readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/token", "utf8"); +const ca = fs.readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"); + +function request(method, path, body) { + const payload = body ? JSON.stringify(body) : ""; + const headers = { Authorization: "Bearer " + token }; + if (payload) { + headers["Content-Type"] = "application/json"; + headers["Content-Length"] = Buffer.byteLength(payload); + } + return new Promise((resolve, reject) => { + const req = https.request({ host, port, method, path, ca, headers }, (res) => { + let data = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { data += chunk; }); + res.on("end", () => resolve({ statusCode: res.statusCode || 0, body: data })); + }); + req.on("error", reject); + if (payload) req.write(payload); + req.end(); + }); +} + +const labels = { + "app.kubernetes.io/part-of": "hwlab", + "hwlab.pikastech.local/gitops-target": "g14", + "hwlab.pikastech.local/source-commit": revision, + "hwlab.pikastech.local/trigger": "polling" +}; + +const pipelineRun = { + apiVersion: "tekton.dev/v1", + kind: "PipelineRun", + metadata: { + name, + namespace, + labels, + annotations: { + "hwlab.pikastech.local/source-subject": env("POLLER_SOURCE_SUBJECT", ""), + "hwlab.pikastech.local/source-branch": env("SOURCE_BRANCH", "G14"), + "hwlab.pikastech.local/gitops-branch": env("GITOPS_BRANCH", "G14-gitops") + } + }, + spec: { + pipelineRef: { name: env("PIPELINE_NAME", "hwlab-g14-ci-image-publish") }, + taskRunTemplate: { + serviceAccountName: "hwlab-tekton-runner", + podTemplate: { hostNetwork: true, dnsPolicy: "ClusterFirstWithHostNet" } + }, + params: [ + { name: "git-url", value: env("GIT_URL", "git@github.com:pikasTech/HWLAB.git") }, + { name: "source-branch", value: env("SOURCE_BRANCH", "G14") }, + { name: "gitops-branch", value: env("GITOPS_BRANCH", "G14-gitops") }, + { name: "revision", value: revision }, + { name: "registry-prefix", value: env("REGISTRY_PREFIX", "127.0.0.1:5000/hwlab") }, + { name: "services", value: env("SERVICES", "hwlab-cloud-api,hwlab-cloud-web,hwlab-agent-mgr,hwlab-agent-worker,hwlab-gateway,hwlab-gateway-simu,hwlab-box-simu,hwlab-patch-panel,hwlab-router,hwlab-tunnel-client,hwlab-edge-proxy,hwlab-cli,hwlab-agent-skills") }, + { name: "base-image", value: env("BASE_IMAGE", "node:20-bookworm-slim") }, + { name: "concurrency", value: env("CONCURRENCY", "4") } + ], + workspaces: [ + { name: "source", volumeClaimTemplate: { spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "8Gi" } } } } }, + { name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } } + ] + } +}; + +const base = "/apis/tekton.dev/v1/namespaces/" + encodeURIComponent(namespace) + "/pipelineruns"; +const item = base + "/" + encodeURIComponent(name); + +(async () => { + const existing = await request("GET", item); + if (existing.statusCode === 200) { + console.log(JSON.stringify({ status: "exists", pipelineRun: name, revision })); + return; + } + if (existing.statusCode !== 404) { + throw new Error("unexpected PipelineRun lookup status " + existing.statusCode + ": " + existing.body.slice(0, 500)); + } + const created = await request("POST", base, pipelineRun); + if (created.statusCode < 200 || created.statusCode > 299) { + throw new Error("PipelineRun create failed with status " + created.statusCode + ": " + created.body.slice(0, 1000)); + } + console.log(JSON.stringify({ status: "created", pipelineRun: name, revision })); +})().catch((error) => { + console.error(error.stack || error.message); + process.exitCode = 1; +}); +NODE +`; +} + function tektonRbac() { return { apiVersion: "v1", @@ -367,6 +549,21 @@ function tektonRbac() { metadata: { name: "hwlab-tekton-runtime-observer", namespace: "hwlab-dev" }, subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }], roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-tekton-runtime-observer" } + }, + { + apiVersion: "rbac.authorization.k8s.io/v1", + kind: "Role", + metadata: { name: "hwlab-tekton-pipelinerun-writer", namespace: "hwlab-ci" }, + rules: [ + { apiGroups: ["tekton.dev"], resources: ["pipelineruns"], verbs: ["get", "list", "create"] } + ] + }, + { + apiVersion: "rbac.authorization.k8s.io/v1", + kind: "RoleBinding", + metadata: { name: "hwlab-tekton-pipelinerun-writer", namespace: "hwlab-ci" }, + subjects: [{ kind: "ServiceAccount", name: "hwlab-tekton-runner", namespace: "hwlab-ci" }], + roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-tekton-pipelinerun-writer" } } ] }; @@ -392,6 +589,7 @@ function tektonPipeline(ci) { params: [ { name: "git-url", type: "string", default: defaultSourceRepo }, { name: "source-branch", type: "string", default: defaultBranch }, + { name: "gitops-branch", type: "string", default: defaultGitopsBranch }, { name: "revision", type: "string", description: "Full git commit SHA; the only source truth for image tags." }, { name: "registry-prefix", type: "string", default: defaultRegistryPrefix }, { name: "services", type: "string", default: "hwlab-cloud-api,hwlab-cloud-web,hwlab-agent-mgr,hwlab-agent-worker,hwlab-gateway,hwlab-gateway-simu,hwlab-box-simu,hwlab-patch-panel,hwlab-router,hwlab-tunnel-client,hwlab-edge-proxy,hwlab-cli,hwlab-agent-skills" }, @@ -461,6 +659,32 @@ function tektonPipeline(ci) { { name: "base-image", value: "$(params.base-image)" }, { name: "concurrency", value: "$(params.concurrency)" } ] + }, + { + name: "gitops-promote", + runAfter: ["image-publish"], + workspaces: [ + { name: "source", workspace: "source" }, + { name: "git-ssh", workspace: "git-ssh" } + ], + taskSpec: { + params: [ + { name: "git-url" }, + { name: "source-branch" }, + { name: "gitops-branch" }, + { name: "revision" }, + { name: "registry-prefix" } + ], + workspaces: [{ name: "source" }, { name: "git-ssh" }], + steps: [{ name: "promote", image: "node:22-bookworm-slim", env: proxyEnv(), script: gitopsPromoteScript() }] + }, + params: [ + { name: "git-url", value: "$(params.git-url)" }, + { name: "source-branch", value: "$(params.source-branch)" }, + { name: "gitops-branch", value: "$(params.gitops-branch)" }, + { name: "revision", value: "$(params.revision)" }, + { name: "registry-prefix", value: "$(params.registry-prefix)" } + ] } ] } @@ -492,6 +716,7 @@ function tektonPipelineRunTemplate({ source, args }) { params: [ { name: "git-url", value: args.sourceRepo }, { name: "source-branch", value: args.sourceBranch }, + { name: "gitops-branch", value: args.gitopsBranch }, { name: "revision", value: source.full }, { name: "registry-prefix", value: args.registryPrefix }, { name: "base-image", value: "node:20-bookworm-slim" }, @@ -505,6 +730,71 @@ function tektonPipelineRunTemplate({ source, args }) { }; } +function tektonPollerCronJob(args) { + return { + apiVersion: "batch/v1", + kind: "CronJob", + metadata: { + name: "hwlab-g14-branch-poller", + namespace: "hwlab-ci", + labels: { + "app.kubernetes.io/part-of": "hwlab", + "hwlab.pikastech.local/gitops-target": "g14" + }, + annotations: { + "hwlab.pikastech.local/source-branch": args.sourceBranch, + "hwlab.pikastech.local/gitops-branch": args.gitopsBranch + } + }, + spec: { + schedule: "*/5 * * * *", + concurrencyPolicy: "Forbid", + successfulJobsHistoryLimit: 3, + failedJobsHistoryLimit: 3, + jobTemplate: { + spec: { + backoffLimit: 1, + template: { + metadata: { + labels: { + "app.kubernetes.io/part-of": "hwlab", + "hwlab.pikastech.local/gitops-target": "g14" + } + }, + spec: { + serviceAccountName: "hwlab-tekton-runner", + restartPolicy: "Never", + hostNetwork: true, + dnsPolicy: "ClusterFirstWithHostNet", + containers: [{ + name: "poll", + image: "node:22-alpine", + imagePullPolicy: "IfNotPresent", + env: [ + ...proxyEnv(), + { name: "POD_NAMESPACE", valueFrom: { fieldRef: { fieldPath: "metadata.namespace" } } }, + { name: "PIPELINE_NAME", value: "hwlab-g14-ci-image-publish" }, + { name: "GIT_URL", value: args.sourceRepo }, + { name: "SOURCE_BRANCH", value: args.sourceBranch }, + { name: "GITOPS_BRANCH", value: args.gitopsBranch }, + { name: "REGISTRY_PREFIX", value: args.registryPrefix }, + { name: "SERVICES", value: "hwlab-cloud-api,hwlab-cloud-web,hwlab-agent-mgr,hwlab-agent-worker,hwlab-gateway,hwlab-gateway-simu,hwlab-box-simu,hwlab-patch-panel,hwlab-router,hwlab-tunnel-client,hwlab-edge-proxy,hwlab-cli,hwlab-agent-skills" }, + { name: "BASE_IMAGE", value: "node:20-bookworm-slim" }, + { name: "CONCURRENCY", value: "4" } + ], + volumeMounts: [{ name: "git-ssh", mountPath: "/workspace/git-ssh", readOnly: true }], + command: ["/bin/sh", "-c"], + args: [pollerScript()] + }], + volumes: [{ name: "git-ssh", secret: { secretName: "hwlab-git-ssh" } }] + } + } + } + } + } + }; +} + function registryManifest() { return { apiVersion: "v1", @@ -574,7 +864,7 @@ function argoApplication(args) { }, spec: { project: "hwlab-g14", - source: { repoURL: args.sourceRepo, targetRevision: args.sourceBranch, path: "deploy/gitops/g14/runtime" }, + source: { repoURL: args.sourceRepo, targetRevision: args.gitopsBranch, path: "deploy/gitops/g14/runtime" }, destination: { server: "https://kubernetes.default.svc", namespace: "hwlab-dev" }, syncPolicy: { automated: { prune: false, selfHeal: true }, syncOptions: ["CreateNamespace=true", "ApplyOutOfSyncOnly=true"] } } @@ -598,7 +888,8 @@ This directory is generated from \`CI.json\`, \`deploy/deploy.json\`, and \`depl - Target: G14 k3s only. - CI: Tekton Pipeline \`hwlab-ci/hwlab-g14-ci-image-publish\`. - Artifact contract: images are tagged by source git commit \`${source.short}\` and pushed to \`${args.registryPrefix}\`. -- CD: Argo CD Application \`argocd/hwlab-g14-dev\` consumes \`deploy/gitops/g14/runtime\`. +- Branch split: source branch \`${args.sourceBranch}\` remains the watched code branch; generated desired state is promoted to \`${args.gitopsBranch}\`. +- CD: Argo CD Application \`argocd/hwlab-g14-dev\` consumes \`${args.gitopsBranch}:deploy/gitops/g14/runtime\`. - D601 boundary: this path does not target D601, does not use UniDesk Code Queue, and does not change D601 production traffic. - Concurrency: CI builds are per source commit and do not acquire the legacy DEV CD Lease; Argo CD reconciles the Git desired state. `; @@ -630,6 +921,7 @@ async function plannedFiles(args) { sourceCommit: source.full, sourceShortCommit: source.short, sourceBranch: args.sourceBranch, + gitopsBranch: args.gitopsBranch, sourceRepo: args.sourceRepo, registryPrefix: args.registryPrefix, runtimePath: "deploy/gitops/g14/runtime", @@ -640,6 +932,7 @@ async function plannedFiles(args) { putJson("registry/registry.yaml", registryManifest()); putJson("tekton/rbac.yaml", tektonRbac()); putJson("tekton/pipeline.yaml", tektonPipeline(ci)); + putJson("tekton/poller.yaml", tektonPollerCronJob(args)); putJson("tekton/pipelinerun.sample.yaml", tektonPipelineRunTemplate({ source, args })); putJson("argocd/project.yaml", argoProject()); putJson("argocd/application.yaml", argoApplication(args));