From 02b8fd8a839fefefcae974c4a66bc2e357d12ee5 Mon Sep 17 00:00:00 2001 From: guojing Date: Mon, 20 Apr 2026 18:27:06 +0800 Subject: [PATCH 1/2] feat(ts-sdk): add integration test CI --- .github/workflows/ts-sdk-ci.yml | 99 +++++++++++++++++++ rock-conf/rock-ci.yml | 14 +++ .../rocklet/local_files/docker_run_with_uv.sh | 5 +- .../tests/integration/file-system.test.ts | 12 +-- .../integration/sandbox-lifecycle.test.ts | 8 +- 5 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 rock-conf/rock-ci.yml diff --git a/.github/workflows/ts-sdk-ci.yml b/.github/workflows/ts-sdk-ci.yml index abae30af48..31e6934628 100644 --- a/.github/workflows/ts-sdk-ci.yml +++ b/.github/workflows/ts-sdk-ci.yml @@ -40,4 +40,103 @@ jobs: echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" pnpm test:unit + integration-test: + runs-on: self-hosted + timeout-minutes: 20 + defaults: + run: + working-directory: rock/ts-sdk + steps: + - uses: actions/checkout@v4 + + - name: Cleanup old Ray sessions + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + ray stop --force 2>/dev/null || true + docker rm -f $(docker ps -aq) 2>/dev/null || true + working-directory: . + + - name: Install Python dependencies with uv + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + uv sync --all-extras --python 3.11 + working-directory: . + + - name: Pull test Docker image + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + docker image inspect python:3.11 > /dev/null 2>&1 || docker pull python:3.11 + working-directory: . + + - name: Start ROCK Admin server + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + export ROCK_WORKER_ENV_TYPE=uv + nohup uv run admin --env ci --role admin --port 8080 > /tmp/rock-admin.log 2>&1 & + echo $! > /tmp/rock-admin.pid + echo "Waiting for Admin server to be ready..." + for i in $(seq 1 60); do + if grep -q "Uvicorn running on" /tmp/rock-admin.log 2>/dev/null; then + echo "Admin server is ready!" + break + fi + if [ $i -eq 60 ]; then + echo "Admin server failed to start within 60 seconds" + cat /tmp/rock-admin.log + exit 1 + fi + sleep 1 + done + working-directory: . + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install pnpm + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + npm install -g pnpm + + - name: Install dependencies + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + pnpm install --frozen-lockfile + + - name: Run integration tests + env: + ROCK_BASE_URL: http://127.0.0.1:8080 + ROCK_TEST_IMAGE: python:3.11 + ROCK_TEST_CLUSTER: default + ROCK_STARTUP_TIMEOUT: "600" + ROCK_TEST_TIMEOUT: "660000" + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + pnpm test:integration + + - name: Print debug logs + if: always() + run: | + echo "=== Admin server logs ===" + cat /tmp/rock-admin.log 2>/dev/null || echo "No admin log found" + echo "" + echo "=== Docker containers ===" + docker ps -a 2>/dev/null || true + echo "" + echo "=== Ray status ===" + ray status 2>/dev/null || true + working-directory: . + + - name: Cleanup + if: always() + run: | + echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" + if [ -f /tmp/rock-admin.pid ]; then + kill $(cat /tmp/rock-admin.pid) 2>/dev/null || true + fi + ray stop --force 2>/dev/null || true + docker rm -f $(docker ps -aq) 2>/dev/null || true + working-directory: . + diff --git a/rock-conf/rock-ci.yml b/rock-conf/rock-ci.yml new file mode 100644 index 0000000000..7db30d7db9 --- /dev/null +++ b/rock-conf/rock-ci.yml @@ -0,0 +1,14 @@ +ray: + runtime_env: + working_dir: ./ + pip: ./requirements_sandbox_actor.txt + namespace: "rock-sandbox-ci" + +runtime: + standard_spec: + memory: "2g" + cpus: 1 + +warmup: + images: + - "python:3.11" diff --git a/rock/rocklet/local_files/docker_run_with_uv.sh b/rock/rocklet/local_files/docker_run_with_uv.sh index 150cf14e2b..798f595959 100755 --- a/rock/rocklet/local_files/docker_run_with_uv.sh +++ b/rock/rocklet/local_files/docker_run_with_uv.sh @@ -32,7 +32,10 @@ if [ ! -f /etc/alpine-release ]; then UV_CMD=$HOME/.local/bin/uv fi - cd $PROJECT_ROOT + # Copy project to a writable directory (volume may be mounted read-only) + WRITABLE_PROJECT=/tmp/rocklet-project + cp -r $PROJECT_ROOT $WRITABLE_PROJECT + cd $WRITABLE_PROJECT # Create virtual environment $UV_CMD venv --python 3.11 /tmp/rocklet-venv diff --git a/rock/ts-sdk/tests/integration/file-system.test.ts b/rock/ts-sdk/tests/integration/file-system.test.ts index 263582a68a..bf860545b4 100644 --- a/rock/ts-sdk/tests/integration/file-system.test.ts +++ b/rock/ts-sdk/tests/integration/file-system.test.ts @@ -14,9 +14,9 @@ import * as os from 'os'; const TEST_CONFIG = { baseUrl: process.env.ROCK_BASE_URL || 'http://11.166.8.116:8080', - image: 'reg.docker.alibaba-inc.com/yanan/python:3.11', - cluster: 'zb', - startupTimeout: 120, + image: process.env.ROCK_TEST_IMAGE || 'reg.docker.alibaba-inc.com/yanan/python:3.11', + cluster: process.env.ROCK_TEST_CLUSTER || 'zb', + startupTimeout: parseInt(process.env.ROCK_STARTUP_TIMEOUT || '120', 10), }; describe('FileSystem Integration', () => { @@ -32,7 +32,7 @@ describe('FileSystem Integration', () => { // Create a temporary directory for test files tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rock-test-')); - }, 180000); // 3 minutes timeout for sandbox startup + }, parseInt(process.env.ROCK_TEST_TIMEOUT || '180000', 10)); afterEach(async () => { // Cleanup: ensure sandbox is stopped even if test fails @@ -87,7 +87,7 @@ describe('FileSystem Integration', () => { // Assert: Nested files should exist const nestedResult = await sandbox.arun(`cat ${targetDir}/subdir/file3.txt`, { mode: RunMode.NORMAL }); expect(nestedResult.output.trim()).toBe('Nested File'); - }, 180000); + }, parseInt(process.env.ROCK_TEST_TIMEOUT || '180000', 10)); test('should return error when source directory does not exist', async () => { const nonExistentDir = path.join(tempDir, 'nonexistent'); @@ -149,6 +149,6 @@ describe('FileSystem Integration', () => { // Use test command instead of ls to avoid throwing on non-existent file const oldFileCheck = await sandbox.arun(`test -f ${targetDir}/oldfile.txt && echo "exists" || echo "not exists"`, { mode: RunMode.NORMAL }); expect(oldFileCheck.output.trim()).toBe('not exists'); - }, 180000); + }, parseInt(process.env.ROCK_TEST_TIMEOUT || '180000', 10)); }); }); diff --git a/rock/ts-sdk/tests/integration/sandbox-lifecycle.test.ts b/rock/ts-sdk/tests/integration/sandbox-lifecycle.test.ts index 5916d03dea..f486b0426f 100644 --- a/rock/ts-sdk/tests/integration/sandbox-lifecycle.test.ts +++ b/rock/ts-sdk/tests/integration/sandbox-lifecycle.test.ts @@ -11,9 +11,9 @@ import { Sandbox } from '../../src/sandbox/client.js'; const TEST_CONFIG = { baseUrl: process.env.ROCK_BASE_URL || 'http://11.166.8.116:8080', - image: 'reg.docker.alibaba-inc.com/yanan/python:3.11', - cluster: 'zb', - startupTimeout: 120, // 2 minutes timeout for sandbox startup + image: process.env.ROCK_TEST_IMAGE || 'reg.docker.alibaba-inc.com/yanan/python:3.11', + cluster: process.env.ROCK_TEST_CLUSTER || 'zb', + startupTimeout: parseInt(process.env.ROCK_STARTUP_TIMEOUT || '120', 10), }; describe('Sandbox Lifecycle Integration', () => { @@ -53,5 +53,5 @@ describe('Sandbox Lifecycle Integration', () => { // After stop, isAlive may throw error, which is acceptable expect(String(e)).toContain('Failed to get is alive'); } - }, 180000); // 3 minutes timeout for the whole test + }, parseInt(process.env.ROCK_TEST_TIMEOUT || '180000', 10)); }); From c18332abb8696eb3cd7f37ea6a191e7318dc30e2 Mon Sep 17 00:00:00 2001 From: guojing Date: Mon, 20 Apr 2026 22:38:03 +0800 Subject: [PATCH 2/2] feat(ts-sdk): add integration test CI --- .github/workflows/ts-sdk-ci.yml | 87 +++++++++++++++++---------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ts-sdk-ci.yml b/.github/workflows/ts-sdk-ci.yml index 31e6934628..3c077dcb02 100644 --- a/.github/workflows/ts-sdk-ci.yml +++ b/.github/workflows/ts-sdk-ci.yml @@ -43,52 +43,58 @@ jobs: integration-test: runs-on: self-hosted timeout-minutes: 20 - defaults: - run: - working-directory: rock/ts-sdk steps: - uses: actions/checkout@v4 + # --- Cleanup & Python Setup (following python-ci.yml pattern) --- - name: Cleanup old Ray sessions run: | - echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" - ray stop --force 2>/dev/null || true - docker rm -f $(docker ps -aq) 2>/dev/null || true - working-directory: . - - - name: Install Python dependencies with uv + target="$(readlink -f /tmp/ray 2>/dev/null || echo /tmp/ray)" + [ -d "$target" ] || exit 0 + echo "ray dir: $target" + echo "Before:" + du -sh "$target" 2>/dev/null || true + find "$target" -mindepth 1 -maxdepth 1 -type d -name 'session_*' -mmin +60 -exec rm -rf -- {} + || true + echo "After:" + du -sh "$target" 2>/dev/null || true + + - name: Install ROCK Python dependencies run: | echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" uv sync --all-extras --python 3.11 - working-directory: . - - name: Pull test Docker image + - name: Prepare Docker image run: | echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" - docker image inspect python:3.11 > /dev/null 2>&1 || docker pull python:3.11 - working-directory: . + docker system prune -f 2>/dev/null || true + if ! docker image inspect python:3.11 > /dev/null 2>&1; then + echo "Pulling python:3.11..." + docker pull python:3.11 + else + echo "python:3.11 already exists, skipping pull" + fi - - name: Start ROCK Admin server + - name: Start ROCK Admin service run: | echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" export ROCK_WORKER_ENV_TYPE=uv nohup uv run admin --env ci --role admin --port 8080 > /tmp/rock-admin.log 2>&1 & - echo $! > /tmp/rock-admin.pid - echo "Waiting for Admin server to be ready..." + echo "Waiting for admin service to be ready..." for i in $(seq 1 60); do if grep -q "Uvicorn running on" /tmp/rock-admin.log 2>/dev/null; then - echo "Admin server is ready!" + echo "Admin service is ready!" break fi - if [ $i -eq 60 ]; then - echo "Admin server failed to start within 60 seconds" - cat /tmp/rock-admin.log - exit 1 - fi - sleep 1 + echo "Attempt $i/60: Admin not ready yet, waiting 2s..." + sleep 2 done - working-directory: . + if ! grep -q "Uvicorn running on" /tmp/rock-admin.log 2>/dev/null; then + echo "ERROR: Admin service failed to start!" + cat /tmp/rock-admin.log + exit 1 + fi + # --- Node.js & TS SDK Setup --- - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -99,12 +105,15 @@ jobs: echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" npm install -g pnpm - - name: Install dependencies + - name: Install TS SDK dependencies + working-directory: rock/ts-sdk run: | echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" pnpm install --frozen-lockfile + # --- Run Integration Tests --- - name: Run integration tests + working-directory: rock/ts-sdk env: ROCK_BASE_URL: http://127.0.0.1:8080 ROCK_TEST_IMAGE: python:3.11 @@ -115,28 +124,24 @@ jobs: echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" pnpm test:integration - - name: Print debug logs + # --- Cleanup & Diagnostics --- + - name: Print admin logs on failure if: always() run: | - echo "=== Admin server logs ===" - cat /tmp/rock-admin.log 2>/dev/null || echo "No admin log found" - echo "" - echo "=== Docker containers ===" - docker ps -a 2>/dev/null || true - echo "" - echo "=== Ray status ===" - ray status 2>/dev/null || true - working-directory: . + echo "=== ROCK Admin Logs (last 200 lines) ===" + tail -200 /tmp/rock-admin.log || true + echo "=== Docker Containers ===" + docker ps -a || true + echo "=== Ray Status ===" + uv run ray status 2>/dev/null || true + echo "=== Ray Runtime Env Logs ===" + find /tmp/ray -name "runtime_env*.log" -exec tail -50 {} \; 2>/dev/null || true - name: Cleanup if: always() run: | - echo "=== Task started at: $(date '+%Y-%m-%d %H:%M:%S') ===" - if [ -f /tmp/rock-admin.pid ]; then - kill $(cat /tmp/rock-admin.pid) 2>/dev/null || true - fi - ray stop --force 2>/dev/null || true docker rm -f $(docker ps -aq) 2>/dev/null || true - working-directory: . + ray stop --force 2>/dev/null || true + pkill -f "admin --env ci" 2>/dev/null || true