apiVersion: tekton.dev/v1 kind: Pipeline metadata: name: agentrun-v01-ci-image-publish namespace: agentrun-ci labels: app.kubernetes.io/part-of: agentrun agentrun.pikastech.local/lane: v0.1 spec: params: - name: git-url type: string default: git@github.com:pikasTech/agentrun.git - name: git-read-url type: string default: http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/agentrun.git - name: git-write-url type: string default: http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/agentrun.git - name: source-branch type: string default: v0.1 - name: gitops-branch type: string default: v0.1-gitops - name: revision type: string - name: registry-prefix type: string default: 127.0.0.1:5000/agentrun - name: tools-image type: string default: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1 workspaces: - name: source - name: git-ssh tasks: - name: prepare-source workspaces: - name: source workspace: source taskSpec: params: - name: git-read-url - name: revision - name: tools-image workspaces: - name: source steps: - name: clone-and-check image: $(params.tools-image) env: - name: HTTP_PROXY value: http://127.0.0.1:10808 - name: HTTPS_PROXY value: http://127.0.0.1:10808 - name: NO_PROXY value: hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.42.0.0/16,10.43.0.0/16,.svc,.cluster.local - name: http_proxy value: http://127.0.0.1:10808 - name: https_proxy value: http://127.0.0.1:10808 - name: no_proxy value: hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.42.0.0/16,10.43.0.0/16,.svc,.cluster.local - name: GIT_TERMINAL_PROMPT value: "0" script: | #!/bin/sh set -eu rm -rf /workspace/source/repo mkdir -p /workspace/source/repo git init /workspace/source/repo cd /workspace/source/repo git remote add origin "$(params.git-read-url)" git fetch --depth=1 origin "$(params.revision)" git checkout --detach FETCH_HEAD test "$(git rev-parse HEAD)" = "$(params.revision)" bun install --frozen-lockfile bun run check bun scripts/agentrun-gitops-render.ts --out /tmp/agentrun-gitops-render-check --source-commit "$(params.revision)" --check AGENTRUN_SELFTEST_CODEX_COMMAND="$(command -v bun)" \ AGENTRUN_SELFTEST_CODEX_ARGS="[\"$PWD/src/selftest/fake-codex-app-server.ts\"]" \ bun run self-test params: - name: git-read-url value: $(params.git-read-url) - name: revision value: $(params.revision) - name: tools-image value: $(params.tools-image) - name: plan-artifacts runAfter: [prepare-source] workspaces: - name: source workspace: source taskSpec: params: - name: git-read-url - name: gitops-branch - name: revision - name: registry-prefix - name: tools-image results: - name: env-identity - name: build-count - name: reuse-count - name: summary workspaces: - name: source steps: - name: plan image: $(params.tools-image) env: - name: GIT_TERMINAL_PROMPT value: "0" script: | #!/bin/sh set -eu cd /workspace/source/repo node <<'NODE' > /workspace/source/env-identity const { createHash } = require("node:crypto"); const { readFileSync } = require("node:fs"); const inputs = [ ["toolsImage", "127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1"], ["systemPackages", "ca-certificates curl git kubectl nodejs openssh-client"], ["containerfile", readFileSync("deploy/container/Containerfile", "utf8")], ["bootScript", readFileSync("deploy/runtime/boot/agentrun-boot.sh", "utf8")], ["bootMgr", readFileSync("deploy/runtime/boot/agentrun-mgr.sh", "utf8")], ["bootRunner", readFileSync("deploy/runtime/boot/agentrun-runner.sh", "utf8")], ["packageJson", readFileSync("package.json", "utf8")], ["bunLock", readFileSync("bun.lock", "utf8")], ["tsconfig", readFileSync("tsconfig.json", "utf8")], ]; process.stdout.write(createHash("sha256").update(JSON.stringify(inputs)).digest("hex").slice(0, 20)); NODE env_identity="$(cat /workspace/source/env-identity)" rm -rf /workspace/source/gitops-prev if git clone --depth=1 --branch "$(params.gitops-branch)" "$(params.git-read-url)" /workspace/source/gitops-prev >/tmp/agentrun-prev-gitops.log 2>&1; then prev_catalog=/workspace/source/gitops-prev/deploy/artifact-catalog.v01.json else prev_catalog=/dev/null fi AGENTRUN_ENV_IDENTITY="$env_identity" \ AGENTRUN_PREV_CATALOG="$prev_catalog" \ AGENTRUN_REVISION="$(params.revision)" \ AGENTRUN_GITOPS_BRANCH="$(params.gitops-branch)" \ AGENTRUN_REGISTRY_PREFIX="$(params.registry-prefix)" \ node <<'NODE' const { readFileSync, writeFileSync } = require("node:fs"); const { execFileSync } = require("node:child_process"); const envIdentity = process.env.AGENTRUN_ENV_IDENTITY; const revision = process.env.AGENTRUN_REVISION; const gitopsBranch = process.env.AGENTRUN_GITOPS_BRANCH; const registryPrefix = process.env.AGENTRUN_REGISTRY_PREFIX; let previousService = null; try { const catalog = JSON.parse(readFileSync(process.env.AGENTRUN_PREV_CATALOG, "utf8")); previousService = (catalog.services || []).find((item) => item.serviceId === "agentrun-mgr" && item.envIdentity === envIdentity && /^sha256:[a-f0-9]{64}$/.test(item.envDigest || item.digest || "")) || null; } catch {} if (previousService === null) { try { const digest = execFileSync("curl", ["-fsSI", "-H", "Accept: application/vnd.docker.distribution.manifest.v2+json", `http://127.0.0.1:5000/v2/agentrun/agentrun-mgr-env/manifests/${envIdentity}`], { encoding: "utf8" }) .split(/\r?\n/) .map((line) => line.split(": ")) .find(([key]) => String(key).toLowerCase() === "docker-content-digest")?.[1] ?.trim(); if (/^sha256:[a-f0-9]{64}$/.test(digest || "")) { previousService = { serviceId: "agentrun-mgr", artifactKind: "env-reuse", status: "registry-reuse", image: `${registryPrefix}/agentrun-mgr-env:${envIdentity}`, digest, repositoryDigest: `${registryPrefix}/agentrun-mgr-env@${digest}`, imageTag: envIdentity, envIdentity, envImage: `${registryPrefix}/agentrun-mgr-env:${envIdentity}`, envDigest: digest, envRepositoryDigest: `${registryPrefix}/agentrun-mgr-env@${digest}`, bootCommit: null, }; } } catch {} } const reused = previousService !== null; const plan = { lane: "v0.1", sourceBranch: "v0.1", gitopsBranch, sourceCommitId: revision, envIdentity, toolchainInputs: ["127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1", "deploy/container/Containerfile", "deploy/runtime/boot/*.sh", "package.json", "bun.lock", "tsconfig.json", "tools:ca-certificates curl git kubectl nodejs openssh-client"], buildServices: reused ? [] : ["agentrun-mgr"], reusedServices: reused ? ["agentrun-mgr"] : [], unsafeReuseServices: [], previousService, summary: `build=${reused ? 0 : 1} reuse=${reused ? 1 : 0} unsafeReuse=0`, }; writeFileSync("/workspace/source/ci-plan.json", `${JSON.stringify(plan, null, 2)}\n`); writeFileSync("/tekton/results/env-identity", envIdentity); writeFileSync("/tekton/results/build-count", String(plan.buildServices.length)); writeFileSync("/tekton/results/reuse-count", String(plan.reusedServices.length)); writeFileSync("/tekton/results/summary", plan.summary); console.log(JSON.stringify({ event: "agentrun-ci-plan", status: "succeeded", sourceCommitId: revision, envIdentity, buildServices: plan.buildServices, reusedServices: plan.reusedServices, unsafeReuseServices: [], summary: plan.summary })); NODE params: - name: git-read-url value: $(params.git-read-url) - name: gitops-branch value: $(params.gitops-branch) - name: revision value: $(params.revision) - name: registry-prefix value: $(params.registry-prefix) - name: tools-image value: $(params.tools-image) - name: image-publish runAfter: [plan-artifacts] workspaces: - name: source workspace: source taskSpec: params: - name: revision - name: registry-prefix - name: tools-image results: - name: image - name: digest - name: repository-digest - name: env-identity - name: status sidecars: - name: buildkitd image: moby/buildkit:rootless args: - --addr - unix:///workspace/buildkit-run/buildkitd.sock - --oci-worker-no-process-sandbox env: - name: HTTP_PROXY value: http://127.0.0.1:10808 - name: HTTPS_PROXY value: http://127.0.0.1:10808 - name: NO_PROXY value: hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.42.0.0/16,10.43.0.0/16,.svc,.cluster.local - name: http_proxy value: http://127.0.0.1:10808 - name: https_proxy value: http://127.0.0.1:10808 - name: no_proxy value: hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.42.0.0/16,10.43.0.0/16,.svc,.cluster.local securityContext: allowPrivilegeEscalation: true appArmorProfile: type: Unconfined seccompProfile: type: Unconfined runAsUser: 1000 runAsGroup: 1000 volumeMounts: - name: buildkit-run mountPath: /workspace/buildkit-run steps: - name: prepare-buildctl image: moby/buildkit:rootless script: | #!/bin/sh set -eu mkdir -p /workspace/buildkit-bin cp /usr/bin/buildctl /workspace/buildkit-bin/buildctl chmod +x /workspace/buildkit-bin/buildctl volumeMounts: - name: buildkit-bin mountPath: /workspace/buildkit-bin - name: build-or-reuse image: $(params.tools-image) env: - name: HTTP_PROXY value: http://127.0.0.1:10808 - name: HTTPS_PROXY value: http://127.0.0.1:10808 - name: NO_PROXY value: hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.42.0.0/16,10.43.0.0/16,.svc,.cluster.local script: | #!/bin/sh set -eu cd /workspace/source/repo env_identity="$(cat /workspace/source/env-identity)" if node -e 'const p=require("/workspace/source/ci-plan.json"); process.exit((p.buildServices||[]).length===0 ? 0 : 1)'; then node <<'NODE' const { readFileSync, writeFileSync } = require("node:fs"); const plan = JSON.parse(readFileSync("/workspace/source/ci-plan.json", "utf8")); const service = plan.previousService; if (!service) throw new Error("reuse plan missing previousService"); const image = service.envImage || service.image; const digest = service.envDigest || service.digest; const repositoryDigest = service.envRepositoryDigest || service.repositoryDigest; writeFileSync("/tekton/results/image", image); writeFileSync("/tekton/results/digest", digest); writeFileSync("/tekton/results/repository-digest", repositoryDigest); writeFileSync("/tekton/results/env-identity", plan.envIdentity); writeFileSync("/tekton/results/status", "reused"); console.log(JSON.stringify({ event: "agentrun-env-image", status: "reused", serviceId: "agentrun-mgr", envIdentity: plan.envIdentity, image, digest, summary: plan.summary })); NODE exit 0 fi image="$(params.registry-prefix)/agentrun-mgr-env:${env_identity}" buildctl=/workspace/buildkit-bin/buildctl for attempt in $(seq 1 60); do if "$buildctl" --addr unix:///workspace/buildkit-run/buildkitd.sock debug workers >/dev/null 2>&1; then break; fi sleep 1 done "$buildctl" --addr unix:///workspace/buildkit-run/buildkitd.sock build \ --frontend dockerfile.v0 \ --local context=. \ --local dockerfile=deploy/container \ --opt filename=Containerfile \ --opt build-arg:HTTP_PROXY=http://127.0.0.1:10808 \ --opt build-arg:HTTPS_PROXY=http://127.0.0.1:10808 \ --opt build-arg:NO_PROXY=hyueapi.com,.hyueapi.com,127.0.0.1,localhost,::1,10.42.0.0/16,10.43.0.0/16,.svc,.cluster.local \ --output type=image,name="$image",push=true,registry.insecure=true digest="$(curl -fsSI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "http://127.0.0.1:5000/v2/agentrun/agentrun-mgr-env/manifests/$env_identity" | awk -F': ' 'tolower($1)=="docker-content-digest" {gsub(/\r/,"",$2); print $2; exit}')" test -n "$digest" curl -fsSI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "http://127.0.0.1:5000/v2/agentrun/agentrun-mgr-env/manifests/$digest" >/dev/null printf '%s' "$image" > /tekton/results/image printf '%s' "$digest" > /tekton/results/digest printf '%s' "$(params.registry-prefix)/agentrun-mgr-env@$digest" > /tekton/results/repository-digest printf '%s' "$env_identity" > /tekton/results/env-identity printf '%s' built > /tekton/results/status printf '{"event":"agentrun-env-image","status":"built","serviceId":"agentrun-mgr","envIdentity":"%s","image":"%s","digest":"%s"}\n' "$env_identity" "$image" "$digest" volumeMounts: - name: buildkit-bin mountPath: /workspace/buildkit-bin - name: buildkit-run mountPath: /workspace/buildkit-run volumes: - name: buildkit-bin emptyDir: {} - name: buildkit-run emptyDir: {} params: - name: revision value: $(params.revision) - name: registry-prefix value: $(params.registry-prefix) - name: tools-image value: $(params.tools-image) - name: gitops-promote runAfter: [image-publish] workspaces: - name: source workspace: source taskSpec: params: - name: git-write-url - name: gitops-branch - name: revision - name: registry-prefix - name: image - name: digest - name: repository-digest - name: env-identity - name: image-status - name: tools-image workspaces: - name: source steps: - name: promote image: $(params.tools-image) env: - name: GIT_TERMINAL_PROMPT value: "0" - name: AGENTRUN_IMAGE value: $(params.image) - name: AGENTRUN_DIGEST value: $(params.digest) - name: AGENTRUN_REPOSITORY_DIGEST value: $(params.repository-digest) - name: AGENTRUN_ENV_IDENTITY value: $(params.env-identity) - name: AGENTRUN_IMAGE_STATUS value: $(params.image-status) - name: AGENTRUN_REVISION value: $(params.revision) - name: AGENTRUN_GITOPS_BRANCH value: $(params.gitops-branch) script: | #!/bin/sh set -eu cd /workspace/source/repo node <<'NODE' > /workspace/source/artifact-catalog.v01.json const { readFileSync } = require("node:fs"); const plan = JSON.parse(readFileSync("/workspace/source/ci-plan.json", "utf8")); const service = { serviceId: "agentrun-mgr", artifactKind: "env-reuse", status: process.env.AGENTRUN_IMAGE_STATUS, image: process.env.AGENTRUN_IMAGE, digest: process.env.AGENTRUN_DIGEST, repositoryDigest: process.env.AGENTRUN_REPOSITORY_DIGEST, imageTag: process.env.AGENTRUN_ENV_IDENTITY, envIdentity: process.env.AGENTRUN_ENV_IDENTITY, envImage: process.env.AGENTRUN_IMAGE, envDigest: process.env.AGENTRUN_DIGEST, envRepositoryDigest: process.env.AGENTRUN_REPOSITORY_DIGEST, bootCommit: process.env.AGENTRUN_REVISION, bootScript: "deploy/runtime/boot/agentrun-boot.sh", provenance: { sourceCommitId: process.env.AGENTRUN_REVISION, toolchainInputs: plan.toolchainInputs, buildServices: plan.buildServices, reusedServices: plan.reusedServices, unsafeReuseServices: plan.unsafeReuseServices, previousSourceCommitId: plan.previousService?.bootCommit || null, }, }; const catalog = { lane: "v0.1", sourceBranch: "v0.1", gitopsBranch: process.env.AGENTRUN_GITOPS_BRANCH, sourceCommitId: process.env.AGENTRUN_REVISION, summary: plan.summary, services: [service], }; console.log(JSON.stringify(catalog, null, 2)); NODE rm -rf /workspace/source/rendered bun scripts/agentrun-gitops-render.ts --out /workspace/source/rendered --source-commit "$(params.revision)" --registry-prefix "$(params.registry-prefix)" --catalog /workspace/source/artifact-catalog.v01.json --require-catalog rm -rf /workspace/source/gitops git clone --branch "$(params.gitops-branch)" "$(params.git-write-url)" /workspace/source/gitops || { git clone "$(params.git-write-url)" /workspace/source/gitops cd /workspace/source/gitops git checkout --orphan "$(params.gitops-branch)" git rm -rf . >/dev/null 2>&1 || true } cd /workspace/source/gitops git config user.email agentrun-ci@g14.local git config user.name agentrun-ci mkdir -p deploy/gitops/g14 deploy rm -rf deploy/gitops/g14/runtime-v01 deploy/gitops/g14/argocd cp -a /workspace/source/rendered/runtime-v01 deploy/gitops/g14/runtime-v01 cp -a /workspace/source/rendered/argocd deploy/gitops/g14/argocd cp /workspace/source/rendered/artifact-catalog.v01.json deploy/artifact-catalog.v01.json git add deploy git commit -m "gitops: promote agentrun v0.1 $(params.revision)" || true git push origin "$(params.gitops-branch)" printf '{"event":"agentrun-gitops-promote","status":"succeeded","sourceCommitId":"%s","envIdentity":"%s","imageStatus":"%s","summary":%s}\n' "$(params.revision)" "$(params.env-identity)" "$(params.image-status)" "$(node -e 'const p=require("/workspace/source/ci-plan.json"); console.log(JSON.stringify(p.summary))')" params: - name: git-write-url value: $(params.git-write-url) - name: gitops-branch value: $(params.gitops-branch) - name: revision value: $(params.revision) - name: registry-prefix value: $(params.registry-prefix) - name: image value: $(tasks.image-publish.results.image) - name: digest value: $(tasks.image-publish.results.digest) - name: repository-digest value: $(tasks.image-publish.results.repository-digest) - name: env-identity value: $(tasks.image-publish.results.env-identity) - name: image-status value: $(tasks.image-publish.results.status) - name: tools-image value: $(params.tools-image)