diff --git a/scripts/lib/docker-build.sh b/scripts/lib/docker-build.sh index 3d6e77817bf3..31d63d2a08f2 100644 --- a/scripts/lib/docker-build.sh +++ b/scripts/lib/docker-build.sh @@ -51,7 +51,7 @@ docker_build_args_need_buildx() { docker_build_transient_failure() { local log_file="$1" grep -Eqi \ - 'frontend grpc server closed unexpectedly|failed to dial gRPC|no active session|buildkit.*connection.*closed|rpc error: code = Unavailable' \ + 'frontend grpc server closed unexpectedly|failed to dial gRPC|no active session|buildkit.*connection.*closed|rpc error: code = Unavailable|failed to fetch oauth token:.*(5[0-9][0-9]|Gateway Timeout)|unexpected status from .*: 5[0-9][0-9]|TLS handshake timeout|net/http: TLS handshake timeout|i/o timeout|connection reset by peer' \ "$log_file" } @@ -235,7 +235,7 @@ docker_build_with_retries() { return 1 fi - echo "Docker build failed with a transient BuildKit transport error; retrying ($attempt/$retries)..." >&2 + echo "Docker build failed with a transient Docker/registry error; retrying ($attempt/$retries)..." >&2 docker_e2e_print_log "$log_file" rm -f "$log_file" attempt=$((attempt + 1)) diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index fb83bae18a3e..c6e24899c206 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -162,6 +162,33 @@ describe("docker build helper", () => { expect(helper).toContain("frontend grpc server closed unexpectedly"); }); + it("treats Docker registry auth 5xx failures as transient build failures", () => { + const workDir = mkdtempSync(join(tmpdir(), "openclaw-docker-build-transient-")); + + try { + const logPath = join(workDir, "docker-build.log"); + writeFileSync( + logPath, + [ + "#3 ERROR: failed to authorize: failed to fetch oauth token: unexpected status from POST request to https://auth.docker.io/token: 504 Gateway Timeout: error code: 504", + "ERROR: failed to solve: failed to resolve source metadata for docker.io/docker/dockerfile:1.7", + ].join("\n"), + ); + const rootDir = process.cwd(); + const script = ` +set -euo pipefail +ROOT_DIR=${shellQuote(rootDir)} +LOG_PATH=${shellQuote(logPath)} +source "$ROOT_DIR/scripts/lib/docker-build.sh" +docker_build_transient_failure "$LOG_PATH" +`; + + execFileSync("bash", ["-lc", script], { encoding: "utf8" }); + } finally { + rmSync(workDir, { recursive: true, force: true }); + } + }); + it("keeps shell-script Docker builds behind the helper", () => { for (const path of CENTRALIZED_BUILD_SCRIPTS) { const script = readFileSync(path, "utf8");