fix(e2e): clean functional Docker build inputs

This commit is contained in:
Vincent Koc
2026-05-26 09:29:57 +02:00
parent 2e17003165
commit abc7b7b331
3 changed files with 182 additions and 6 deletions

View File

@@ -67,20 +67,39 @@ docker_e2e_build_or_reuse() {
echo "Building Docker image: $image_name"
local build_args=()
local package_tgz=""
local package_context=""
local package_pack_dir=""
if [ -n "$target" ]; then
build_args+=(--target "$target")
fi
if [ "$target" = "functional" ]; then
local package_tgz
local package_context
package_tgz="$(docker_e2e_prepare_package_tgz "$label")"
package_context="$(docker_e2e_prepare_package_context "$package_tgz")"
if [ -z "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}" ]; then
package_pack_dir="$(dirname "$package_tgz")"
fi
local context_status=0
package_context="$(docker_e2e_prepare_package_context "$package_tgz")" || context_status="$?"
if [ "$context_status" -ne 0 ]; then
if [ -n "$package_pack_dir" ]; then
rm -rf "$package_pack_dir"
fi
return "$context_status"
fi
# The Dockerfile never sees repo sources as app input; functional installs
# exactly this tarball through a named BuildKit context.
build_args+=(--build-context "openclaw_package=$package_context")
fi
build_args+=(-t "$image_name" -f "$dockerfile" "$context")
docker_build_run "$label-build" "${build_args[@]}"
local build_status=0
docker_build_run "$label-build" "${build_args[@]}" || build_status="$?"
if [ -n "$package_context" ]; then
rm -rf "$package_context"
fi
if [ -n "$package_pack_dir" ]; then
rm -rf "$package_pack_dir"
fi
return "$build_status"
}
docker_e2e_test_state_shell_b64() {

View File

@@ -31,13 +31,19 @@ docker_e2e_prepare_package_tgz() {
local pack_dir
pack_dir="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-docker-e2e-pack.XXXXXX")"
local pack_status=0
package_tgz="$(
node "$ROOT_DIR/scripts/package-openclaw-for-docker.mjs" \
--output-dir "$pack_dir" \
--output-name openclaw-current.tgz
)"
)" || pack_status="$?"
if [ "$pack_status" -ne 0 ]; then
rm -rf "$pack_dir"
return "$pack_status"
fi
if [ -z "$package_tgz" ]; then
echo "missing packed OpenClaw tarball" >&2
rm -rf "$pack_dir"
return 1
fi
docker_e2e_abs_path "$package_tgz"
@@ -49,7 +55,12 @@ docker_e2e_prepare_package_context() {
context_dir="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-docker-e2e-package-context.XXXXXX")"
# BuildKit named contexts must be directories, so expose the tarball as a
# stable filename inside a tiny temporary context.
cp "$package_tgz" "$context_dir/openclaw-current.tgz"
local copy_status=0
cp "$package_tgz" "$context_dir/openclaw-current.tgz" || copy_status="$?"
if [ "$copy_status" -ne 0 ]; then
rm -rf "$context_dir"
return "$copy_status"
fi
printf '%s\n' "$context_dir"
}

View File

