fix: harden G14 CI dependency diagnostics

This commit is contained in:
Codex
2026-05-25 13:13:45 +08:00
parent d73fad089f
commit 230ac9dcb8
3 changed files with 146 additions and 42 deletions
+126 -1
View File
@@ -295,17 +295,132 @@ function shellSingleQuote(value) {
return `'${String(value).replace(/'/g, `'"'"'`)}'`;
}
function dependencyProxyProbeScript(phase, targetUrl) {
return `HWLAB_PROXY_PROBE_PHASE=${shellSingleQuote(phase)} HWLAB_PROXY_PROBE_URL=${shellSingleQuote(targetUrl)} node <<'NODE'
const net = require("node:net");
const phase = process.env.HWLAB_PROXY_PROBE_PHASE || "unknown";
const target = new URL(process.env.HWLAB_PROXY_PROBE_URL || "http://deb.debian.org/debian/dists/bookworm/InRelease");
const proxyRaw = process.env.HTTP_PROXY || process.env.http_proxy || "";
function redactProxy(value) {
return String(value || "")
.replace(/(https?:\/\/)([^/@]+)@/u, "$1***@")
.replace(/(socks5h?:\/\/)([^/@]+)@/u, "$1***@");
}
function emit(payload, exitCode = 0) {
console.log(JSON.stringify({
event: "dependency-proxy-probe",
phase,
target: target.href,
proxy: redactProxy(proxyRaw),
...payload
}));
if (exitCode) process.exit(exitCode);
}
if (!proxyRaw) emit({ ok: false, reason: "proxy-env-missing" }, 21);
let proxy;
try {
proxy = new URL(proxyRaw);
} catch (error) {
emit({ ok: false, reason: "proxy-url-invalid", error: error.message }, 22);
}
if (proxy.protocol !== "http:" && proxy.protocol !== "https:") {
emit({ ok: false, reason: "http-proxy-required", protocol: proxy.protocol }, 23);
}
const startedAt = Date.now();
const socket = net.createConnection({ host: proxy.hostname, port: Number(proxy.port || (proxy.protocol === "https:" ? 443 : 80)) });
let bytes = 0;
let firstByteMs = null;
let responseHead = "";
let finished = false;
const timeout = setTimeout(() => {
if (finished) return;
finished = true;
socket.destroy();
emit({ ok: false, reason: "timeout", timeoutMs: 15000 }, 24);
}, 15000);
socket.on("connect", () => {
socket.write("GET " + target.href + " HTTP/1.1\\r\\nHost: " + target.host + "\\r\\nUser-Agent: hwlab-g14-proxy-probe\\r\\nConnection: close\\r\\n\\r\\n");
});
socket.on("data", (chunk) => {
if (firstByteMs === null) firstByteMs = Date.now() - startedAt;
bytes += chunk.length;
if (responseHead.length < 240) responseHead += chunk.toString("utf8", 0, Math.min(chunk.length, 240 - responseHead.length));
});
socket.on("end", () => {
if (finished) return;
finished = true;
clearTimeout(timeout);
const totalMs = Date.now() - startedAt;
const statusLine = responseHead.split("\\r\\n", 1)[0] || "";
const statusCode = Number(statusLine.split(" ")[1] || 0);
const ok = statusLine.startsWith("HTTP/") && statusCode >= 200 && statusCode < 400;
emit({
ok,
reason: ok ? null : "bad-http-status",
statusLine,
statusCode,
firstByteMs,
totalMs,
bytes,
speedBytesPerSecond: totalMs > 0 ? Math.round(bytes * 1000 / totalMs) : 0
}, ok ? 0 : 25);
});
socket.on("error", (error) => {
if (finished) return;
finished = true;
clearTimeout(timeout);
emit({ ok: false, reason: "proxy-connect-failed", error: error.message, totalMs: Date.now() - startedAt }, 26);
});
NODE`;
}
function curlDownloadProbeFunction() {
return `redact_proxy_value() {
printf '%s' "\${1:-}" | sed -E 's#(https?://)[^/@]+@#\\1***@#; s#(socks5h?://)[^/@]+@#\\1***@#'
}
curl_dependency_probe() {
phase="$1"
url="$2"
proxy="\${HTTPS_PROXY:-\${https_proxy:-\${HTTP_PROXY:-\${http_proxy:-}}}}"
if [ -z "$proxy" ]; then
echo '{"event":"dependency-curl-probe","phase":"'"$phase"'","ok":false,"reason":"proxy-env-missing"}'
return 21
fi
safe_proxy="$(redact_proxy_value "$proxy")"
echo '{"event":"dependency-curl-probe-start","phase":"'"$phase"'","proxy":"'"$safe_proxy"'","target":"'"$url"'"}'
curl -sS -L --range 0-1048575 -o /dev/null --connect-timeout 15 --max-time 60 --proxy "$proxy" \
-w '{"event":"dependency-curl-probe","phase":"'"$phase"'","http_code":"%{http_code}","time_namelookup":%{time_namelookup},"time_connect":%{time_connect},"time_starttransfer":%{time_starttransfer},"time_total":%{time_total},"speed_download":%{speed_download},"size_download":%{size_download},"remote_ip":"%{remote_ip}"}\n' \
"$url"
}`;
}
function ciJsonScript(ci) {
const commands = ci.commands ?? [];
const lines = [
"#!/bin/sh",
"set -eu",
dependencyProxyProbeScript("ci-json-pre-apt", "http://deb.debian.org/debian/dists/bookworm/InRelease"),
"if ! command -v git >/dev/null 2>&1 || ! command -v python3 >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then",
" export DEBIAN_FRONTEND=noninteractive",
" apt-get update",
" apt-get install -y --no-install-recommends git python3 openssh-client ca-certificates",
" apt-get install -y --no-install-recommends git python3 openssh-client ca-certificates curl",
" rm -rf /var/lib/apt/lists/*",
"fi",
curlDownloadProbeFunction(),
"curl_dependency_probe \"ci-json-pre-npm\" \"https://registry.npmjs.org/npm/latest\"",
"mkdir -p /root/.ssh",
"cp /workspace/git-ssh/ssh-privatekey /root/.ssh/id_rsa",
"chmod 600 /root/.ssh/id_rsa",
@@ -316,6 +431,8 @@ function ciJsonScript(ci) {
"git checkout \"$(params.revision)\"",
"test \"$(git rev-parse HEAD)\" = \"$(params.revision)\"",
"npm ci --ignore-scripts",
"playwright_browser_probe_url=\"$(node -e 'const fs=require(\"node:fs\"); let url=\"\"; try { const p=require.resolve(\"playwright-core/browsers.json\"); const data=JSON.parse(fs.readFileSync(p,\"utf8\")); const b=data.browsers.find((item)=>item.name===\"chromium\"); if (b?.revision) url=\"https://playwright.azureedge.net/builds/chromium/\"+b.revision+\"/chromium-linux.zip\"; } catch {} process.stdout.write(url);' 2>/dev/null || true)\"",
"if [ -n \"$playwright_browser_probe_url\" ]; then curl_dependency_probe \"ci-json-pre-playwright\" \"$playwright_browser_probe_url\"; fi",
"npx playwright install --with-deps chromium",
`echo ${shellSingleQuote(`CI.json ${ci.name ?? "hwlab"} command count=${commands.length}`)}`
];
@@ -329,6 +446,7 @@ function ciJsonScript(ci) {
function publishScript() {
return `#!/bin/sh
set -eu
${dependencyProxyProbeScript("image-publish-pre-apk", "https://dl-cdn.alpinelinux.org/alpine/latest-stable/main/x86_64/APKINDEX.tar.gz")}
apk add --no-cache docker-cli git openssh-client python3
export DOCKER_HOST=unix:///var/run/docker.sock
export HWLAB_CI_ARTIFACT_RUN_ID="$(context.pipelineRun.name)"
@@ -339,7 +457,12 @@ if [ -n "$(params.base-image)" ]; then export HWLAB_DEV_BASE_IMAGE="$(params.bas
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
${dependencyProxyProbeScript("image-publish-pre-base-image", "https://registry-1.docker.io/v2/")}
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)"
@@ -351,6 +474,7 @@ node scripts/refresh-artifact-catalog.mjs --target-ref HEAD --publish-report /wo
function gitopsPromoteScript() {
return `#!/bin/sh
set -eu
${dependencyProxyProbeScript("gitops-promote-pre-apt", "http://deb.debian.org/debian/dists/bookworm/InRelease")}
if ! command -v git >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
apt-get update
@@ -412,6 +536,7 @@ echo '{"status":"pushed","gitopsBranch":"'"$(params.gitops-branch)"'","sourceRev
function pollerScript() {
return `#!/bin/sh
set -eu
${dependencyProxyProbeScript("poller-pre-apk", "https://dl-cdn.alpinelinux.org/alpine/latest-stable/main/x86_64/APKINDEX.tar.gz")}
apk add --no-cache git openssh-client ca-certificates
mkdir -p /root/.ssh
cp /workspace/git-ssh/ssh-privatekey /root/.ssh/id_rsa