name: Real E2E Tests permissions: contents: read pull-requests: read on: workflow_dispatch: inputs: run_code_interpreter_e2e: description: 'Run code-interpreter E2E tests' required: false type: boolean default: false pull_request: branches: [ main ] push: branches: [ main ] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: changes: uses: ./.github/workflows/detect-changes.yml with: area: real-e2e python-e2e: name: Python E2E (docker bridge) needs: changes if: needs.changes.outputs.relevant == 'true' runs-on: self-hosted env: UV_BIN: /home/admin/.local/bin RUN_CODE_INTERPRETER_E2E: ${{ github.event_name == 'workflow_dispatch' && inputs.run_code_interpreter_e2e == true && 'true' || 'false' }} steps: - name: Checkout code uses: actions/checkout@v6 - name: Clean Docker runner cache run: bash scripts/ci-docker-cleanup.sh - name: Set up uv PATH and verify run: | echo "${UV_BIN}" >> "$GITHUB_PATH" export PATH="${UV_BIN}:${PATH}" uv --version uv run python --version - name: Clean up previous E2E resources run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-redis || true # Remove root-owned files from previous sandbox runs by mounting parent dir docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true docker image prune -f || true - name: Build local egress image run: docker build -t opensandbox/egress:local -f components/egress/Dockerfile . - name: Start Redis for Python pool E2E run: | docker rm -f opensandbox-e2e-redis || true docker run -d --name opensandbox-e2e-redis -p 127.0.0.1::6379 redis:7-alpine REDIS_PORT="$(docker inspect -f '{{(index (index .NetworkSettings.Ports "6379/tcp") 0).HostPort}}' opensandbox-e2e-redis)" echo "OPENSANDBOX_TEST_REDIS_URL=redis://127.0.0.1:${REDIS_PORT}/0" >> "$GITHUB_ENV" for i in {1..30}; do if docker exec opensandbox-e2e-redis redis-cli ping | grep -q PONG; then exit 0 fi sleep 1 done docker logs opensandbox-e2e-redis exit 1 - name: Run tests run: | set -e # Create config file cat < ~/.sandbox.toml [server] host = "127.0.0.1" port = 8080 api_key = "" [log] level = "INFO" [runtime] type = "docker" execd_image = "opensandbox/execd:local" [egress] image = "opensandbox/egress:local" mode = "dns+nft" [docker] network_mode = "bridge" [storage] allowed_host_paths = ["/tmp/opensandbox-e2e"] [renew_intent] enabled = true min_interval_seconds = 60 EOF ./scripts/python-e2e.sh - name: Eval server logs if: ${{ always() }} run: cat server/server.log - name: Upload execd logs if: always() uses: actions/upload-artifact@v7 with: name: execd-log-for-python-e2e path: /tmp/opensandbox-e2e/logs/ retention-days: 5 - name: Clean up after E2E if: always() run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-redis || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true java-e2e: name: Java E2E (docker bridge) needs: changes if: needs.changes.outputs.relevant == 'true' runs-on: self-hosted env: UV_BIN: /home/admin/.local/bin RUN_CODE_INTERPRETER_E2E: ${{ github.event_name == 'workflow_dispatch' && inputs.run_code_interpreter_e2e == true && 'true' || 'false' }} steps: - name: Checkout code uses: actions/checkout@v6 - name: Clean Docker runner cache run: bash scripts/ci-docker-cleanup.sh - name: Set up uv PATH and verify run: | echo "${UV_BIN}" >> "$GITHUB_PATH" export PATH="${UV_BIN}:${PATH}" uv --version uv run python --version - name: Set up JDK 8 uses: actions/setup-java@v5 with: distribution: temurin java-version: "8" - name: Set up JDK 17 uses: actions/setup-java@v5 with: distribution: temurin java-version: "17" - name: Clean up previous E2E resources run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-redis || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true docker image prune -f || true - name: Build local egress image run: docker build -t opensandbox/egress:local -f components/egress/Dockerfile . - name: Start Redis for Java pool E2E run: | docker rm -f opensandbox-e2e-redis || true docker run -d --name opensandbox-e2e-redis -p 127.0.0.1::6379 redis:7-alpine REDIS_PORT="$(docker inspect -f '{{(index (index .NetworkSettings.Ports "6379/tcp") 0).HostPort}}' opensandbox-e2e-redis)" echo "OPENSANDBOX_TEST_REDIS_URL=redis://127.0.0.1:${REDIS_PORT}/0" >> "$GITHUB_ENV" for i in {1..30}; do if docker exec opensandbox-e2e-redis redis-cli ping | grep -q PONG; then exit 0 fi sleep 1 done docker logs opensandbox-e2e-redis exit 1 - name: Run tests env: GRADLE_USER_HOME: ${{ github.workspace }}/.gradle-user-home OPENSANDBOX_TEST_SECURE_ACCESS_VERIFIABLE: "false" run: | set -e export GRADLE_OPTS="-Dorg.gradle.java.installations.auto-detect=true -Dorg.gradle.java.installations.auto-download=false -Dorg.gradle.java.installations.paths=${JAVA_HOME_8_X64},${JAVA_HOME_17_X64}" # Create config file cat < ~/.sandbox.toml [server] host = "127.0.0.1" port = 8080 api_key = "" [log] level = "INFO" [runtime] type = "docker" execd_image = "opensandbox/execd:local" [egress] image = "opensandbox/egress:local" mode = "dns+nft" [docker] network_mode = "bridge" [storage] allowed_host_paths = ["/tmp/opensandbox-e2e"] EOF bash ./scripts/java-e2e.sh - name: Eval server logs if: ${{ always() }} run: cat server/server.log - name: Upload Test Report if: always() uses: actions/upload-artifact@v7 with: name: java-test-report path: tests/java/build/reports/tests/test/ retention-days: 5 - name: Upload execd logs if: always() uses: actions/upload-artifact@v7 with: name: execd-log-for-java-e2e path: /tmp/opensandbox-e2e/logs/ retention-days: 5 - name: Clean up after E2E if: always() run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-redis || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true javascript-e2e: name: JavaScript E2E (docker bridge) needs: changes if: needs.changes.outputs.relevant == 'true' runs-on: self-hosted env: UV_BIN: /home/admin/.local/bin NODE_VERSION: "20.19.0" RUN_CODE_INTERPRETER_E2E: ${{ github.event_name == 'workflow_dispatch' && inputs.run_code_interpreter_e2e == true && 'true' || 'false' }} steps: - name: Checkout code uses: actions/checkout@v6 - name: Clean Docker runner cache run: bash scripts/ci-docker-cleanup.sh - name: Set up uv PATH and verify run: | echo "${UV_BIN}" >> "$GITHUB_PATH" export PATH="${UV_BIN}:${PATH}" uv --version uv run python --version - name: Set up Node.js run: | NODE_DIR="/home/admin/.local/node-v${NODE_VERSION}-linux-x64" if [ -x "${NODE_DIR}/bin/node" ]; then echo "Node.js ${NODE_VERSION} already cached" else echo "Downloading Node.js ${NODE_VERSION}..." mkdir -p /home/admin/.local curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" \ | tar -xJ -C /home/admin/.local/ fi echo "${NODE_DIR}/bin" >> "$GITHUB_PATH" export PATH="${NODE_DIR}/bin:${PATH}" node --version npm --version - name: Clean up previous E2E resources run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true docker image prune -f || true - name: Build local egress image run: docker build -t opensandbox/egress:local -f components/egress/Dockerfile . - name: Run tests run: | set -e # Create config file (match other E2E jobs) cat < ~/.sandbox.toml [server] host = "127.0.0.1" port = 8080 api_key = "" [log] level = "INFO" [runtime] type = "docker" execd_image = "opensandbox/execd:local" [egress] image = "opensandbox/egress:local" mode = "dns+nft" [docker] network_mode = "bridge" [storage] allowed_host_paths = ["/tmp/opensandbox-e2e"] EOF bash ./scripts/javascript-e2e.sh - name: Eval server logs if: ${{ always() }} run: cat server/server.log - name: Upload Test Report if: always() uses: actions/upload-artifact@v7 with: name: javascript-test-report path: tests/javascript/build/test-results/junit.xml retention-days: 5 - name: Upload execd logs if: always() uses: actions/upload-artifact@v7 with: name: execd-log-for-js-e2e path: /tmp/opensandbox-e2e/logs/ retention-days: 5 - name: Clean up after E2E if: always() run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true csharp-e2e: name: C# E2E (docker bridge) needs: changes if: needs.changes.outputs.relevant == 'true' runs-on: self-hosted env: UV_BIN: /home/admin/.local/bin RUN_CODE_INTERPRETER_E2E: ${{ github.event_name == 'workflow_dispatch' && inputs.run_code_interpreter_e2e == true && 'true' || 'false' }} steps: - name: Checkout code uses: actions/checkout@v6 - name: Clean Docker runner cache run: bash scripts/ci-docker-cleanup.sh - name: Set up uv PATH and verify run: | echo "${UV_BIN}" >> "$GITHUB_PATH" export PATH="${UV_BIN}:${PATH}" uv --version uv run python --version - name: Set up .NET SDK uses: actions/setup-dotnet@v5 env: DOTNET_INSTALL_DIR: /home/admin/.local/dotnet with: dotnet-version: "10.0.x" - name: Clean up previous E2E resources run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true docker image prune -f || true - name: Build local egress image run: docker build -t opensandbox/egress:local -f components/egress/Dockerfile . - name: Run tests run: | set -e cat < ~/.sandbox.toml [server] host = "127.0.0.1" port = 8080 api_key = "" [log] level = "INFO" [runtime] type = "docker" execd_image = "opensandbox/execd:local" [egress] image = "opensandbox/egress:local" mode = "dns+nft" [docker] network_mode = "bridge" [storage] allowed_host_paths = ["/tmp/opensandbox-e2e"] EOF bash ./scripts/csharp-e2e.sh - name: Eval server logs if: ${{ always() }} run: cat server/server.log - name: Upload Test Report if: always() uses: actions/upload-artifact@v7 with: name: csharp-test-report path: tests/csharp/build/test-results/ retention-days: 5 - name: Upload execd logs if: always() uses: actions/upload-artifact@v7 with: name: execd-log-for-csharp-e2e path: /tmp/opensandbox-e2e/logs/ retention-days: 5 - name: Clean up after E2E if: always() run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true go-e2e: name: Go E2E (docker bridge) needs: changes if: needs.changes.outputs.relevant == 'true' runs-on: self-hosted env: UV_BIN: /home/admin/.local/bin HOME: /home/admin/actions-runner GOCACHE: /home/admin/actions-runner/_temp/go-build GOMODCACHE: /home/admin/actions-runner/_temp/go-mod-cache RUN_CODE_INTERPRETER_E2E: ${{ github.event_name == 'workflow_dispatch' && inputs.run_code_interpreter_e2e == true && 'true' || 'false' }} steps: - name: Checkout code uses: actions/checkout@v6 - name: Clean Docker runner cache run: bash scripts/ci-docker-cleanup.sh - name: Set up uv PATH and verify run: | echo "${UV_BIN}" >> "$GITHUB_PATH" export PATH="${UV_BIN}:${PATH}" uv --version uv run python --version - name: Set up Go uses: actions/setup-go@v6 with: go-version: "1.24" cache: false - name: Clean up previous E2E resources run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true docker image prune -f || true - name: Build local egress image run: docker build -t opensandbox/egress:local -f components/egress/Dockerfile . - name: Run tests run: | set -e cat < ~/.sandbox.toml [server] host = "127.0.0.1" port = 8080 api_key = "" [log] level = "INFO" [runtime] type = "docker" execd_image = "opensandbox/execd:local" [egress] image = "opensandbox/egress:local" mode = "dns+nft" [docker] network_mode = "bridge" [storage] allowed_host_paths = ["/tmp/opensandbox-e2e"] EOF bash ./scripts/go-e2e.sh - name: Eval server logs if: ${{ always() }} run: cat server/server.log - name: Upload Test Report if: always() uses: actions/upload-artifact@v7 with: name: go-test-report path: tests/go/reports/ retention-days: 5 - name: Upload execd logs if: always() uses: actions/upload-artifact@v7 with: name: execd-log-for-go-e2e path: /tmp/opensandbox-e2e/logs/ retention-days: 5 - name: Clean up after E2E if: always() run: | docker ps -aq --filter "label=opensandbox" | xargs -r docker rm -f || true docker rm -f opensandbox-e2e-credential-vault-target || true docker ps -aq --filter "label=opensandbox.e2e=credential-vault" | xargs -r docker rm -f || true docker run --rm -v /tmp:/host_tmp alpine rm -rf /host_tmp/opensandbox-e2e || true required: name: Real E2E CI if: ${{ always() && github.event_name == 'pull_request' }} needs: [changes, python-e2e, java-e2e, javascript-e2e, csharp-e2e, go-e2e] runs-on: ubuntu-latest steps: - name: Verify required jobs env: RELEVANT: ${{ needs.changes.outputs.relevant }} CHANGES_RESULT: ${{ needs.changes.result }} PYTHON_RESULT: ${{ needs.python-e2e.result }} JAVA_RESULT: ${{ needs.java-e2e.result }} JAVASCRIPT_RESULT: ${{ needs.javascript-e2e.result }} CSHARP_RESULT: ${{ needs.csharp-e2e.result }} GO_RESULT: ${{ needs.go-e2e.result }} run: | if [[ "$CHANGES_RESULT" != "success" ]]; then echo "Change detection failed: $CHANGES_RESULT" exit 1 fi if [[ "$RELEVANT" == "true" ]]; then [[ "$PYTHON_RESULT" == "success" && "$JAVA_RESULT" == "success" && "$JAVASCRIPT_RESULT" == "success" && "$CSHARP_RESULT" == "success" && "$GO_RESULT" == "success" ]] else [[ "$RELEVANT" == "false" && "$PYTHON_RESULT" == "skipped" && "$JAVA_RESULT" == "skipped" && "$JAVASCRIPT_RESULT" == "skipped" && "$CSHARP_RESULT" == "skipped" && "$GO_RESULT" == "skipped" ]] fi