apiVersion: v1
kind: Namespace
metadata:
  name: unidesk-ci
  labels:
    app.kubernetes.io/part-of: unidesk
    unidesk.ai/purpose: ci
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: unidesk-ci-runner
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: unidesk-ci-runner
  namespace: unidesk-ci
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "services"]
    verbs: ["get", "list", "watch", "create", "delete", "patch"]
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "delete", "patch"]
  - apiGroups: ["tekton.dev"]
    resources: ["pipelineruns", "taskruns"]
    verbs: ["get", "list", "watch", "create", "delete", "patch"]
  - apiGroups: ["triggers.tekton.dev"]
    resources: ["eventlisteners", "triggers", "triggerbindings", "triggertemplates", "interceptors"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: unidesk-ci-runner
  namespace: unidesk-ci
subjects:
  - kind: ServiceAccount
    name: unidesk-ci-runner
    namespace: unidesk-ci
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: unidesk-ci-runner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: unidesk-ci-trigger-reader
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
rules:
  - apiGroups: ["triggers.tekton.dev"]
    resources: ["clusterinterceptors", "clustertriggerbindings"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: unidesk-ci-trigger-reader
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
subjects:
  - kind: ServiceAccount
    name: unidesk-ci-runner
    namespace: unidesk-ci
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: unidesk-ci-trigger-reader
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: unidesk-ci-dev-e2e-manager
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
rules:
  - apiGroups: [""]
    resources: ["namespaces"]
    verbs: ["get", "list", "watch", "create", "delete", "patch"]
  - apiGroups: [""]
    resources: ["configmaps", "secrets", "services", "services/proxy", "pods", "pods/log"]
    verbs: ["get", "list", "watch", "create", "delete", "patch"]
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "delete", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: unidesk-ci-dev-e2e-manager
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
subjects:
  - kind: ServiceAccount
    name: unidesk-ci-runner
    namespace: unidesk-ci
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: unidesk-ci-dev-e2e-manager
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: unidesk-ci-cross-namespace
  namespace: unidesk
rules:
  - apiGroups: [""]
    resources: ["services"]
    verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: unidesk-ci-cross-namespace
  namespace: unidesk
subjects:
  - kind: ServiceAccount
    name: unidesk-ci-runner
    namespace: unidesk-ci
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: unidesk-ci-cross-namespace
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: unidesk-ci-cache
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: unidesk-ci-budgets
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
data:
  firstPaintMs: "10000"
  traceSummaryMs: "10000"
  traceStepsMs: "20000"
  traceStepDetailMs: "20000"
  overviewP95Ms: "20000"
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: unidesk-repo-check
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: repo-check
spec:
  params:
    - name: repo-url
      type: string
    - name: revision
      type: string
    - name: image
      type: string
      default: unidesk-code-queue:dev
  workspaces:
    - name: source
  volumes:
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock
        type: Socket
  steps:
    - name: clone
      image: alpine/git:2.45.2
      env:
        - name: HTTP_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: HTTPS_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: ALL_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,ci-git-mirror,ci-git-mirror.unidesk-ci,ci-git-mirror.unidesk-ci.svc,ci-git-mirror.unidesk-ci.svc.cluster.local,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local"
        - name: http_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: https_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: all_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,ci-git-mirror,ci-git-mirror.unidesk-ci,ci-git-mirror.unidesk-ci.svc,ci-git-mirror.unidesk-ci.svc.cluster.local,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local"
      script: |
        #!/bin/sh
        set -eu
        rm -rf "$(workspaces.source.path)/repo"
        git clone --filter=blob:none "$(params.repo-url)" "$(workspaces.source.path)/repo"
        cd "$(workspaces.source.path)/repo"
        git fetch --depth=1 origin "$(params.revision)"
        git checkout --detach FETCH_HEAD
        git rev-parse HEAD | tee "$(workspaces.source.path)/commit.txt"
    - name: install-and-check
      image: "$(params.image)"
      imagePullPolicy: Never
      env:
        - name: DOCKER_HOST
          value: unix:///var/run/docker.sock
        - name: BUN_INSTALL_CACHE_DIR
          value: "$(workspaces.source.path)/cache/bun"
        - name: HTTP_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: HTTPS_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: ALL_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local"
        - name: http_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: https_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: all_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local"
      volumeMounts:
        - name: docker-sock
          mountPath: /var/run/docker.sock
      script: |
        #!/usr/bin/env bash
        set -euo pipefail
        cd "$(workspaces.source.path)/repo"
        command -v bun
        command -v git
        command -v docker
        docker compose version >/dev/null
        bun install --frozen-lockfile
        (cd src && bun install --frozen-lockfile)
        UNIDESK_NATIVE_K3S_RUST_CHECK=1 bun scripts/cli.ts check --full --rust
        cargo build --manifest-path src/components/microservices/code-queue-mgr/Cargo.toml --release
        CQ_MGR_ID="$(printf '%s' "$(params.revision)" | tr -cd 'A-Za-z0-9_.-' | cut -c1-48)"
        CQ_MGR_DB="$(workspaces.source.path)/code-queue-mgr-workdirs-postgres"
        rm -rf "$CQ_MGR_DB"
        mkdir -p "$CQ_MGR_DB"
        docker rm -f "code-queue-mgr-workdirs-$CQ_MGR_ID" >/dev/null 2>&1 || true
        docker run -d --name "code-queue-mgr-workdirs-$CQ_MGR_ID" \
          -p 127.0.0.1::5432 \
          -e POSTGRES_USER=unidesk_ci \
          -e POSTGRES_PASSWORD=unidesk_ci_password \
          -e POSTGRES_DB=unidesk_ci \
          -v "$CQ_MGR_DB:/var/lib/postgresql/data" \
          postgres:16-alpine >/tmp/code-queue-mgr-workdirs-postgres.cid
        CQ_MGR_DB_PORT="$(docker inspect "code-queue-mgr-workdirs-$CQ_MGR_ID" --format '{{(index (index .NetworkSettings.Ports "5432/tcp") 0).HostPort}}')"
        cleanup() {
          docker rm -f "code-queue-mgr-workdirs-$CQ_MGR_ID" >/dev/null 2>&1 || true
        }
        trap cleanup EXIT
        for _ in $(seq 1 60); do
          if docker exec "code-queue-mgr-workdirs-$CQ_MGR_ID" pg_isready -U unidesk_ci -d unidesk_ci >/dev/null 2>&1; then
            break
          fi
          sleep 1
        done
        CQ_MGR_PORT="$(python3 - <<'PY'
        import socket
        s = socket.socket()
        s.bind(("127.0.0.1", 0))
        print(s.getsockname()[1])
        s.close()
        PY
        )"
        DATABASE_URL="postgres://unidesk_ci:unidesk_ci_password@127.0.0.1:$CQ_MGR_DB_PORT/unidesk_ci" \
        HOST=127.0.0.1 \
        PORT="$CQ_MGR_PORT" \
        CODE_QUEUE_MAIN_PROVIDER_ID=G14 \
        CODE_QUEUE_WORKDIR=/workspace \
        CODE_QUEUE_REMOTE_WORKDIR=/root/unidesk \
        src/components/microservices/code-queue-mgr/target/release/code-queue-mgr >"$(workspaces.source.path)/code-queue-mgr-workdirs.log" 2>&1 &
        cq_mgr_pid="$!"
        trap 'kill "$cq_mgr_pid" >/dev/null 2>&1 || true; cleanup' EXIT
        for _ in $(seq 1 60); do
          if curl -fsS "http://127.0.0.1:$CQ_MGR_PORT/health" >/dev/null 2>&1; then
            break
          fi
          sleep 1
        done
        workdirs_json="$(curl -fsS "http://127.0.0.1:$CQ_MGR_PORT/api/workdirs")"
        printf '%s\n' "$workdirs_json"
        WORKDIRS_JSON="$workdirs_json" bun -e 'const body=JSON.parse(process.env.WORKDIRS_JSON||"{}"); if (body.ok !== true || !Array.isArray(body.workdirs) || !body.workdirs.some((item)=>item && item.path === "/workspace" && item.providerId === "G14" && item.executionMode === "default")) { console.error(JSON.stringify(body)); process.exit(1); }'
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: unidesk-backend-core-artifact-publish
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: backend-core-artifact
spec:
  params:
    - name: repo-url
      type: string
    - name: revision
      type: string
    - name: dockerfile
      type: string
    - name: image-repository
      type: string
    - name: app-image
      type: string
      default: unidesk-code-queue:dev
    - name: registry
      type: string
      default: 127.0.0.1:5000
    - name: source-host-path
      type: string
  results:
    - name: backend_core_artifact_service_id
      description: CI artifact summary service id.
    - name: backend_core_artifact_source_commit
      description: Full Git source commit used to build the artifact.
    - name: backend_core_artifact_source_repo
      description: Source repository URL used to build the artifact.
    - name: backend_core_artifact_dockerfile
      description: Repo-relative Dockerfile path used to build the artifact.
    - name: backend_core_artifact_registry
      description: G14 loopback registry host.
    - name: backend_core_artifact_repository
      description: Registry repository without tag or digest.
    - name: backend_core_artifact_image
      description: Commit-tagged image reference.
    - name: backend_core_artifact_tag
      description: Commit-pinned image tag.
    - name: backend_core_artifact_digest
      description: Registry manifest digest.
    - name: backend_core_artifact_digest_ref
      description: Immutable image digest reference.
  workspaces:
    - name: source
  volumes:
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock
        type: Socket
    - name: prepared-source
      hostPath:
        path: /root/.unidesk/ci/backend-core-artifacts
        type: Directory
  steps:
    - name: prepare-source
      image: alpine/git:2.45.2
      volumeMounts:
        - name: prepared-source
          mountPath: /prepared-source
          readOnly: true
      script: |
        #!/bin/sh
        set -eu
        dockerfile="$(params.dockerfile)"
        case "$(params.revision)" in
          [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]) ;;
          *) echo "backend_core_artifact_revision_must_be_full_sha=$(params.revision)" >&2; exit 2 ;;
        esac
        case "$dockerfile" in
          /*|*..*|""|*latest*) echo "backend_core_artifact_dockerfile_invalid=$dockerfile" >&2; exit 2 ;;
        esac
        mkdir -p "$(workspaces.source.path)/backend-core-artifact-repo"
        find "$(workspaces.source.path)/backend-core-artifact-repo" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
        case "$(params.source-host-path)" in
          /root/.unidesk/ci/backend-core-artifacts/*) ;;
          *) echo "backend_core_artifact_source_host_path_invalid=$(params.source-host-path)" >&2; exit 2 ;;
        esac
        source_name="$(basename "$(params.source-host-path)")"
        source_dir="/prepared-source/$source_name"
        test -f "$source_dir/.unidesk-source-commit"
        prepared_commit="$(cat "$source_dir/.unidesk-source-commit")"
        test "$prepared_commit" = "$(params.revision)"
        cp -a "$source_dir/." "$(workspaces.source.path)/backend-core-artifact-repo/"
        cd "$(workspaces.source.path)/backend-core-artifact-repo"
        test -f "$dockerfile"
        test -d src/components/backend-core/src
        printf '%s\n' "$prepared_commit" | tee "$(workspaces.source.path)/backend-core-artifact-commit.txt"
        printf '%s\n' "$dockerfile" | tee "$(workspaces.source.path)/backend-core-artifact-dockerfile.txt"
    - name: build-and-push
      image: "$(params.app-image)"
      imagePullPolicy: Never
      workingDir: "$(workspaces.source.path)/backend-core-artifact-repo"
      env:
        - name: DOCKER_HOST
          value: unix:///var/run/docker.sock
        - name: HTTP_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: HTTPS_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: ALL_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,host.docker.internal,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local"
        - name: http_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: https_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: all_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,host.docker.internal,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local"
      volumeMounts:
        - name: docker-sock
          mountPath: /var/run/docker.sock
      script: |
        #!/usr/bin/env bash
        set -euo pipefail
        commit="$(cat "$(workspaces.source.path)/backend-core-artifact-commit.txt")"
        dockerfile="$(cat "$(workspaces.source.path)/backend-core-artifact-dockerfile.txt")"
        registry="$(params.registry)"
        image_repository="$(params.image-repository)"
        test "$registry" = "127.0.0.1:5000" || { echo "backend_core_artifact_registry_must_be_g14_loopback=$registry" >&2; exit 2; }
        case "$image_repository" in
          ""|/*|*..*|*:*|*@*|*latest*|*[!a-z0-9._/-]*) echo "backend_core_artifact_image_repository_invalid=$image_repository" >&2; exit 2 ;;
        esac
        local_image="$image_repository:$commit"
        registry_image="$registry/$image_repository:$commit"
        repository="$registry/$image_repository"
        command -v docker
        docker version >/dev/null
        docker run --rm --network host rancher/mirrored-library-busybox:1.36.1 wget -q -O- "http://$registry/v2/" >/dev/null
        DOCKER_BUILDKIT=0 docker build \
          --network host \
          --build-arg HTTP_PROXY=http://127.0.0.1:18789 \
          --build-arg HTTPS_PROXY=http://127.0.0.1:18789 \
          --build-arg ALL_PROXY=http://127.0.0.1:18789 \
          --build-arg NO_PROXY=localhost,127.0.0.1,::1,host.docker.internal,registry.npmjs.org,.registry.npmjs.org \
          --label "unidesk.ai/service-id=backend-core" \
          --label "unidesk.ai/source-repo=$(params.repo-url)" \
          --label "unidesk.ai/source-commit=$commit" \
          --label "unidesk.ai/dockerfile=$dockerfile" \
          -t "$local_image" \
          -t "$registry_image" \
          -f "$dockerfile" \
          .
        actual_commit="$(docker image inspect "$registry_image" --format '{{ index .Config.Labels "unidesk.ai/source-commit" }}')"
        test "$actual_commit" = "$commit"
        docker push "$registry_image"
        docker pull "$registry_image" >/dev/null
        repo_digests="$(docker image inspect "$registry_image" --format '{{json .RepoDigests}}')"
        digest="$(docker image inspect "$registry_image" --format '{{range .RepoDigests}}{{println .}}{{end}}' | awk -F@ -v repo="$repository" '$1 == repo { print $2; exit }')"
        test -n "$digest"
        digest_ref="$repository@$digest"
        printf '%s' "backend-core" > "$(results.backend_core_artifact_service_id.path)"
        printf '%s' "$commit" > "$(results.backend_core_artifact_source_commit.path)"
        printf '%s' "$(params.repo-url)" > "$(results.backend_core_artifact_source_repo.path)"
        printf '%s' "$dockerfile" > "$(results.backend_core_artifact_dockerfile.path)"
        printf '%s' "$registry" > "$(results.backend_core_artifact_registry.path)"
        printf '%s' "$repository" > "$(results.backend_core_artifact_repository.path)"
        printf '%s' "$registry_image" > "$(results.backend_core_artifact_image.path)"
        printf '%s' "$commit" > "$(results.backend_core_artifact_tag.path)"
        printf '%s' "$digest" > "$(results.backend_core_artifact_digest.path)"
        printf '%s' "$digest_ref" > "$(results.backend_core_artifact_digest_ref.path)"
        printf 'backend_core_artifact_service_id=backend-core\nbackend_core_artifact_image=%s\nbackend_core_artifact_repository=%s\nbackend_core_artifact_tag=%s\nbackend_core_artifact_digest=%s\nbackend_core_artifact_digest_ref=%s\nbackend_core_artifact_source_commit=%s\nbackend_core_artifact_source_repo=%s\nbackend_core_artifact_dockerfile=%s\nbackend_core_artifact_registry=%s\nbackend_core_artifact_repo_digests=%s\n' "$registry_image" "$repository" "$commit" "$digest" "$digest_ref" "$commit" "$(params.repo-url)" "$dockerfile" "$registry" "$repo_digests"
---
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: unidesk-backend-core-artifact-publish
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: backend-core-artifact
    app.kubernetes.io/part-of: unidesk
spec:
  params:
    - name: repo-url
      type: string
      default: https://github.com/pikasTech/unidesk
    - name: revision
      type: string
    - name: dockerfile
      type: string
      default: src/components/backend-core/Dockerfile
    - name: image-repository
      type: string
      default: unidesk/backend-core
    - name: app-image
      type: string
      default: unidesk-code-queue:dev
    - name: registry
      type: string
      default: 127.0.0.1:5000
    - name: source-host-path
      type: string
  workspaces:
    - name: shared-workspace
  tasks:
    - name: publish-backend-core-artifact
      taskRef:
        name: unidesk-backend-core-artifact-publish
      params:
        - name: repo-url
          value: "$(params.repo-url)"
        - name: revision
          value: "$(params.revision)"
        - name: dockerfile
          value: "$(params.dockerfile)"
        - name: image-repository
          value: "$(params.image-repository)"
        - name: app-image
          value: "$(params.app-image)"
        - name: registry
          value: "$(params.registry)"
        - name: source-host-path
          value: "$(params.source-host-path)"
      workspaces:
        - name: source
          workspace: shared-workspace
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: unidesk-user-service-artifact-publish
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: user-service-artifact
spec:
  params:
    - name: repo-url
      type: string
    - name: revision
      type: string
    - name: service-id
      type: string
    - name: dockerfile
      type: string
    - name: image-repository
      type: string
    - name: app-image
      type: string
      default: unidesk-code-queue:dev
    - name: registry
      type: string
      default: 127.0.0.1:5000
    - name: source-host-path
      type: string
  results:
    - name: user_service_artifact_service_id
      description: CI artifact summary service id.
    - name: user_service_artifact_source_commit
      description: Full Git source commit used to build the artifact.
    - name: user_service_artifact_source_repo
      description: Source repository URL used to build the artifact.
    - name: user_service_artifact_dockerfile
      description: Repo-relative Dockerfile path used to build the artifact.
    - name: user_service_artifact_registry
      description: G14 loopback registry host.
    - name: user_service_artifact_repository
      description: Registry repository without tag or digest.
    - name: user_service_artifact_image
      description: Commit-tagged image reference.
    - name: user_service_artifact_tag
      description: Commit-pinned image tag.
    - name: user_service_artifact_digest
      description: Registry manifest digest.
    - name: user_service_artifact_digest_ref
      description: Immutable image digest reference.
  workspaces:
    - name: source
  volumes:
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock
        type: Socket
    - name: prepared-source
      hostPath:
        path: /root/.unidesk/ci/user-service-artifacts
        type: Directory
  steps:
    - name: prepare-source
      image: alpine/git:2.45.2
      volumeMounts:
        - name: prepared-source
          mountPath: /prepared-source
          readOnly: true
      script: |
        #!/bin/sh
        set -eu
        service_id="$(params.service-id)"
        dockerfile="$(params.dockerfile)"
        source_host_path="$(params.source-host-path)"
        case "$(params.revision)" in
          [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]) ;;
          *) echo "user_service_artifact_revision_must_be_full_sha=$(params.revision)" >&2; exit 2 ;;
        esac
        printf '%s\n' "$service_id" | grep -Eq '^[a-z0-9]([-a-z0-9]{0,62}[a-z0-9])?$' || { echo "user_service_artifact_service_id_invalid=$service_id" >&2; exit 2; }
        case "$dockerfile" in
          /*|*..*|""|*latest*) echo "user_service_artifact_dockerfile_invalid=$dockerfile" >&2; exit 2 ;;
        esac
        expected_prefix="/root/.unidesk/ci/user-service-artifacts/$service_id/"
        case "$source_host_path" in
          "$expected_prefix"*) ;;
          *) echo "user_service_artifact_source_host_path_invalid=$source_host_path" >&2; exit 2 ;;
        esac
        mkdir -p "$(workspaces.source.path)/user-service-artifact-repo"
        find "$(workspaces.source.path)/user-service-artifact-repo" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
        source_name="$(basename "$source_host_path")"
        source_dir="/prepared-source/$service_id/$source_name"
        test -f "$source_dir/.unidesk-source-commit"
        test -f "$source_dir/.unidesk-service-id"
        test -f "$source_dir/.unidesk-dockerfile"
        prepared_commit="$(cat "$source_dir/.unidesk-source-commit")"
        prepared_service_id="$(cat "$source_dir/.unidesk-service-id")"
        prepared_dockerfile="$(cat "$source_dir/.unidesk-dockerfile")"
        test "$prepared_commit" = "$(params.revision)"
        test "$prepared_service_id" = "$service_id"
        test "$prepared_dockerfile" = "$dockerfile"
        cp -a "$source_dir/." "$(workspaces.source.path)/user-service-artifact-repo/"
        cd "$(workspaces.source.path)/user-service-artifact-repo"
        test -f "$dockerfile"
        if [ "$service_id" = "claudeqq" ]; then
          test -f "$(dirname "$dockerfile")/scripts/src/server_ts/package.json"
          test -d "$(dirname "$dockerfile")/scripts/src/server_ts/src"
        elif [ "$service_id" = "frontend" ]; then
          test -d src/components/frontend/src
        else
          test -d "$(dirname "$dockerfile")/src"
        fi
        printf '%s\n' "$prepared_commit" | tee "$(workspaces.source.path)/user-service-artifact-commit.txt"
        printf '%s\n' "$service_id" | tee "$(workspaces.source.path)/user-service-artifact-service-id.txt"
        printf '%s\n' "$dockerfile" | tee "$(workspaces.source.path)/user-service-artifact-dockerfile.txt"
    - name: build-and-push
      image: "$(params.app-image)"
      imagePullPolicy: Never
      workingDir: "$(workspaces.source.path)/user-service-artifact-repo"
      env:
        - name: DOCKER_HOST
          value: unix:///var/run/docker.sock
        - name: HTTP_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: HTTPS_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: ALL_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,host.docker.internal,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local"
        - name: http_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: https_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: all_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,host.docker.internal,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local"
      volumeMounts:
        - name: docker-sock
          mountPath: /var/run/docker.sock
      script: |
        #!/usr/bin/env bash
        set -euo pipefail
        commit="$(cat "$(workspaces.source.path)/user-service-artifact-commit.txt")"
        service_id="$(cat "$(workspaces.source.path)/user-service-artifact-service-id.txt")"
        dockerfile="$(cat "$(workspaces.source.path)/user-service-artifact-dockerfile.txt")"
        registry="$(params.registry)"
        image_repository="$(params.image-repository)"
        test "$registry" = "127.0.0.1:5000" || { echo "user_service_artifact_registry_must_be_g14_loopback=$registry" >&2; exit 2; }
        case "$image_repository" in
          ""|/*|*..*|*:*|*@*|*latest*|*[!a-z0-9._/-]*) echo "user_service_artifact_image_repository_invalid=$image_repository" >&2; exit 2 ;;
        esac
        case "$commit" in
          [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]) ;;
          *) echo "user_service_artifact_commit_invalid=$commit" >&2; exit 2 ;;
        esac
        local_image="$image_repository:$commit"
        repository="$registry/$image_repository"
        registry_image="$repository:$commit"
        command -v docker
        docker version >/dev/null
        docker run --rm --network host rancher/mirrored-library-busybox:1.36.1 wget -q -O- "http://$registry/v2/" >/dev/null
        build_context=.
        if [ "$service_id" = "claudeqq" ]; then
          build_context="$(dirname "$dockerfile")"
        fi
        base_args=()
        if [ "$service_id" = "code-queue" ]; then
          if docker image inspect unidesk-code-queue:g14 >/dev/null 2>&1; then
            base_args=(--build-arg CODE_QUEUE_BASE_IMAGE=unidesk-code-queue:g14)
            echo "user_service_artifact_base_image=unidesk-code-queue:g14"
          else
            echo "user_service_artifact_base_image_missing=unidesk-code-queue:g14" >&2
            echo "user_service_artifact_slow_build_blocked=true" >&2
            exit 2
          fi
        else
          echo "user_service_artifact_base_image=default"
        fi
        DOCKER_BUILDKIT=0 docker build \
          --network host \
          "${base_args[@]}" \
          --build-arg HTTP_PROXY=http://127.0.0.1:18789 \
          --build-arg HTTPS_PROXY=http://127.0.0.1:18789 \
          --build-arg ALL_PROXY=http://127.0.0.1:18789 \
          --build-arg NO_PROXY=localhost,127.0.0.1,::1,host.docker.internal,registry.npmjs.org,.registry.npmjs.org \
          --label "unidesk.ai/service-id=$service_id" \
          --label "unidesk.ai/source-repo=$(params.repo-url)" \
          --label "unidesk.ai/source-commit=$commit" \
          --label "unidesk.ai/dockerfile=$dockerfile" \
          -t "$local_image" \
          -t "$registry_image" \
          -f "$dockerfile" \
          "$build_context"
        actual_service="$(docker image inspect "$registry_image" --format '{{ index .Config.Labels "unidesk.ai/service-id" }}')"
        actual_commit="$(docker image inspect "$registry_image" --format '{{ index .Config.Labels "unidesk.ai/source-commit" }}')"
        test "$actual_service" = "$service_id"
        test "$actual_commit" = "$commit"
        docker push "$registry_image"
        docker pull "$registry_image" >/dev/null
        repo_digests="$(docker image inspect "$registry_image" --format '{{json .RepoDigests}}')"
        digest="$(docker image inspect "$registry_image" --format '{{range .RepoDigests}}{{println .}}{{end}}' | awk -F@ -v repo="$repository" '$1 == repo { print $2; exit }')"
        test -n "$digest"
        digest_ref="$repository@$digest"
        printf '%s' "$service_id" > "$(results.user_service_artifact_service_id.path)"
        printf '%s' "$commit" > "$(results.user_service_artifact_source_commit.path)"
        printf '%s' "$(params.repo-url)" > "$(results.user_service_artifact_source_repo.path)"
        printf '%s' "$dockerfile" > "$(results.user_service_artifact_dockerfile.path)"
        printf '%s' "$registry" > "$(results.user_service_artifact_registry.path)"
        printf '%s' "$repository" > "$(results.user_service_artifact_repository.path)"
        printf '%s' "$registry_image" > "$(results.user_service_artifact_image.path)"
        printf '%s' "$commit" > "$(results.user_service_artifact_tag.path)"
        printf '%s' "$digest" > "$(results.user_service_artifact_digest.path)"
        printf '%s' "$digest_ref" > "$(results.user_service_artifact_digest_ref.path)"
        printf 'user_service_artifact_service_id=%s\nuser_service_artifact_image=%s\nuser_service_artifact_repository=%s\nuser_service_artifact_tag=%s\nuser_service_artifact_digest=%s\nuser_service_artifact_digest_ref=%s\nuser_service_artifact_source_commit=%s\nuser_service_artifact_source_repo=%s\nuser_service_artifact_dockerfile=%s\nuser_service_artifact_registry=%s\nuser_service_artifact_repo_digests=%s\n' "$service_id" "$registry_image" "$repository" "$commit" "$digest" "$digest_ref" "$commit" "$(params.repo-url)" "$dockerfile" "$registry" "$repo_digests"
---
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: unidesk-user-service-artifact-publish
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: user-service-artifact
    app.kubernetes.io/part-of: unidesk
spec:
  params:
    - name: repo-url
      type: string
      default: https://github.com/pikasTech/unidesk
    - name: revision
      type: string
    - name: service-id
      type: string
    - name: dockerfile
      type: string
    - name: image-repository
      type: string
    - name: app-image
      type: string
      default: unidesk-code-queue:dev
    - name: registry
      type: string
      default: 127.0.0.1:5000
    - name: source-host-path
      type: string
  workspaces:
    - name: shared-workspace
  tasks:
    - name: publish-user-service-artifact
      taskRef:
        name: unidesk-user-service-artifact-publish
      params:
        - name: repo-url
          value: "$(params.repo-url)"
        - name: revision
          value: "$(params.revision)"
        - name: service-id
          value: "$(params.service-id)"
        - name: dockerfile
          value: "$(params.dockerfile)"
        - name: image-repository
          value: "$(params.image-repository)"
        - name: app-image
          value: "$(params.app-image)"
        - name: registry
          value: "$(params.registry)"
        - name: source-host-path
          value: "$(params.source-host-path)"
      workspaces:
        - name: source
          workspace: shared-workspace
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: unidesk-code-queue-read-perf
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: code-queue-performance
spec:
  params:
    - name: revision
      type: string
    - name: app-image
      type: string
      default: oven/bun:1-debian
  workspaces:
    - name: source
  steps:
    - name: start-read-service
      image: "$(params.app-image)"
      imagePullPolicy: Never
      env:
        - name: HTTP_PROXY
          value: ""
        - name: HTTPS_PROXY
          value: ""
        - name: ALL_PROXY
          value: ""
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster.local"
        - name: http_proxy
          value: ""
        - name: https_proxy
          value: ""
        - name: all_proxy
          value: ""
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster.local"
      script: |
        #!/bin/bash
        set -euo pipefail
        kube_api="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}"
        kube_token="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
        kube_ca="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
        kube_namespace="$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)"
        kube() {
          local method="$1"
          shift
          curl -fsS --cacert "$kube_ca" -H "Authorization: Bearer $kube_token" -X "$method" "$@"
        }
        delete_if_exists() {
          local path="$1"
          local code
          code="$(curl -sS -o /tmp/unidesk-ci-delete-response -w "%{http_code}" --cacert "$kube_ca" -H "Authorization: Bearer $kube_token" -X DELETE "$kube_api/$path")"
          if [ "$code" = "200" ] || [ "$code" = "202" ] || [ "$code" = "404" ]; then
            return 0
          fi
          cat /tmp/unidesk-ci-delete-response >&2
          return 1
        }
        wait_deleted() {
          local path="$1"
          local deadline=$((SECONDS + 120))
          local code
          while [ "$SECONDS" -lt "$deadline" ]; do
            code="$(curl -sS -o /tmp/unidesk-ci-get-response -w "%{http_code}" --cacert "$kube_ca" -H "Authorization: Bearer $kube_token" "$kube_api/$path")"
            if [ "$code" = "404" ]; then
              return 0
            fi
            if [ "$code" != "200" ]; then
              cat /tmp/unidesk-ci-get-response >&2
              return 1
            fi
            sleep 2
          done
          echo "timeout waiting for $path deletion" >&2
          return 1
        }
        delete_if_exists "apis/apps/v1/namespaces/$kube_namespace/deployments/code-queue-ci-read"
        delete_if_exists "api/v1/namespaces/$kube_namespace/services/code-queue-ci-read"
        wait_deleted "apis/apps/v1/namespaces/$kube_namespace/deployments/code-queue-ci-read"
        cat >/tmp/code-queue-ci-read-deployment.yaml <<YAML
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: code-queue-ci-read
          namespace: unidesk-ci
          labels:
            app.kubernetes.io/name: code-queue
            app.kubernetes.io/component: ci-read
            app.kubernetes.io/part-of: unidesk
            unidesk.ai/ci-revision: "$(params.revision)"
        spec:
          replicas: 1
          selector:
            matchLabels:
              app.kubernetes.io/name: code-queue
              app.kubernetes.io/component: ci-read
          strategy:
            type: Recreate
          template:
            metadata:
              labels:
                app.kubernetes.io/name: code-queue
                app.kubernetes.io/component: ci-read
                app.kubernetes.io/part-of: unidesk
                unidesk.ai/node-id: G14
                unidesk.ai/ci-task-run: "$(context.taskRun.name)"
            spec:
              nodeSelector:
                unidesk.ai/node-id: G14
              terminationGracePeriodSeconds: 10
              containers:
                - name: code-queue
                  image: "$(params.app-image)"
                  imagePullPolicy: IfNotPresent
                  ports:
                    - name: http
                      containerPort: 4222
                  envFrom:
                    - secretRef:
                        name: code-queue-env
                        optional: true
                  env:
                    - name: HOST
                      value: "0.0.0.0"
                    - name: PORT
                      value: "4222"
                    - name: DATABASE_URL
                      value: "postgres://unidesk:unidesk_dev_password@g14-tcp-egress-gateway.unidesk.svc.cluster.local:15432/unidesk"
                    - name: CODE_QUEUE_INSTANCE_ID
                      value: "CI-read"
                    - name: CODE_QUEUE_SERVICE_ROLE
                      value: "read"
                    - name: CODE_QUEUE_SCHEDULER_ENABLED
                      value: "false"
                    - name: CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED
                      value: "false"
                    - name: CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED
                      value: "false"
                    - name: CODE_QUEUE_CODEX_SQLITE_LOG_EXPORT_ENABLED
                      value: "false"
                    - name: CODE_QUEUE_EGRESS_PROXY_ENABLED
                      value: "true"
                    - name: CODE_QUEUE_EGRESS_PROXY_URL
                      value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
                    - name: CODE_QUEUE_EGRESS_PROXY_NO_PROXY
                      value: "localhost,127.0.0.1,::1,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,backend-core,oa-event-flow,database,hyueapi.com,.hyueapi.com"
                    - name: HTTP_PROXY
                      value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
                    - name: HTTPS_PROXY
                      value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
                    - name: ALL_PROXY
                      value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
                    - name: NO_PROXY
                      value: "localhost,127.0.0.1,::1,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,backend-core,oa-event-flow,database,hyueapi.com,.hyueapi.com"
                    - name: http_proxy
                      value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
                    - name: https_proxy
                      value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
                    - name: all_proxy
                      value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
                    - name: no_proxy
                      value: "localhost,127.0.0.1,::1,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,backend-core,oa-event-flow,database,hyueapi.com,.hyueapi.com"
                    - name: CODE_QUEUE_DATA_DIR
                      value: "/var/lib/unidesk/code-queue-ci"
                    - name: CODE_QUEUE_WORKDIR
                      value: "/workspace"
                    - name: CODE_QUEUE_CODEX_HOME
                      value: "/var/lib/unidesk/code-queue-ci/codex-home"
                    - name: CODE_QUEUE_OPENCODE_XDG_DIR
                      value: "/var/lib/unidesk/code-queue-ci/opencode-xdg"
                    - name: CODE_QUEUE_DEFAULT_MODEL
                      value: "gpt-5.5"
                    - name: CODE_QUEUE_MODELS
                      value: "gpt-5.5,gpt-5.4-mini,gpt-5.4,deepseek-chat,minimax-m2.7"
                    - name: CODE_QUEUE_DATABASE_POOL_MAX
                      value: "4"
                    - name: CODE_QUEUE_IN_MEMORY_OUTPUT_RECORDS
                      value: "5"
                    - name: CODE_QUEUE_IN_MEMORY_EVENT_RECORDS
                      value: "5"
                    - name: OA_EVENT_FLOW_BASE_URL
                      value: "http://g14-tcp-egress-gateway.unidesk.svc.cluster.local:4255"
                    - name: LOG_FILE
                      value: "/var/log/unidesk/code-queue-ci-read.jsonl"
                    - name: NODE_OPTIONS
                      value: "--max-old-space-size=512"
                  readinessProbe:
                    httpGet:
                      path: /live
                      port: http
                    periodSeconds: 5
                    timeoutSeconds: 3
                    failureThreshold: 20
                  startupProbe:
                    httpGet:
                      path: /live
                      port: http
                    periodSeconds: 5
                    timeoutSeconds: 3
                    failureThreshold: 84
                  livenessProbe:
                    httpGet:
                      path: /live
                      port: http
                    periodSeconds: 10
                    timeoutSeconds: 3
                    failureThreshold: 6
                  resources:
                    requests:
                      cpu: 100m
                      memory: 256Mi
                    limits:
                      memory: 1Gi
                  volumeMounts:
                    - name: source
                      mountPath: /app
                    - name: state
                      mountPath: /var/lib/unidesk/code-queue-ci
                    - name: logs
                      mountPath: /var/log/unidesk
              volumes:
                - name: source
                  hostPath:
                    path: "$(workspaces.source.path)/repo"
                    type: Directory
                - name: state
                  emptyDir: {}
                - name: logs
                  emptyDir: {}
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: code-queue-ci-read
          namespace: unidesk-ci
          labels:
            app.kubernetes.io/name: code-queue
            app.kubernetes.io/component: ci-read
            app.kubernetes.io/part-of: unidesk
        spec:
          type: ClusterIP
          selector:
            app.kubernetes.io/name: code-queue
            app.kubernetes.io/component: ci-read
          ports:
            - name: http
              port: 4222
              targetPort: http
        YAML
        csplit -s -f /tmp/code-queue-ci-read- /tmp/code-queue-ci-read-deployment.yaml '/^---$/' '{*}'
        kube PATCH \
          -H "Content-Type: application/apply-patch+yaml" \
          --data-binary @/tmp/code-queue-ci-read-00 \
          "$kube_api/apis/apps/v1/namespaces/$kube_namespace/deployments/code-queue-ci-read?fieldManager=unidesk-ci&force=true" >/dev/null
        kube PATCH \
          -H "Content-Type: application/apply-patch+yaml" \
          --data-binary @/tmp/code-queue-ci-read-01 \
          "$kube_api/api/v1/namespaces/$kube_namespace/services/code-queue-ci-read?fieldManager=unidesk-ci&force=true" >/dev/null
        deadline=$((SECONDS + 420))
        while [ "$SECONDS" -lt "$deadline" ]; do
          status="$(kube GET "$kube_api/apis/apps/v1/namespaces/$kube_namespace/deployments/code-queue-ci-read")"
          replicas="$(printf '%s' "$status" | jq -r '.spec.replicas // 1')"
          available="$(printf '%s' "$status" | jq -r '.status.availableReplicas // 0')"
          updated="$(printf '%s' "$status" | jq -r '.status.updatedReplicas // 0')"
          observed="$(printf '%s' "$status" | jq -r '.status.observedGeneration // 0')"
          generation="$(printf '%s' "$status" | jq -r '.metadata.generation // 0')"
          if [ "$available" -ge "$replicas" ] && [ "$updated" -ge "$replicas" ] && [ "$observed" -ge "$generation" ]; then
            echo "code_queue_ci_read_rollout=available replicas=$available generation=$generation"
            exit 0
          fi
          sleep 2
        done
        echo "code_queue_ci_read_rollout=timeout" >&2
        kube GET "$kube_api/apis/apps/v1/namespaces/$kube_namespace/deployments/code-queue-ci-read" >&2
        exit 1
    - name: measure
      image: "$(params.app-image)"
      imagePullPolicy: Never
      workingDir: "$(workspaces.source.path)/repo"
      env:
        - name: CI_CODE_QUEUE_URL
          value: "http://code-queue-ci-read.unidesk-ci.svc.cluster.local:4222"
        - name: HTTP_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: HTTPS_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: ALL_PROXY
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local"
        - name: http_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: https_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: all_proxy
          value: "http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789"
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,code-queue-ci-read,code-queue-ci-read.unidesk-ci,code-queue-ci-read.unidesk-ci.svc,code-queue-ci-read.unidesk-ci.svc.cluster.local,g14-tcp-egress-gateway,g14-tcp-egress-gateway.unidesk,g14-tcp-egress-gateway.unidesk.svc,g14-tcp-egress-gateway.unidesk.svc.cluster.local,g14-provider-egress-proxy,g14-provider-egress-proxy.unidesk,g14-provider-egress-proxy.unidesk.svc,g14-provider-egress-proxy.unidesk.svc.cluster.local"
        - name: FIRST_PAINT_BUDGET_MS
          valueFrom:
            configMapKeyRef:
              name: unidesk-ci-budgets
              key: firstPaintMs
        - name: TRACE_SUMMARY_BUDGET_MS
          valueFrom:
            configMapKeyRef:
              name: unidesk-ci-budgets
              key: traceSummaryMs
        - name: TRACE_STEPS_BUDGET_MS
          valueFrom:
            configMapKeyRef:
              name: unidesk-ci-budgets
              key: traceStepsMs
        - name: TRACE_STEP_DETAIL_BUDGET_MS
          valueFrom:
            configMapKeyRef:
              name: unidesk-ci-budgets
              key: traceStepDetailMs
        - name: OVERVIEW_P95_BUDGET_MS
          valueFrom:
            configMapKeyRef:
              name: unidesk-ci-budgets
              key: overviewP95Ms
      script: |
        #!/usr/bin/env bash
        set -euo pipefail
        bun scripts/ci-code-queue-read-perf.ts
    - name: cleanup
      image: "$(params.app-image)"
      imagePullPolicy: Never
      env:
        - name: HTTP_PROXY
          value: ""
        - name: HTTPS_PROXY
          value: ""
        - name: ALL_PROXY
          value: ""
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster.local"
        - name: http_proxy
          value: ""
        - name: https_proxy
          value: ""
        - name: all_proxy
          value: ""
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster.local"
      script: |
        #!/bin/bash
        set -euo pipefail
        kube_api="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}"
        kube_token="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
        kube_ca="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
        kube_namespace="$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)"
        delete_resource() {
          local path="$1"
          local code
          code="$(curl -sS -o /tmp/unidesk-ci-delete-response -w "%{http_code}" --cacert "$kube_ca" -H "Authorization: Bearer $kube_token" -X DELETE "$kube_api/$path")"
          if [ "$code" = "200" ] || [ "$code" = "202" ] || [ "$code" = "404" ]; then
            return 0
          fi
          cat /tmp/unidesk-ci-delete-response >&2
          return 1
        }
        delete_resource "apis/apps/v1/namespaces/$kube_namespace/deployments/code-queue-ci-read"
        delete_resource "api/v1/namespaces/$kube_namespace/services/code-queue-ci-read"
---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: unidesk-dev-namespace-e2e
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: dev-namespace-e2e
spec:
  params:
    - name: repo-url
      type: string
      default: https://github.com/pikasTech/unidesk
    - name: desired-ref
      type: string
      default: master
    - name: deploy-commit
      type: string
    - name: environment
      type: string
      default: dev
    - name: run-id
      type: string
    - name: keep-namespace
      type: string
      default: "false"
    - name: app-image
      type: string
      default: unidesk-code-queue:dev
    - name: backend-core-commit
      type: string
      default: unknown
    - name: frontend-commit
      type: string
      default: unknown
    - name: code-queue-commit
      type: string
      default: unknown
    - name: deploy-json-b64
      type: string
      default: ""
  workspaces:
    - name: source
  steps:
    - name: namespace-smoke-e2e
      image: "$(params.app-image)"
      imagePullPolicy: Never
      env:
        - name: HTTP_PROXY
          value: ""
        - name: HTTPS_PROXY
          value: ""
        - name: ALL_PROXY
          value: ""
        - name: NO_PROXY
          value: "localhost,127.0.0.1,::1,kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster.local"
        - name: http_proxy
          value: ""
        - name: https_proxy
          value: ""
        - name: all_proxy
          value: ""
        - name: no_proxy
          value: "localhost,127.0.0.1,::1,kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster.local"
      script: |
        #!/usr/bin/env bash
        set -euo pipefail
        ns="unidesk-ci-e2e-$(params.run-id)"
        keep="$(params.keep-namespace)"
        run_dir="$(workspaces.source.path)/dev-e2e-$(params.run-id)"
        mkdir -p "$run_dir"
        result_json="$run_dir/dev-e2e-result.json"
        deploy_json_b64="$(params.deploy-json-b64)"
        backend_commit="$(params.backend-core-commit)"
        frontend_commit="$(params.frontend-commit)"
        code_queue_commit="$(params.code-queue-commit)"
        app_image="$(params.app-image)"
        database_url="postgres://unidesk_ci:unidesk_ci_password@postgres-dev.$ns.svc.cluster.local:5432/unidesk_ci"
        kube_api="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}"
        kube_token="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
        kube_ca="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
        kube() {
          local method="$1"
          shift
          curl -fsS --cacert "$kube_ca" -H "Authorization: Bearer $kube_token" -X "$method" "$@"
        }
        delete_path() {
          local path="$1"
          local code
          code="$(curl -sS -o /tmp/unidesk-dev-e2e-delete-response -w "%{http_code}" --cacert "$kube_ca" -H "Authorization: Bearer $kube_token" -X DELETE "$kube_api/$path")"
          if [ "$code" = "200" ] || [ "$code" = "202" ] || [ "$code" = "404" ]; then
            return 0
          fi
          cat /tmp/unidesk-dev-e2e-delete-response >&2
          return 1
        }
        proxy_get() {
          local service_name="$1"
          local path="$2"
          local output_path="${3:-}"
          local tries="${4:-1}"
          local response_path="/tmp/unidesk-dev-e2e-proxy-${service_name}-$(printf '%s' "$path" | tr -c 'A-Za-z0-9' '-').json"
          local code="000"
          local attempt=1
          while [ "$attempt" -le "$tries" ]; do
            code="$(curl -sS -o "$response_path" -w "%{http_code}" --cacert "$kube_ca" -H "Authorization: Bearer $kube_token" \
              "$kube_api/api/v1/namespaces/$ns/services/${service_name}:4222/proxy$path" || true)"
            if [ "$code" -ge 200 ] && [ "$code" -lt 300 ]; then
              if [ -n "$output_path" ]; then
                cp "$response_path" "$output_path"
              else
                cat "$response_path"
              fi
              return 0
            fi
            echo "dev_e2e_proxy_get_retry service=$service_name path=$path status=$code attempt=$attempt/$tries" >&2
            cat "$response_path" >&2 || true
            if [ "$attempt" -lt "$tries" ]; then sleep 2; fi
            attempt=$((attempt + 1))
          done
          if [ -n "$output_path" ]; then cp "$response_path" "$output_path" 2>/dev/null || true; fi
          return 22
        }
        proxy_json() {
          local method="$1"
          local service_name="$2"
          local path="$3"
          local body="${4:-}"
          local output_path="${5:-}"
          local tries="${6:-1}"
          local response_path="/tmp/unidesk-dev-e2e-proxy-${service_name}-${method}-$(printf '%s' "$path" | tr -c 'A-Za-z0-9' '-').json"
          local code="000"
          local attempt=1
          local curl_args=(--cacert "$kube_ca" -H "Authorization: Bearer $kube_token" -X "$method")
          if [ -n "$body" ]; then
            curl_args+=(-H "Content-Type: application/json" --data "$body")
          fi
          while [ "$attempt" -le "$tries" ]; do
            code="$(curl -sS -o "$response_path" -w "%{http_code}" "${curl_args[@]}" \
              "$kube_api/api/v1/namespaces/$ns/services/${service_name}:4222/proxy$path" || true)"
            if [ "$code" -ge 200 ] && [ "$code" -lt 300 ]; then
              if [ -n "$output_path" ]; then
                cp "$response_path" "$output_path"
              else
                cat "$response_path"
              fi
              return 0
            fi
            echo "dev_e2e_proxy_json_retry method=$method service=$service_name path=$path status=$code attempt=$attempt/$tries" >&2
            cat "$response_path" >&2 || true
            if [ "$attempt" -lt "$tries" ]; then sleep 2; fi
            attempt=$((attempt + 1))
          done
          if [ -n "$output_path" ]; then cp "$response_path" "$output_path" 2>/dev/null || true; fi
          return 22
        }
        wait_deployment_available() {
          local deployment="$1"
          local timeout_seconds="$2"
          local deadline=$((SECONDS + timeout_seconds))
          while [ "$SECONDS" -lt "$deadline" ]; do
            status="$(kube GET "$kube_api/apis/apps/v1/namespaces/$ns/deployments/$deployment")"
            replicas="$(printf '%s' "$status" | jq -r '.spec.replicas // 1')"
            available="$(printf '%s' "$status" | jq -r '.status.availableReplicas // 0')"
            updated="$(printf '%s' "$status" | jq -r '.status.updatedReplicas // 0')"
            observed="$(printf '%s' "$status" | jq -r '.status.observedGeneration // 0')"
            generation="$(printf '%s' "$status" | jq -r '.metadata.generation // 0')"
            if [ "$available" -ge "$replicas" ] && [ "$updated" -ge "$replicas" ] && [ "$observed" -ge "$generation" ]; then
              echo "dev_e2e_rollout=available deployment=$deployment namespace=$ns replicas=$available generation=$generation"
              return 0
            fi
            sleep 2
          done
          echo "dev_e2e_rollout=timeout deployment=$deployment namespace=$ns" >&2
          kube GET "$kube_api/apis/apps/v1/namespaces/$ns/deployments/$deployment" >&2
          return 1
        }
        apply_code_queue_role() {
          local role="$1"
          local readiness_path="$2"
          local scheduler_enabled="$3"
          local name="code-queue-${role}-dev"
          cat >/tmp/dev-e2e-code-queue-$role.yaml <<YAML
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: $name
          namespace: $ns
          labels:
            app.kubernetes.io/name: code-queue
            app.kubernetes.io/component: $role
            app.kubernetes.io/part-of: unidesk
            unidesk.ai/ci-run-id: "$(params.run-id)"
            unidesk.ai/deploy-service-id: code-queue
        spec:
          replicas: 1
          strategy:
            type: Recreate
          selector:
            matchLabels:
              app.kubernetes.io/name: code-queue
              app.kubernetes.io/component: $role
              unidesk.ai/ci-run-id: "$(params.run-id)"
          template:
            metadata:
              labels:
                app.kubernetes.io/name: code-queue
                app.kubernetes.io/component: $role
                app.kubernetes.io/part-of: unidesk
                unidesk.ai/ci-run-id: "$(params.run-id)"
                unidesk.ai/node-id: G14
                unidesk.ai/deploy-service-id: code-queue
            spec:
              nodeSelector:
                unidesk.ai/node-id: G14
              terminationGracePeriodSeconds: 10
              containers:
                - name: code-queue
                  image: "$app_image"
                  imagePullPolicy: Never
                  ports:
                    - name: http
                      containerPort: 4222
                  envFrom:
                    - secretRef:
                        name: code-queue-dev-e2e-env
                  env:
                    - name: HOST
                      value: "0.0.0.0"
                    - name: PORT
                      value: "4222"
                    - name: UNIDESK_ENV
                      value: dev
                    - name: UNIDESK_NAMESPACE
                      value: "$ns"
                    - name: UNIDESK_DATABASE_NAME
                      value: unidesk_ci
                    - name: UNIDESK_DEPLOY_REF
                      value: origin/master:deploy.json#environments.dev
                    - name: UNIDESK_DEPLOY_SERVICE_ID
                      value: code-queue
                    - name: CODE_QUEUE_DEPLOY_COMMIT
                      value: "$code_queue_commit"
                    - name: CODE_QUEUE_DEPLOY_REQUESTED_COMMIT
                      value: "$code_queue_commit"
                    - name: CODE_QUEUE_INSTANCE_ID
                      value: G14-dev-ci-$role
                    - name: CODE_QUEUE_SERVICE_ROLE
                      value: "$role"
                    - name: CODE_QUEUE_SCHEDULER_ENABLED
                      value: "$scheduler_enabled"
                    - name: CODE_QUEUE_MAX_ACTIVE_QUEUES
                      value: "0"
                    - name: CODE_QUEUE_DATABASE_POOL_MAX
                      value: "2"
                    - name: CODE_QUEUE_MAIN_PROVIDER_ID
                      value: G14-dev
                    - name: CODE_QUEUE_EXECUTION_PROVIDER_IDS
                      value: G14-dev
                    - name: CODE_QUEUE_WORKDIR
                      value: /workspace-dev
                    - name: CODE_QUEUE_REMOTE_WORKDIR
                      value: /root/unidesk-dev-workspace
                    - name: CODE_QUEUE_DATA_DIR
                      value: /var/lib/unidesk-dev-e2e/code-queue
                    - name: CODE_QUEUE_CODEX_HOME
                      value: /var/lib/unidesk-dev-e2e/code-queue/codex-home
                    - name: CODE_QUEUE_OPENCODE_XDG_DIR
                      value: /var/lib/unidesk-dev-e2e/code-queue/opencode-xdg
                    - name: CODE_QUEUE_EGRESS_PROXY_ENABLED
                      value: "false"
                    - name: CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED
                      value: "false"
                    - name: CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED
                      value: "false"
                    - name: CODE_QUEUE_CODEX_SQLITE_LOG_EXPORT_ENABLED
                      value: "false"
                    - name: OA_EVENT_FLOW_BASE_URL
                      value: http://127.0.0.1:9
                    - name: LOG_FILE
                      value: /var/log/unidesk/code-queue-$role-dev-e2e.jsonl
                    - name: NODE_OPTIONS
                      value: --max-old-space-size=512
                  readinessProbe:
                    httpGet:
                      path: $readiness_path
                      port: http
                    periodSeconds: 5
                    timeoutSeconds: 3
                    failureThreshold: 24
                  startupProbe:
                    httpGet:
                      path: /live
                      port: http
                    periodSeconds: 5
                    timeoutSeconds: 3
                    failureThreshold: 60
                  resources:
                    requests:
                      cpu: 80m
                      memory: 192Mi
                    limits:
                      cpu: 300m
                      memory: 768Mi
                  volumeMounts:
                    - name: state
                      mountPath: /var/lib/unidesk-dev-e2e/code-queue
                    - name: logs
                      mountPath: /var/log/unidesk
              volumes:
                - name: state
                  emptyDir: {}
                - name: logs
                  emptyDir: {}
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: $name
          namespace: $ns
          labels:
            app.kubernetes.io/name: code-queue
            app.kubernetes.io/component: $role
            app.kubernetes.io/part-of: unidesk
        spec:
          type: ClusterIP
          selector:
            app.kubernetes.io/name: code-queue
            app.kubernetes.io/component: $role
            unidesk.ai/ci-run-id: "$(params.run-id)"
          ports:
            - name: http
              port: 4222
              targetPort: http
        YAML
          csplit -s -f /tmp/dev-e2e-code-queue-$role- /tmp/dev-e2e-code-queue-$role.yaml '/^---$/' '{*}'
          kube PATCH -H "Content-Type: application/apply-patch+yaml" --data-binary @/tmp/dev-e2e-code-queue-$role-00 "$kube_api/apis/apps/v1/namespaces/$ns/deployments/$name?fieldManager=unidesk-ci&force=true" >/dev/null
          kube PATCH -H "Content-Type: application/apply-patch+yaml" --data-binary @/tmp/dev-e2e-code-queue-$role-01 "$kube_api/api/v1/namespaces/$ns/services/$name?fieldManager=unidesk-ci&force=true" >/dev/null
        }
        cleanup() {
          if [ "$keep" = "true" ]; then
            echo "dev_e2e_namespace_retained=$ns"
            return 0
          fi
          delete_path "api/v1/namespaces/$ns" || true
          echo "dev_e2e_namespace_deleted=$ns"
        }
        trap cleanup EXIT

        delete_path "api/v1/namespaces/$ns"
        cat >/tmp/dev-e2e-namespace.yaml <<YAML
        apiVersion: v1
        kind: Namespace
        metadata:
          name: $ns
          labels:
            app.kubernetes.io/part-of: unidesk
            unidesk.ai/purpose: ci-dev-e2e
            unidesk.ai/deploy-ref: master-deploy-json-dev
        YAML
        kube PATCH \
          -H "Content-Type: application/apply-patch+yaml" \
          --data-binary @/tmp/dev-e2e-namespace.yaml \
          "$kube_api/api/v1/namespaces/$ns?fieldManager=unidesk-ci&force=true" >/dev/null

        cat >/tmp/dev-e2e-configmap.yaml <<YAML
        apiVersion: v1
        kind: ConfigMap
        metadata:
          name: desired-manifest
          namespace: $ns
          labels:
            app.kubernetes.io/name: unidesk-dev-namespace-e2e
            app.kubernetes.io/part-of: unidesk
        data:
          deployCommit: "$(params.deploy-commit)"
          environment: "$(params.environment)"
          backendCoreCommit: "$backend_commit"
          frontendCommit: "$frontend_commit"
          codeQueueCommit: "$code_queue_commit"
          deploy.json.b64: "$deploy_json_b64"
        YAML
        kube PATCH \
          -H "Content-Type: application/apply-patch+yaml" \
          --data-binary @/tmp/dev-e2e-configmap.yaml \
          "$kube_api/api/v1/namespaces/$ns/configmaps/desired-manifest?fieldManager=unidesk-ci&force=true" >/dev/null

        database_url_b64="$(printf '%s' "$database_url" | base64 -w0)"
        cat >/tmp/dev-e2e-secret.yaml <<YAML
        apiVersion: v1
        kind: Secret
        metadata:
          name: code-queue-dev-e2e-env
          namespace: $ns
          labels:
            app.kubernetes.io/name: code-queue
            app.kubernetes.io/component: ci-dev-e2e
            app.kubernetes.io/part-of: unidesk
        type: Opaque
        data:
          DATABASE_URL: "$database_url_b64"
        YAML
        kube PATCH \
          -H "Content-Type: application/apply-patch+yaml" \
          --data-binary @/tmp/dev-e2e-secret.yaml \
          "$kube_api/api/v1/namespaces/$ns/secrets/code-queue-dev-e2e-env?fieldManager=unidesk-ci&force=true" >/dev/null

        cat >/tmp/dev-e2e-postgres.yaml <<YAML
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: postgres-dev
          namespace: $ns
          labels:
            app.kubernetes.io/name: postgres-dev
            app.kubernetes.io/component: database
            app.kubernetes.io/part-of: unidesk
        spec:
          replicas: 1
          selector:
            matchLabels:
              app.kubernetes.io/name: postgres-dev
              app.kubernetes.io/component: database
          template:
            metadata:
              labels:
                app.kubernetes.io/name: postgres-dev
                app.kubernetes.io/component: database
                app.kubernetes.io/part-of: unidesk
            spec:
              nodeSelector:
                unidesk.ai/node-id: G14
              terminationGracePeriodSeconds: 5
              containers:
                - name: postgres
                  image: postgres:16-alpine
                  imagePullPolicy: IfNotPresent
                  ports:
                    - name: postgres
                      containerPort: 5432
                  env:
                    - name: POSTGRES_USER
                      value: unidesk_ci
                    - name: POSTGRES_PASSWORD
                      value: unidesk_ci_password
                    - name: POSTGRES_DB
                      value: unidesk_ci
                  readinessProbe:
                    exec:
                      command:
                        - pg_isready
                        - -U
                        - unidesk_ci
                        - -d
                        - unidesk_ci
                    periodSeconds: 5
                    timeoutSeconds: 3
                    failureThreshold: 24
                  resources:
                    requests:
                      cpu: 50m
                      memory: 128Mi
                    limits:
                      cpu: 250m
                      memory: 512Mi
                  volumeMounts:
                    - name: data
                      mountPath: /var/lib/postgresql/data
              volumes:
                - name: data
                  emptyDir: {}
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: postgres-dev
          namespace: $ns
          labels:
            app.kubernetes.io/name: postgres-dev
            app.kubernetes.io/component: database
            app.kubernetes.io/part-of: unidesk
        spec:
          type: ClusterIP
          selector:
            app.kubernetes.io/name: postgres-dev
            app.kubernetes.io/component: database
          ports:
            - name: postgres
              port: 5432
              targetPort: postgres
        YAML
        csplit -s -f /tmp/dev-e2e-postgres- /tmp/dev-e2e-postgres.yaml '/^---$/' '{*}'
        kube PATCH \
          -H "Content-Type: application/apply-patch+yaml" \
          --data-binary @/tmp/dev-e2e-postgres-00 \
          "$kube_api/apis/apps/v1/namespaces/$ns/deployments/postgres-dev?fieldManager=unidesk-ci&force=true" >/dev/null
        kube PATCH \
          -H "Content-Type: application/apply-patch+yaml" \
          --data-binary @/tmp/dev-e2e-postgres-01 \
          "$kube_api/api/v1/namespaces/$ns/services/postgres-dev?fieldManager=unidesk-ci&force=true" >/dev/null
        wait_deployment_available postgres-dev 180

        apply_code_queue_role scheduler /health true
        apply_code_queue_role read /live false
        apply_code_queue_role write /health false
        wait_deployment_available code-queue-scheduler-dev 420
        wait_deployment_available code-queue-read-dev 420
        wait_deployment_available code-queue-write-dev 420

        proxy_get code-queue-write-dev /health "$run_dir/code-queue-write-health.json" 30
        proxy_get code-queue-scheduler-dev /health "$run_dir/code-queue-scheduler-health.json" 30
        proxy_get code-queue-read-dev /live "$run_dir/code-queue-read-live.json" 30
        proxy_get code-queue-read-dev /api/workdirs "$run_dir/code-queue-read-workdirs.json" 30
        proxy_json POST code-queue-write-dev /api/workdirs '{"providerId":"G14-dev","executionMode":"default","path":"ci-workdirs-smoke"}' "$run_dir/code-queue-workdir-created.json" 30
        proxy_get code-queue-write-dev '/api/workdirs?providerId=G14-dev&executionMode=default' "$run_dir/code-queue-write-workdirs.json" 30
        proxy_json DELETE code-queue-write-dev /api/workdirs/G14-dev/default/ci-workdirs-smoke "" "$run_dir/code-queue-workdir-deleted.json" 30

        bun - "$ns" "$(params.deploy-commit)" "$backend_commit" "$frontend_commit" "$code_queue_commit" "$app_image" "$result_json" "$run_dir" <<'BUN'
        const [ns, deployCommit, backendCommit, frontendCommit, codeQueueCommit, appImage, resultPath, runDir] = process.argv.slice(2);
        const runBase = new URL(`file://${runDir.replace(/\/+$/u, "")}/`);
        async function readJson(name) {
          return await Bun.file(new URL(name, runBase)).json();
        }
        const health = await readJson("code-queue-write-health.json");
        const scheduler = await readJson("code-queue-scheduler-health.json");
        const readLive = await readJson("code-queue-read-live.json");
        const initialWorkdirs = await readJson("code-queue-read-workdirs.json");
        const created = await readJson("code-queue-workdir-created.json");
        const listed = await readJson("code-queue-write-workdirs.json");
        const deleted = await readJson("code-queue-workdir-deleted.json");
        const checks = [
          health?.ok === true && health?.role === "write" && health?.deploy?.commit === codeQueueCommit,
          scheduler?.ok === true && scheduler?.role === "scheduler" && scheduler?.schedulerEnabled === true,
          readLive?.ok === true && readLive?.role === "read",
          initialWorkdirs?.ok === true && Array.isArray(initialWorkdirs?.workdirs),
          created?.ok === true && created?.workdir?.providerId === "G14-dev" && created?.workdir?.path === "/workspace-dev/ci-workdirs-smoke",
          Array.isArray(listed?.workdirs) && listed.workdirs.some((item) => item?.path === "/workspace-dev/ci-workdirs-smoke"),
          deleted?.ok === true,
        ];
        const result = {
          ok: checks.every(Boolean),
          namespace: ns,
          deployCommit,
          backendCoreCommit: backendCommit,
          frontendCommit,
          codeQueueCommit,
          codeQueueImage: appImage,
          accessPath: "kubernetes-api-service-proxy",
          services: ["code-queue-scheduler-dev", "code-queue-read-dev", "code-queue-write-dev"],
          health,
          scheduler,
          readLive,
          initialWorkdirs,
          created,
          listed,
          deleted,
        };
        await Bun.write(resultPath, `${JSON.stringify(result, null, 2)}\n`);
        console.log(JSON.stringify(result));
        if (!result.ok) {
          process.exit(1);
        }
        BUN
---
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: unidesk-dev-namespace-e2e
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/component: dev-namespace-e2e
    app.kubernetes.io/part-of: unidesk
spec:
  params:
    - name: repo-url
      type: string
      default: https://github.com/pikasTech/unidesk
    - name: desired-ref
      type: string
      default: master
    - name: deploy-commit
      type: string
    - name: environment
      type: string
      default: dev
    - name: run-id
      type: string
    - name: keep-namespace
      type: string
      default: "false"
    - name: app-image
      type: string
      default: unidesk-code-queue:dev
    - name: backend-core-commit
      type: string
      default: unknown
    - name: frontend-commit
      type: string
      default: unknown
    - name: code-queue-commit
      type: string
      default: unknown
    - name: deploy-json-b64
      type: string
      default: ""
  workspaces:
    - name: shared-workspace
  tasks:
    - name: dev-namespace-e2e
      taskRef:
        name: unidesk-dev-namespace-e2e
      params:
        - name: repo-url
          value: "$(params.repo-url)"
        - name: desired-ref
          value: "$(params.desired-ref)"
        - name: deploy-commit
          value: "$(params.deploy-commit)"
        - name: environment
          value: "$(params.environment)"
        - name: run-id
          value: "$(params.run-id)"
        - name: keep-namespace
          value: "$(params.keep-namespace)"
        - name: app-image
          value: "$(params.app-image)"
        - name: backend-core-commit
          value: "$(params.backend-core-commit)"
        - name: frontend-commit
          value: "$(params.frontend-commit)"
        - name: code-queue-commit
          value: "$(params.code-queue-commit)"
        - name: deploy-json-b64
          value: "$(params.deploy-json-b64)"
      workspaces:
        - name: source
          workspace: shared-workspace
---
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: unidesk-ci
  namespace: unidesk-ci
  labels:
    app.kubernetes.io/name: unidesk-ci
    app.kubernetes.io/part-of: unidesk
spec:
  params:
    - name: repo-url
      type: string
      default: https://github.com/pikasTech/unidesk
    - name: revision
      type: string
    - name: check-image
      type: string
      default: unidesk-code-queue:dev
    - name: code-queue-image
      type: string
      default: unidesk-code-queue:dev
  workspaces:
    - name: shared-workspace
  tasks:
    - name: repo-check
      taskRef:
        name: unidesk-repo-check
      params:
        - name: repo-url
          value: "$(params.repo-url)"
        - name: revision
          value: "$(params.revision)"
        - name: image
          value: "$(params.check-image)"
      workspaces:
        - name: source
          workspace: shared-workspace
    - name: code-queue-read-perf
      runAfter:
        - repo-check
      taskRef:
        name: unidesk-code-queue-read-perf
      params:
        - name: revision
          value: "$(params.revision)"
        - name: app-image
          value: "$(params.code-queue-image)"
      workspaces:
        - name: source
          workspace: shared-workspace
