Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ jobs:
# and show misleading red status. Same-repo PRs run normally.
if: |-
${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
env:
E2E_CONTAINER_OWNER: '${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.shard }}'
strategy:
# One failing shard must not cancel the others: a partial matrix hides the
# rest of the suite and reports an incomplete failure set.
Expand Down Expand Up @@ -256,9 +258,24 @@ jobs:
RUNNER_ENVIRONMENT: '${{ runner.environment }}'
QWEN_SANDBOX: "${{ matrix.sandbox == 'sandbox:docker' && 'docker' || 'false' }}"
BUILD_SANDBOX_FLAGS: '--label org.qwen-code.ci.sandbox=true'
SANDBOX_FLAGS: '--label org.qwen-code.ci.owner=${E2E_CONTAINER_OWNER}'
run: |-
set -euo pipefail

cleanup_e2e_job() {
if [ '${{ matrix.sandbox }}' = 'sandbox:docker' ]; then
container_ids="$(timeout 30 docker ps -aq --filter "label=org.qwen-code.ci.owner=${E2E_CONTAINER_OWNER}" 2>/dev/null)" || container_ids=''
if [ -n "$container_ids" ]; then
printf '%s\n' "$container_ids" | xargs -r timeout 60 docker rm -f > /dev/null 2>&1 || echo "::warning::failed to remove E2E containers for ${E2E_CONTAINER_OWNER}"
fi
fi
if [ -n "${QWEN_CI_TMPDIR:-}" ]; then
rm -rf "$QWEN_CI_TMPDIR" 2>/dev/null || true
fi
}
trap cleanup_e2e_job EXIT
trap 'exit 1' INT TERM

if [ '${{ matrix.sandbox }}' = 'sandbox:docker' ]; then
sandbox_image="$(node -p "require('./packages/cli/package.json').config.sandboxImageUri")-e2e-${GITHUB_SHA}"

Expand Down Expand Up @@ -337,7 +354,6 @@ jobs:
if [ -n "$QWEN_CI_TMPDIR" ]; then
TMPDIR="$QWEN_CI_TMPDIR"
export TMPDIR
trap 'rm -rf "$TMPDIR" 2>/dev/null || true' EXIT
fi
fi
# QWEN_E2E_RENDERER=ink pins the baseline leg of the renderer
Expand Down Expand Up @@ -380,6 +396,21 @@ jobs:
}
fi

- name: 'Remove job-owned E2E containers'
if: |-
${{ always() && matrix.sandbox == 'sandbox:docker' && runner.environment == 'self-hosted' }}
run: |-
set -euo pipefail
container_ids="$(timeout 30 docker ps -aq --filter "label=org.qwen-code.ci.owner=${E2E_CONTAINER_OWNER}")"
if [ -n "$container_ids" ]; then
printf '%s\n' "$container_ids" | xargs -r timeout 60 docker rm -f > /dev/null || true
fi
remaining="$(timeout 30 docker ps -aq --filter "label=org.qwen-code.ci.owner=${E2E_CONTAINER_OWNER}")"
if [ -n "$remaining" ]; then
echo "::error::E2E containers remain for ${E2E_CONTAINER_OWNER}: ${remaining//$'\n'/,}"
exit 1
fi

# Commit-qualified CI images are pruned under the exclusive daemon lock
# once they are older than a day. The cleanup is non-blocking so a shard
# finishing early cannot queue a writer ahead of another shard's reader.
Expand Down
27 changes: 26 additions & 1 deletion scripts/tests/e2e-workflow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,31 @@ describe('e2e workflow', () => {
expect(runStep.env.VERBOSE).toBe('true');
});

it('reaps only the sandbox containers owned by its matrix job', () => {
const owner =
'${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.shard }}';
const cleanupStep = steps.find(
(step) => step.name === 'Remove job-owned E2E containers',
);

expect(yml.jobs['e2e-test-linux'].env.E2E_CONTAINER_OWNER).toBe(owner);
expect(runStep.env.SANDBOX_FLAGS).toContain(
'org.qwen-code.ci.owner=${E2E_CONTAINER_OWNER}',
);
expect(runStep.run).toContain('trap cleanup_e2e_job EXIT');
expect(runStep.run).toContain("trap 'exit 1' INT TERM");
expect(runStep.run).toContain(
'--filter "label=org.qwen-code.ci.owner=${E2E_CONTAINER_OWNER}"',
);
expect(cleanupStep.if).toContain('always()');
expect(cleanupStep.run).toContain(
'--filter "label=org.qwen-code.ci.owner=${E2E_CONTAINER_OWNER}"',
);
expect(cleanupStep.run).toContain('docker rm -f > /dev/null || true');
expect(cleanupStep.run.match(/docker ps -aq/g)).toHaveLength(2);
expect(cleanupStep.run).toContain('E2E containers remain');
});

it('never waits on a lock another run holds through its tests', () => {
// Run 33637097713 lost two Docker shards to the #10605 protocol on one
// host: shard 1/3 held the per-commit coordinator lock and polled 30
Expand Down Expand Up @@ -357,6 +382,6 @@ describe('e2e workflow', () => {
(step) => step.name === 'Run E2E tests',
);
expect(runStep.run).toContain('mktemp -d /var/tmp/qwen-ci-XXXXXX');
expect(runStep.run).toContain('trap \'rm -rf "$TMPDIR"');
expect(runStep.run).toContain('rm -rf "$QWEN_CI_TMPDIR"');
});
});
Loading