From 3224005c82574396443542884c098f8f9de86693 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 28 Jul 2026 11:44:28 -0400 Subject: [PATCH] fix(ci): bound Telegram Mantis proof cleanup (#115253) * test(mantis): bound Telegram proof cleanup * test(mantis): enforce cleanup deadline in wrapper * test(mantis): supervise cleanup through bash --- .../mantis-telegram-desktop-proof.yml | 9 ++++++++ scripts/mantis/mantis-sut-container.sh | 22 +++++++++++++++++++ ...is-telegram-desktop-proof-workflow.test.ts | 20 ++++++++++++++++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/.github/workflows/mantis-telegram-desktop-proof.yml b/.github/workflows/mantis-telegram-desktop-proof.yml index c2b4d7a7dff8..b496dd062540 100644 --- a/.github/workflows/mantis-telegram-desktop-proof.yml +++ b/.github/workflows/mantis-telegram-desktop-proof.yml @@ -556,6 +556,15 @@ jobs: "$runtime_parent/attestations/$lane.json" >/dev/null done + - name: Return proof artifacts to the runner + if: ${{ always() }} + shell: bash + run: | + set -euo pipefail + if [[ -d "$MANTIS_OUTPUT_DIR" ]]; then + sudo chown -R "$(id -u):$(id -g)" "$MANTIS_OUTPUT_DIR" + fi + - name: Inspect Mantis evidence manifest id: inspect if: ${{ always() }} diff --git a/scripts/mantis/mantis-sut-container.sh b/scripts/mantis/mantis-sut-container.sh index 7bd19cc52f92..d5dd19a0bf25 100644 --- a/scripts/mantis/mantis-sut-container.sh +++ b/scripts/mantis/mantis-sut-container.sh @@ -8,6 +8,7 @@ readonly runtime_root_file="/etc/openclaw-mantis-sut-runtime-root" readonly docker_bin="/usr/bin/docker" readonly flock_bin="/usr/bin/flock" readonly iptables_bin="/usr/sbin/iptables" +readonly timeout_bin="/usr/bin/timeout" readonly network_lock_file="/run/lock/openclaw-mantis-sut-network.lock" readonly network_state_root="/run/openclaw-mantis-sut-networks" @@ -16,6 +17,19 @@ die() { exit 64 } +run_cleanup_with_deadline() { + local action="$1" + shift + # timeout owns a separate process group, so escalation reaches Docker and + # network-cleanup descendants instead of killing only the caller's sudo. + exec "$timeout_bin" --signal=TERM --kill-after=5s 30s /bin/bash "$0" "__${action}" "$@" +} + +require_cleanup_timeout_parent() { + [[ "$(readlink -f "/proc/$PPID/exe")" == "$timeout_bin" ]] \ + || die "internal cleanup action requires the timeout supervisor" +} + require_container_name() { [[ "$1" =~ ^openclaw-telegram-sut-[0-9a-f-]+$ ]] || die "invalid container name" } @@ -810,6 +824,10 @@ case "$command" in exit "$run_result" ;; stop) + run_cleanup_with_deadline stop "$@" + ;; + __stop) + require_cleanup_timeout_parent [[ $# -eq 2 ]] || die "stop expects a container name and runtime root" require_container_name "$1" runtime_source="$2" @@ -823,6 +841,10 @@ case "$command" in exit "$stop_result" ;; destroy) + run_cleanup_with_deadline destroy "$@" + ;; + __destroy) + require_cleanup_timeout_parent [[ $# -eq 2 ]] || die "destroy expects a container name and runtime root" require_container_name "$1" runtime_source="$2" diff --git a/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts b/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts index e6b73261fe7e..ea91547ec0bd 100644 --- a/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts +++ b/test/scripts/mantis-telegram-desktop-proof-workflow.test.ts @@ -148,6 +148,9 @@ describe("Mantis Telegram Desktop proof workflow", () => { const inspectIndex = steps.findIndex( (step) => step.name === "Inspect Mantis evidence manifest", ); + const returnArtifactsIndex = steps.findIndex( + (step) => step.name === "Return proof artifacts to the runner", + ); expect(codexStep.env?.OPENCLAW_QA_CREDENTIAL_OWNER_ID).toContain( "mantis-telegram-desktop-${{ github.run_id }}-${{ github.run_attempt }}", @@ -155,7 +158,8 @@ describe("Mantis Telegram Desktop proof workflow", () => { expect(workflowStep("Prepare Codex user").run).toContain("OPENCLAW_QA_CREDENTIAL_OWNER_ID"); expect(cleanupIndex).toBeGreaterThan(steps.findIndex((step) => step.name === codexStep.name)); expect(cleanupIndex).toBeGreaterThanOrEqual(0); - expect(inspectIndex).toBeGreaterThan(cleanupIndex); + expect(returnArtifactsIndex).toBeGreaterThan(cleanupIndex); + expect(inspectIndex).toBeGreaterThan(returnArtifactsIndex); const cleanupStep = workflowStep("Release leaked Telegram proof leases"); expect(cleanupStep.if).toBe("${{ always() }}"); @@ -185,6 +189,20 @@ describe("Mantis Telegram Desktop proof workflow", () => { expect(cleanupStep.run).not.toContain("*/telegram-user-crabbox/*/.session/lease.json"); expect(cleanupStep.run).toContain('sudo -u codex "$MANTIS_NODE_BIN"'); expect(cleanupStep.run).not.toContain("sudo -u codex node"); + + const returnArtifactsStep = workflowStep("Return proof artifacts to the runner"); + expect(returnArtifactsStep.if).toBe("${{ always() }}"); + expect(returnArtifactsStep.run).toContain( + 'sudo chown -R "$(id -u):$(id -g)" "$MANTIS_OUTPUT_DIR"', + ); + + const sutWrapper = readFileSync(SUT_CONTAINER_WRAPPER, "utf8"); + expect(sutWrapper).toContain( + 'exec "$timeout_bin" --signal=TERM --kill-after=5s 30s /bin/bash "$0" "__${action}" "$@"', + ); + expect(sutWrapper).toContain('"$(readlink -f "/proc/$PPID/exe")" == "$timeout_bin"'); + expect(sutWrapper).toContain("__stop)"); + expect(sutWrapper).toContain("__destroy)"); }); it("cleans partially started proof daemons when local SUT startup fails", () => {