fix: harden G14 CI dependency diagnostics
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user