@@ -62,6 +62,10 @@ const CENTRALIZED_BUILD_SCRIPTS = [
"scripts/test-live-build-docker.sh",
] as const;
function shellQuote(value: string): string {
return `'${value.replace(/'/gu, `'\\''`)}'`;
}
describe("docker build helper", () => {
it("forces BuildKit for centralized Docker builds", () => {
const helper = readFileSync(HELPER_PATH, "utf8");
@@ -109,6 +113,148 @@ describe("docker build helper", () => {
);
});
it("removes functional Docker build package inputs after the build", () => {
const workDir = mkdtempSync(join(tmpdir(), "openclaw-docker-build-cleanup-"));
try {
const rootDir = process.cwd();
const script = `
set -euo pipefail
ROOT_DIR=${shellQuote(rootDir)}
TMPDIR=${shellQuote(workDir)}
export ROOT_DIR TMPDIR
node() {
local script="$1"
shift
if [[ "$script" != "$ROOT_DIR/scripts/package-openclaw-for-docker.mjs" ]]; then
command node "$script" "$@"
return
fi
local output_dir=""
local output_name=""
while [[ $# -gt 0 ]]; do
case "$1" in
--output-dir)
output_dir="$2"
shift 2
;;
--output-name)
output_name="$2"
shift 2
;;
*)
shift
;;
esac
done
mkdir -p "$output_dir"
printf fixture >"$output_dir/$output_name"
printf "%s\\n" "$output_dir/$output_name"
}
export -f node
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
docker_build_run() {
local build_context=""
local arg
for arg in "$@"; do
case "$arg" in
openclaw_package=*)
build_context="\${arg#openclaw_package=}"
;;
esac
done
test -n "$build_context"
test -f "$build_context/openclaw-current.tgz"
printf "%s\\n" "$build_context" >"$TMPDIR/build-context-seen"
}
docker_e2e_build_or_reuse \\
openclaw-test-image \\
cleanup-proof \\
"$ROOT_DIR/scripts/e2e/Dockerfile" \\
"$ROOT_DIR" \\
functional
test -f "$TMPDIR/build-context-seen"
leftovers="$(find "$TMPDIR" -maxdepth 1 \\( \\
-name 'openclaw-docker-e2e-pack.*' \\
-o -name 'openclaw-docker-e2e-package-context.*' \\
\\) -print)"
if [[ -n "$leftovers" ]]; then
printf 'leftover functional build inputs:\\n%s\\n' "$leftovers" >&2
exit 1
fi
`;
execFileSync("bash", ["-lc", script], { encoding: "utf8" });
} finally {
rmSync(workDir, { recursive: true, force: true });
}
});
it("keeps caller-provided functional Docker build packages", () => {
const workDir = mkdtempSync(join(tmpdir(), "openclaw-docker-build-external-package-"));
try {
const rootDir = process.cwd();
const script = `
set -euo pipefail
ROOT_DIR=${shellQuote(rootDir)}
TMPDIR=${shellQuote(workDir)}
export ROOT_DIR TMPDIR
external_dir="$TMPDIR/external-package"
mkdir -p "$external_dir"
printf fixture >"$external_dir/openclaw-current.tgz"
OPENCLAW_CURRENT_PACKAGE_TGZ="$external_dir/openclaw-current.tgz"
export OPENCLAW_CURRENT_PACKAGE_TGZ
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
docker_build_run() {
local build_context=""
local arg
for arg in "$@"; do
case "$arg" in
openclaw_package=*)
build_context="\${arg#openclaw_package=}"
;;
esac
done
test -n "$build_context"
test -f "$build_context/openclaw-current.tgz"
printf "%s\\n" "$build_context" >"$TMPDIR/build-context-seen"
}
docker_e2e_build_or_reuse \\
openclaw-test-image \\
external-package-proof \\
"$ROOT_DIR/scripts/e2e/Dockerfile" \\
"$ROOT_DIR" \\
functional
test -f "$TMPDIR/build-context-seen"
test -f "$OPENCLAW_CURRENT_PACKAGE_TGZ"
leftovers="$(find "$TMPDIR" -maxdepth 1 -name 'openclaw-docker-e2e-package-context.*' -print)"
if [[ -n "$leftovers" ]]; then
printf 'leftover functional build context:\\n%s\\n' "$leftovers" >&2
exit 1
fi
`;
execFileSync("bash", ["-lc", script], { encoding: "utf8" });
} finally {
rmSync(workDir, { recursive: true, force: true });
}
});
it("includes procps in the shared Docker E2E image for process watchdogs", () => {
const dockerfile = readFileSync("scripts/e2e/Dockerfile", "utf8");