diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..f65602ee --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +dist +tmp +artifacts +*.log +.env +*.tgz +.git diff --git a/.github/workflows/ci-e2e-docker.yml b/.github/workflows/ci-e2e-docker.yml new file mode 100644 index 00000000..7ce2dd82 --- /dev/null +++ b/.github/workflows/ci-e2e-docker.yml @@ -0,0 +1,35 @@ +name: E2E (real codex, Docker) + +on: + workflow_dispatch: + inputs: + forge: + description: "run forge phase (needs BILI_CODEX_COMPACT support in master)" + type: boolean + default: false + +jobs: + e2e-docker: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + - name: Preflight (in container) + run: bash scripts/e2e-docker/run.sh preflight + env: + E2E_UPSTREAM_URL: ${{ secrets.E2E_UPSTREAM_URL }} + E2E_UPSTREAM_KEY: ${{ secrets.E2E_UPSTREAM_KEY }} + - name: E2E suite (in container) + run: bash scripts/e2e-docker/run.sh full + env: + E2E_UPSTREAM_URL: ${{ secrets.E2E_UPSTREAM_URL }} + E2E_UPSTREAM_KEY: ${{ secrets.E2E_UPSTREAM_KEY }} + E2E_FORGE: ${{ inputs.forge && '1' || '' }} + - name: Upload logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-docker-logs + path: tmp/docker-e2e/ + retention-days: 7 + if-no-files-found: ignore diff --git a/package.json b/package.json index 1ba73fb7..11b390ef 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "typecheck": "tsc --noEmit --project tsconfig.build.json", "test": "node --import tsx --test tests/*.test.ts", "start": "node dist/index.js", - "test:e2e": "node --import tsx --test tests/e2e/e2e-codex.test.ts" + "test:e2e": "node --import tsx --test tests/e2e/e2e-codex.test.ts", + "e2e:docker": "bash scripts/e2e-docker/run.sh full", + "e2e:docker:preflight": "bash scripts/e2e-docker/run.sh preflight" }, "keywords": [ "context", diff --git a/scripts/e2e-docker/Dockerfile b/scripts/e2e-docker/Dockerfile new file mode 100644 index 00000000..e51f0d2b --- /dev/null +++ b/scripts/e2e-docker/Dockerfile @@ -0,0 +1,17 @@ +FROM node:22-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci --ignore-scripts --no-fund --no-audit + +COPY . . +RUN npm run build + +RUN npm install -g @openai/codex@latest + +CMD ["bash", "-lc", "E2E_CHECK=1 node --import tsx --test tests/e2e/e2e-codex.test.ts"] diff --git a/scripts/e2e-docker/run.sh b/scripts/e2e-docker/run.sh new file mode 100755 index 00000000..a9522fd3 --- /dev/null +++ b/scripts/e2e-docker/run.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -eo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$REPO_ROOT" + +IMAGE="${BILI_E2E_IMAGE:-billion-context-e2e:local}" +NETWORK="${E2E_DOCKER_NETWORK:-bridge}" +LOG_DIR="${BILI_E2E_LOG_DIR:-$REPO_ROOT/tmp/docker-e2e}" +MODE="${1:-full}" + +case "$MODE" in + full | preflight) ;; + *) echo "usage: $0 [full|preflight]" >&2; exit 64 ;; +esac + +if ! command -v docker >/dev/null 2>&1; then + echo "error: docker not found on PATH" >&2 + exit 3 +fi + +echo ">> building image $IMAGE" +docker build -f scripts/e2e-docker/Dockerfile -t "$IMAGE" . + +FORWARD=() +for v in E2E_UPSTREAM_URL E2E_UPSTREAM_KEY E2E_MODEL E2E_FORGE E2E_TMO E2E_CODEX_BIN; do + if [ -n "${!v:-}" ]; then FORWARD+=(-e "$v=${!v}"); fi +done + +mkdir -p "$LOG_DIR" + +# Default upstream is host loopback; in a bridge container that's the container +# itself. Use E2E_DOCKER_NETWORK=host, or set E2E_UPSTREAM_URL reachable from the container. +if [ "$MODE" = "preflight" ]; then + CMD="E2E_CHECK=1 node --import tsx --test tests/e2e/e2e-codex.test.ts" +else + CMD="ACP_TEST_E2E=1 node --import tsx --test tests/e2e/e2e-codex.test.ts" +fi + +echo ">> running e2e [$MODE] in container (network=$NETWORK, logs=$LOG_DIR)" +exec docker run --rm \ + --network "$NETWORK" \ + -v "$LOG_DIR:/app/tmp" \ + "${FORWARD[@]}" \ + "$IMAGE" bash -lc "$CMD"