refactor: primitive G14 CI without Docker fallback

This commit is contained in:
Codex
2026-05-27 00:53:37 +08:00
parent e56fece528
commit 97846207a3
7 changed files with 122 additions and 253 deletions
+69 -100
View File
@@ -24,7 +24,6 @@ const defaultNoProxy = process.env.HWLAB_G14_NO_PROXY || "hyueapi.com,.hyueapi.c
const ciToolsRunnerImage = process.env.HWLAB_G14_CI_TOOLS_IMAGE || "127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-v1";
const buildkitRunnerImage = process.env.HWLAB_G14_BUILDKIT_IMAGE || "moby/buildkit:rootless";
const defaultDevBaseImage = process.env.HWLAB_G14_DEV_BASE_IMAGE || "127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim";
const tektonSingleImagePublish = process.env.HWLAB_G14_TEKTON_SINGLE_IMAGE_PUBLISH === "1";
const defaultServiceIds = Object.freeze([
"hwlab-cloud-api",
"hwlab-cloud-web",
@@ -61,6 +60,66 @@ const primitiveValidationTasks = Object.freeze([
"render_check_revision=$(node --input-type=module -e \"import fs from 'node:fs'; const data = JSON.parse(fs.readFileSync('deploy/gitops/g14/source.json', 'utf8')); process.stdout.write(data.sourceCommit);\")",
"test -n \"$render_check_revision\"",
"node scripts/g14-gitops-render.mjs --check --source-revision \"$render_check_revision\"",
`node --input-type=module <<'NODE'
import { readdirSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
const root = process.cwd();
const ciFileName = "CI" + ".json";
const forbiddenCiFragments = [
"ci" + "-json",
"HWLAB_G14_TEKTON" + "_SINGLE_IMAGE_PUBLISH",
"single" + "-dind",
"docker" + ":29",
"DOCKER" + "_HOST",
"Docker" + "-in-Docker",
"D" + "IND",
"docker" + " push",
"--build" + "-backend",
"HWLAB_ARTIFACT" + "_BUILD_BACKEND",
"--legacy" + "-source-images",
"HWLAB_G14_USE_DEPLOY_IMAGES" + "=0"
];
const scanTargets = [
"scripts/g14-gitops-render.mjs",
"scripts/artifact-publish.mjs",
"scripts/g14-artifact-publish.mjs",
"scripts/src/g14-ci-plan-lib.mjs",
"deploy/gitops/g14/tekton"
];
const skipDirs = new Set([".git", "node_modules", ".worktree"]);
function walkFiles(relativePath) {
const absolutePath = path.join(root, relativePath);
let stat;
try {
stat = statSync(absolutePath);
} catch {
return [];
}
if (stat.isFile()) return [relativePath];
if (!stat.isDirectory()) return [];
const files = [];
for (const entry of readdirSync(absolutePath, { withFileTypes: true })) {
if (entry.isDirectory() && skipDirs.has(entry.name)) continue;
files.push(...walkFiles(path.join(relativePath, entry.name)));
}
return files;
}
const ciFiles = walkFiles(".").filter((filePath) => path.basename(filePath) === ciFileName);
const violations = [];
for (const filePath of scanTargets.flatMap(walkFiles)) {
const text = readFileSync(path.join(root, filePath), "utf8");
for (const fragment of forbiddenCiFragments) {
if (text.includes(fragment)) violations.push({ filePath, fragment });
}
}
if (ciFiles.length || violations.length) {
console.error(JSON.stringify({ status: "failed", ciFiles, violations }, null, 2));
process.exit(1);
}
NODE`,
"node --check scripts/artifact-publish.mjs",
"node --check scripts/g14-artifact-publish.mjs"
]
@@ -100,7 +159,7 @@ function parseArgs(argv) {
webEndpoint: defaultWebEndpoint,
prodRuntimeEndpoint: defaultProdRuntimeEndpoint,
prodWebEndpoint: defaultProdWebEndpoint,
useDeployImages: process.env.HWLAB_G14_USE_DEPLOY_IMAGES !== "0",
useDeployImages: true,
check: false,
write: true,
help: false
@@ -118,7 +177,6 @@ function parseArgs(argv) {
else if (arg === "--prod-runtime-endpoint") args.prodRuntimeEndpoint = readOption(argv, ++index, arg);
else if (arg === "--prod-web-endpoint") args.prodWebEndpoint = readOption(argv, ++index, arg);
else if (arg === "--use-deploy-images") args.useDeployImages = true;
else if (arg === "--legacy-source-images") args.useDeployImages = false;
else if (arg === "--check") {
args.check = true;
args.write = false;
@@ -152,8 +210,7 @@ function usage() {
` --web-endpoint URL default: ${defaultWebEndpoint}`,
` --prod-runtime-endpoint URL default: ${defaultProdRuntimeEndpoint}`,
` --prod-web-endpoint URL default: ${defaultProdWebEndpoint}`,
" --use-deploy-images default: render workload images/env from deploy/deploy.json per service",
" --legacy-source-images render every workload with the source revision tag instead of per-service desired images",
" --use-deploy-images default and only supported mode: render workload images/env from deploy/deploy.json per service",
" --check verify generated files are current without writing",
" --no-write plan and print generated file list without writing"
].join("\n");
@@ -669,39 +726,6 @@ function primitiveValidationTask(task) {
};
}
function publishScript() {
return `#!/bin/sh
set -eu
${dependencyProxyProbeScript("image-publish-proxy-preflight", "http://deb.debian.org/debian/dists/bookworm/InRelease")}
echo '{"event":"ci-base-image","phase":"image-publish","image":"${ciToolsRunnerImage}","policy":"no-runtime-apk"}'
for tool in node npm git python3 ssh docker; do command -v "$tool" >/dev/null 2>&1 || { echo '{"event":"ci-base-image","phase":"image-publish","ok":false,"reason":"missing-tool","tool":"'"$tool"'"}'; exit 31; }; done
export DOCKER_HOST=unix:///var/run/docker.sock
export HWLAB_CI_ARTIFACT_RUN_ID="$(context.pipelineRun.name)"
export HWLAB_CI_ARTIFACT_RUN_OWNER="tekton"
export HWLAB_DEV_REGISTRY_PREFIX="$(params.registry-prefix)"
export HWLAB_DEV_REGISTRY_K8S_NATIVE=1
if [ -n "$(params.base-image)" ]; then export HWLAB_DEV_BASE_IMAGE="$(params.base-image)"; fi
for i in $(seq 1 90); do docker info >/dev/null 2>&1 && break; sleep 2; done
docker info >/dev/null
if [ -n "\${HWLAB_DEV_BASE_IMAGE:-}" ]; then
echo '{"event":"dependency-local-registry-probe-start","phase":"image-publish-pre-base-image","target":"http://127.0.0.1:5000/v2/"}'
registry_started_at="$(date +%s)"
curl -fsS --max-time 10 http://127.0.0.1:5000/v2/ >/dev/null
registry_finished_at="$(date +%s)"
echo '{"event":"dependency-local-registry-probe","phase":"image-publish-pre-base-image","ok":true,"durationSeconds":'"$((registry_finished_at - registry_started_at))"'}'
started_at="$(date +%s)"
echo '{"event":"dependency-download-start","phase":"image-publish-docker-pull","image":"'"$HWLAB_DEV_BASE_IMAGE"'"}'
docker pull "$HWLAB_DEV_BASE_IMAGE"
finished_at="$(date +%s)"
echo '{"event":"dependency-download-complete","phase":"image-publish-docker-pull","image":"'"$HWLAB_DEV_BASE_IMAGE"'","durationSeconds":'"$((finished_at - started_at))"'}'
fi
cd /workspace/source/repo
test "$(git rev-parse HEAD)" = "$(params.revision)"
node scripts/g14-artifact-publish.mjs --publish --registry-prefix "$(params.registry-prefix)" --report /workspace/source/dev-artifacts.json --quiet-build --concurrency "$(params.concurrency)" --services "$(params.services)"
node scripts/refresh-artifact-catalog.mjs --target-ref HEAD --publish-report /workspace/source/dev-artifacts.json --no-write
`;
}
function planArtifactsScript() {
return `#!/bin/sh
set -eu
@@ -801,7 +825,7 @@ if [ "$buildkit_ready" != "1" ]; then
echo '{"event":"buildkit-sidecar-not-ready","serviceId":"'"$(params.service-id)"'","addr":"'"$HWLAB_BUILDKIT_ADDR"'"}' >&2
exit 1
fi
node scripts/g14-artifact-publish.mjs --publish --registry-prefix "$(params.registry-prefix)" --no-report --tekton-results-dir /tekton/results --quiet-build --concurrency 1 --services "$(params.service-id)" --build-backend buildkit --buildkit-command /workspace/buildkit-bin/buildctl --buildkit-addr "$HWLAB_BUILDKIT_ADDR"
node scripts/g14-artifact-publish.mjs --publish --registry-prefix "$(params.registry-prefix)" --no-report --tekton-results-dir /tekton/results --quiet-build --concurrency 1 --services "$(params.service-id)" --buildkit-command /workspace/buildkit-bin/buildctl --buildkit-addr "$HWLAB_BUILDKIT_ADDR"
`;
}
@@ -1101,8 +1125,7 @@ const pipelineRun = {
{ name: "revision", value: revision },
{ name: "registry-prefix", value: env("REGISTRY_PREFIX", "127.0.0.1:5000/hwlab") },
{ name: "services", value: env("SERVICES", "${defaultServicesParam}") },
{ name: "base-image", value: env("BASE_IMAGE", "${defaultDevBaseImage}") },
{ name: "concurrency", value: env("CONCURRENCY", "4") }
{ name: "base-image", value: env("BASE_IMAGE", "${defaultDevBaseImage}") }
],
workspaces: [
{ name: "source", volumeClaimTemplate: { spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "8Gi" } } } } },
@@ -1290,56 +1313,6 @@ function buildkitSidecar() {
};
}
function dockerRunVolume() {
return { name: "docker-run", emptyDir: {} };
}
function dockerSidecar() {
return {
name: "docker",
image: "docker:29-dind",
command: ["dockerd"],
args: ["--host=unix:///var/run/docker.sock", "--storage-driver=vfs"],
env: [{ name: "DOCKER_TLS_CERTDIR", value: "" }, ...proxyEnv()],
securityContext: { privileged: true },
volumeMounts: [{ name: "docker-run", mountPath: "/var/run" }]
};
}
function legacyImagePublishTask() {
return {
name: "image-publish",
runAfter: primitiveValidationTaskNames(),
workspaces: [{ name: "source", workspace: "source" }],
taskSpec: {
params: [
{ name: "revision" },
{ name: "registry-prefix" },
{ name: "services" },
{ name: "base-image" },
{ name: "concurrency" }
],
workspaces: [{ name: "source" }],
volumes: [dockerRunVolume()],
sidecars: [dockerSidecar()],
steps: [{
name: "publish",
image: ciToolsRunnerImage,
env: [{ name: "DOCKER_HOST", value: "unix:///var/run/docker.sock" }, ...proxyEnv()],
volumeMounts: [{ name: "docker-run", mountPath: "/var/run" }],
script: publishScript()
}]
},
params: [
{ name: "revision", value: "$(params.revision)" },
{ name: "registry-prefix", value: "$(params.registry-prefix)" },
{ name: "services", value: "$(params.services)" },
{ name: "base-image", value: "$(params.base-image)" },
{ name: "concurrency", value: "$(params.concurrency)" }
]
};
}
function planArtifactsTask() {
return {
name: "plan-artifacts",
@@ -1432,7 +1405,6 @@ function collectArtifactsTask() {
}
function imagePublishTaskSet() {
if (tektonSingleImagePublish) return [legacyImagePublishTask()];
return [
planArtifactsTask(),
...defaultServiceIds.map(perServiceBuildTask),
@@ -1454,7 +1426,7 @@ function tektonPipeline() {
annotations: {
"hwlab.pikastech.local/source-config": "scripts/g14-gitops-render.mjs#tekton-native-primitive-ci",
"hwlab.pikastech.local/ci-contract": "tekton-native-primitive-tasks",
"hwlab.pikastech.local/policy": tektonSingleImagePublish ? "single-dind-image-publish-compat" : "native-per-service-taskrun-image-publish"
"hwlab.pikastech.local/policy": "native-per-service-taskrun-image-publish"
}
},
spec: {
@@ -1465,8 +1437,7 @@ function tektonPipeline() {
{ name: "revision", type: "string", description: "Full git commit SHA; the only source truth for image tags." },
{ name: "registry-prefix", type: "string", default: defaultRegistryPrefix },
{ name: "services", type: "string", default: defaultServicesParam },
{ name: "base-image", type: "string", default: defaultDevBaseImage },
{ name: "concurrency", type: "string", default: "4" }
{ name: "base-image", type: "string", default: defaultDevBaseImage }
],
workspaces: [
{ name: "source" },
@@ -1498,7 +1469,7 @@ function tektonPipeline() {
...imagePublishTaskSet(),
{
name: "gitops-promote",
runAfter: [tektonSingleImagePublish ? "image-publish" : "collect-artifacts"],
runAfter: ["collect-artifacts"],
workspaces: [
{ name: "source", workspace: "source" },
{ name: "git-ssh", workspace: "git-ssh" }
@@ -1556,8 +1527,7 @@ function tektonPipelineRunTemplate({ source, args }) {
{ name: "gitops-branch", value: args.gitopsBranch },
{ name: "revision", value: source.full },
{ name: "registry-prefix", value: args.registryPrefix },
{ name: "base-image", value: defaultDevBaseImage },
{ name: "concurrency", value: "4" }
{ name: "base-image", value: defaultDevBaseImage }
],
workspaces: [
{ name: "source", volumeClaimTemplate: { spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "8Gi" } } } } },
@@ -1616,8 +1586,7 @@ function tektonPollerCronJob(args) {
{ name: "GITOPS_BRANCH", value: args.gitopsBranch },
{ name: "REGISTRY_PREFIX", value: args.registryPrefix },
{ name: "SERVICES", value: defaultServicesParam },
{ name: "BASE_IMAGE", value: defaultDevBaseImage },
{ name: "CONCURRENCY", value: "4" }
{ name: "BASE_IMAGE", value: defaultDevBaseImage }
],
volumeMounts: [{ name: "git-ssh", mountPath: "/workspace/git-ssh", readOnly: true }],
command: ["/bin/sh", "-c"],