From f285a0c4c4576b41b57d7ad8ec6f2f8f85c39f7e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 16 Jun 2026 17:23:43 +0200 Subject: [PATCH] fix(ci): fail unusable Windows testbox phone-home --- .../workflows/windows-blacksmith-testbox.yml | 31 ++++++++++++++----- test/scripts/ci-workflow-guards.test.ts | 29 +++++++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/.github/workflows/windows-blacksmith-testbox.yml b/.github/workflows/windows-blacksmith-testbox.yml index 8127f8d2c888..ad49f9387606 100644 --- a/.github/workflows/windows-blacksmith-testbox.yml +++ b/.github/workflows/windows-blacksmith-testbox.yml @@ -65,7 +65,9 @@ jobs: fi runner_ssh_port="${BLACKSMITH_SSH_PORT:-22}" - response="$(curl -s -f -L --post302 --post303 -X POST "${api_url}/api/testbox/phone-home" \ + hydrating_response="$RUNNER_TEMP/testbox-hydrating.response" + hydrating_http_code="$(curl -sS -L --post302 --post303 -o "$hydrating_response" -w '%{http_code}' \ + -X POST "${api_url}/api/testbox/phone-home" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${auth_token}" \ -d "{ @@ -77,7 +79,15 @@ jobs: \"working_directory\": \"${GITHUB_WORKSPACE}\", \"adopted_run_id\": \"${GITHUB_RUN_ID}\", \"metadata\": {} - }" 2>/dev/null || true)" + }" || true)" + + echo "phone_home_hydrating_http=${hydrating_http_code}" + if [[ ! "$hydrating_http_code" =~ ^2 ]]; then + echo "Blacksmith phone-home hydrating failed; response body:" >&2 + cat "$hydrating_response" >&2 || true + exit 1 + fi + response="$(cat "$hydrating_response")" echo "$TESTBOX_ID" > "$state/testbox_id" echo "$installation_model_id" > "$state/installation_model_id" @@ -100,12 +110,14 @@ jobs: fi ssh_public_key="$(cat "$state/ssh_public_key" 2>/dev/null || true)" - if [ -n "$ssh_public_key" ]; then - mkdir -p ~/.ssh - printf '%s\n' "$ssh_public_key" >> ~/.ssh/authorized_keys - chmod 700 ~/.ssh - chmod 600 ~/.ssh/authorized_keys + if [ -z "$ssh_public_key" ]; then + echo "Blacksmith phone-home did not return an SSH public key; testbox cannot accept CLI connections." >&2 + exit 1 fi + mkdir -p ~/.ssh + printf '%s\n' "$ssh_public_key" >> ~/.ssh/authorized_keys + chmod 700 ~/.ssh + chmod 600 ~/.ssh/authorized_keys - name: Checkout uses: actions/checkout@v6 @@ -161,6 +173,11 @@ jobs: -H "Authorization: Bearer ${auth_token}" \ --data-binary @"$ready_body" || true)" echo "phone_home_ready_http=${http_code}" + if [[ ! "$http_code" =~ ^2 ]]; then + echo "Blacksmith phone-home ready failed; response body:" >&2 + cat "$RUNNER_TEMP/testbox-ready.response" >&2 || true + exit 1 + fi echo "============================================" echo "Testbox ready!" diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index b61541b8a66f..21a00a713880 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -197,6 +197,35 @@ describe("ci workflow guards", () => { ); }); + it("fails Windows Testbox setup when Blacksmith phone-home is not accepted", () => { + const workflow = readFileSync(".github/workflows/windows-blacksmith-testbox.yml", "utf8"); + + expect(workflow).toContain('echo "phone_home_hydrating_http=${hydrating_http_code}"'); + expect(workflow).toContain('echo "phone_home_ready_http=${http_code}"'); + const hydratingFailureBlock = workflow.slice( + workflow.indexOf('if [[ ! "$hydrating_http_code" =~ ^2 ]]; then'), + workflow.indexOf('response="$(cat "$hydrating_response")"'), + ); + const missingSshKeyFailureBlock = workflow.slice( + workflow.indexOf('if [ -z "$ssh_public_key" ]; then'), + workflow.indexOf("mkdir -p ~/.ssh"), + ); + const readyFailureBlock = workflow.slice( + workflow.indexOf('if [[ ! "$http_code" =~ ^2 ]]; then'), + workflow.indexOf('echo "============================================"'), + ); + + expect(hydratingFailureBlock).toContain("exit 1"); + expect(missingSshKeyFailureBlock).toContain("exit 1"); + expect(readyFailureBlock).toContain("exit 1"); + expect(workflow).toContain( + "Blacksmith phone-home did not return an SSH public key; testbox cannot accept CLI connections.", + ); + expect(workflow).not.toContain( + 'phone_home_ready_http=${http_code}"\n\n echo "============================================"', + ); + }); + it("runs dependency policy guards in PR CI preflight", () => { const workflow = readFileSync(".github/workflows/ci.yml", "utf8"); const preflightGuards = workflow.slice(