name: PR Unit Test # read-write repo token # access to secrets on: workflow_run: workflows: ["Receive PR"] types: - completed env: YAO_DEV: ${{ github.WORKSPACE }} YAO_ENV: development YAO_ROOT: ${{ github.WORKSPACE }}/../app YAO_HOST: 0.0.0.0 YAO_PORT: 5099 YAO_SESSION: "memory" YAO_LOG: "./logs/application.log" YAO_LOG_MODE: "TEXT" YAO_JWT_SECRET: "bLp@bi!oqo-2U+hoTRUG" YAO_DB_AESKEY: "ZLX=T&f6refeCh-ro*r@" OSS_TEST_ID: ${{ secrets.OSS_TEST_ID}} OSS_TEST_SECRET: ${{ secrets.OSS_TEST_SECRET}} ROOT_PLUGIN: ${{ github.WORKSPACE }}/../../../data/gou-unit/plugins MYSQL_TEST_HOST: "127.0.0.1" MYSQL_TEST_PORT: "3308" MYSQL_TEST_USER: test MYSQL_TEST_PASS: "123456" SQLITE_DB: "./app/db/yao.db" REDIS_TEST_HOST: "127.0.0.1" REDIS_TEST_PORT: "6379" REDIS_TEST_DB: "2" MONGO_TEST_HOST: "127.0.0.1" MONGO_TEST_PORT: "27017" MONGO_TEST_USER: "root" MONGO_TEST_PASS: "123456" OPENAI_TEST_KEY: ${{ secrets.OPENAI_TEST_KEY }} TEST_MOAPI_SECRET: ${{ secrets.OPENAI_TEST_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_TEST_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} TEST_MOAPI_MIRROR: https://api.openai.com # DeepSeek API Configuration DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} DEEPSEEK_API_PROXY: ${{ secrets.DEEPSEEK_API_PROXY }} DEEPSEEK_MODELS_R1: ${{ secrets.DEEPSEEK_MODELS_R1 }} DEEPSEEK_MODELS_V3: ${{ secrets.DEEPSEEK_MODELS_V3 }} DEEPSEEK_MODELS_V3_1: ${{ secrets.DEEPSEEK_MODELS_V3_1 }} # Search API Configuration TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }} SERPAPI_API_KEY: ${{ secrets.SERPAPI_API_KEY }} SERPER_API_KEY: ${{ secrets.SERPER_API_KEY }} # Claude API Configuration CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }} CLAUDE_PROXY: ${{ secrets.CLAUDE_PROXY }} CLAUDE_API_HOST: ${{ secrets.CLAUDE_API_HOST }} CLAUDE_SONNET_4: ${{ secrets.CLAUDE_SONNET_4 }} CLAUDE_SONNET_4_THINKING: ${{ secrets.CLAUDE_SONNET_4_THINKING }} # Moonshot / Kimi API Configuration MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }} MOONSHOT_PROXY: "https://api.moonshot.cn" KIMI_CODE_API_KEY: ${{ secrets.KIMI_CODE_API_KEY }} KIMI_CODE_PROXY: "https://api.kimi.com/coding" TAB_NAME: "::PET ADMIN" PAGE_SIZE: "20" PAGE_LINK: "https://yaoapps.com" PAGE_ICON: "icon-trash" DEMO_APP: ${{ github.WORKSPACE }}/../app # Application Setting ## Path YAO_EXTENSION_ROOT: ${{ github.WORKSPACE }}/../extension YAO_TEST_APPLICATION: ${{ github.WORKSPACE }}/../app YAO_SUI_TEST_APPLICATION: ${{ github.WORKSPACE }}/../yao-startup-webapp ## Runtime YAO_RUNTIME_MIN: 3 YAO_RUNTIME_MAX: 6 YAO_RUNTIME_HEAP_LIMIT: 1500000000 YAO_RUNTIME_HEAP_RELEASE: 10000000 YAO_RUNTIME_HEAP_AVAILABLE: 550000000 YAO_RUNTIME_PRECOMPILE: true # Neo4j NEO4J_TEST_URL: "neo4j://localhost:7687" NEO4J_TEST_USER: "neo4j" NEO4J_TEST_PASS: "Yao2026Neo4j" # Qdrant QDRANT_TEST_HOST: "127.0.0.1" QDRANT_TEST_PORT: "6334" # S3 S3_API: ${{ secrets.S3_API }} S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} S3_BUCKET: ${{ secrets.S3_BUCKET }} S3_PUBLIC_URL: ${{ secrets.S3_PUBLIC_URL }} # === Openapi Signin Configs === SIGNIN_CLIENT_ID: "kiCeR88kDwHBDuNHvN51cZgmpp3tmF6Z" ## Google GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} ## Microsoft MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} ## Apple APPLE_SERVICE_ID: ${{ secrets.APPLE_SERVICE_ID }} APPLE_PRIVATE_KEY_PATH: "apple/signin_client_secret_key.p8" APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} ## Github GITHUBUSER_CLIENT_ID: ${{ secrets.GITHUBUSER_CLIENT_ID }} GITHUBUSER_CLIENT_SECRET: ${{ secrets.GITHUBUSER_CLIENT_SECRET }} ## Cloudflare Turnstile CLOUDFLARE_TURNSTILE_SITEKEY: ${{ secrets.CLOUDFLARE_TURNSTILE_SITEKEY }} CLOUDFLARE_TURNSTILE_SECRET: ${{ secrets.CLOUDFLARE_TURNSTILE_SECRET }} # === Messaging Services === ## Mailgun MAILGUN_DOMAIN: ${{ secrets.MAILGUN_DOMAIN }} MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }} MAILGUN_FROM: "Yaobots Tests " ## SMTP Server( Mailgun ) SMTP_HOST: "smtp.mailgun.org" SMTP_PORT: "465" SMTP_USERNAME: ${{ secrets.SMTP_USERNAME }} SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} SMTP_FROM: "Yaobots SMTP Tests " ## SMTP Server( Gmail ) RELIABLE_SMTP_HOST: "smtp.gmail.com" RELIABLE_SMTP_PORT: "465" RELIABLE_SMTP_USERNAME: ${{ secrets.RELIABLE_SMTP_USERNAME }} RELIABLE_SMTP_PASSWORD: ${{ secrets.RELIABLE_SMTP_PASSWORD }} RELIABLE_SMTP_FROM: "Yaobots Gmail Tests " ## IMAP Server (Gmail) RELIABLE_IMAP_HOST: "imap.gmail.com" RELIABLE_IMAP_PORT: "993" RELIABLE_IMAP_USERNAME: ${{ secrets.RELIABLE_SMTP_USERNAME }} RELIABLE_IMAP_PASSWORD: ${{ secrets.RELIABLE_SMTP_PASSWORD }} RELIABLE_IMAP_MAILBOX: "INBOX" ## Twilio TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }} TWILIO_AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }} TWILIO_API_SID: ${{ secrets.TWILIO_API_SID }} TWILIO_API_KEY: ${{ secrets.TWILIO_API_KEY }} TWILIO_SENDGRID_API_SID: ${{ secrets.TWILIO_SENDGRID_API_SID }} TWILIO_SENDGRID_API_KEY: ${{ secrets.TWILIO_SENDGRID_API_KEY }} TWILIO_FROM_PHONE: "+17035701412" TWILIO_FROM_EMAIL: "unit-test@sendgrid.yaobots.com" TWILIO_TEST_PHONE: ${{ secrets.TWILIO_TEST_PHONE }} jobs: # ============================================================================= # KB Tests (kb) - Run once with SQLite (requires Qdrant, Neo4j, FastEmbed) # ============================================================================= KBTest: runs-on: ubuntu-latest services: qdrant: image: qdrant/qdrant:latest ports: - 6333:6333 - 6334:6334 fastembed: image: yaoapp/fastembed:latest-amd64 env: FASTEMBED_PASSWORD: Yao@2026 ports: - 6001:8000 neo4j: image: neo4j:latest ports: - "7687:7687" env: NEO4J_AUTH: neo4j/Yao2026Neo4j mongodb: image: mongo:6.0 ports: - 27017:27017 env: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: 123456 MONGO_INITDB_DATABASE: test strategy: matrix: go: ["1.25"] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '🤖 KB Tests (kb) running with SQLite...' }); - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Apple Private Key run: | mkdir -p ../app/openapi/certs/apple echo "${{ secrets.APPLE_PRIVATE_KEY_USER }}" > ../app/openapi/certs/apple/signin_client_secret_key.p8 - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Start Redis run: docker run --name redis --publish 6379:6379 --detach redis:6 - name: Setup Go Tools run: make tools - name: Setup ENV (SQLite) run: | mkdir -p ${{ github.WORKSPACE }}/../app/db echo "YAO_DB_DRIVER=sqlite3" >> $GITHUB_ENV echo "YAO_DB_PRIMARY=${{ github.WORKSPACE }}/../app/db/yao.db" >> $GITHUB_ENV - name: Run KB Tests (kb) run: make unit-test-kb - name: Codecov Report uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - name: "Comment on PR - KB Tests Done" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '✅ KB Tests (kb) passed!' }); # ============================================================================= # Agent Tests (agent, aigc) - Run once with SQLite # ============================================================================= AgentTest: runs-on: ubuntu-latest services: qdrant: image: qdrant/qdrant:latest ports: - 6333:6333 - 6334:6334 fastembed: image: yaoapp/fastembed:latest-amd64 env: FASTEMBED_PASSWORD: Yao@2026 ports: - 6001:8000 neo4j: image: neo4j:latest ports: - "7687:7687" env: NEO4J_AUTH: neo4j/Yao2026Neo4j mcp-everything: image: yaoapp/mcp-everything:latest ports: - "3021:3021" - "3022:3022" mongodb: image: mongo:6.0 ports: - 27017:27017 env: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: 123456 MONGO_INITDB_DATABASE: test strategy: matrix: go: ["1.25"] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '🤖 Agent Tests (agent, aigc) running with SQLite...' }); - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Apple Private Key run: | mkdir -p ../app/openapi/certs/apple echo "${{ secrets.APPLE_PRIVATE_KEY_USER }}" > ../app/openapi/certs/apple/signin_client_secret_key.p8 - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Start Redis run: docker run --name redis --publish 6379:6379 --detach redis:6 - name: Setup Go Tools run: make tools - name: Install pdftoppm, mutool, imagemagick run: | sudo apt update sudo apt install -y poppler-utils mupdf-tools imagemagick - name: Test pdftoppm, mutool, imagemagick run: | pdftoppm -v mutool -v convert -version - name: Setup ENV (SQLite) run: | mkdir -p ${{ github.WORKSPACE }}/../app/db echo "YAO_DB_DRIVER=sqlite3" >> $GITHUB_ENV echo "YAO_DB_PRIMARY=${{ github.WORKSPACE }}/../app/db/yao.db" >> $GITHUB_ENV - name: Pull Sandbox Test Images run: | docker pull alpine:latest docker pull yaoapp/sandbox-base:latest || true docker pull yaoapp/sandbox-claude:latest || true - name: Run Agent Tests (agent, aigc) env: YAO_SANDBOX_WORKSPACE: ${{ runner.temp }}/sandbox/workspace YAO_SANDBOX_IPC: ${{ runner.temp }}/sandbox/ipc run: | export YAO_SANDBOX_CONTAINER_USER="$(id -u):$(id -g)" make unit-test-agent - name: Codecov Report uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - name: "Comment on PR - Agent Tests Done" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '✅ Agent Tests (agent, aigc) passed!' }); # ============================================================================= # Robot Tests (all agent/robot/... packages) - Unit + E2E with real LLM calls # ============================================================================= RobotTest: runs-on: ubuntu-latest services: mcp-everything: image: yaoapp/mcp-everything:latest ports: - "3021:3021" - "3022:3022" mongodb: image: mongo:6.0 ports: - 27017:27017 env: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: 123456 MONGO_INITDB_DATABASE: test strategy: matrix: go: ["1.25"] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '🤖 Robot Tests (Unit + E2E) running with SQLite...' }); - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Start Redis run: docker run --name redis --publish 6379:6379 --detach redis:6 - name: Setup Go Tools run: make tools - name: Setup ENV (SQLite) run: | mkdir -p ${{ github.WORKSPACE }}/../app/db echo "YAO_DB_DRIVER=sqlite3" >> $GITHUB_ENV echo "YAO_DB_PRIMARY=${{ github.WORKSPACE }}/../app/db/yao.db" >> $GITHUB_ENV - name: Run Robot Tests (Unit + E2E) run: make unit-test-robot - name: Codecov Report uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - name: "Comment on PR - Robot Tests Done" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '✅ Robot Tests (Unit + E2E) passed!' }); # ============================================================================= # Sandbox Tests (requires Docker) - Run with Docker-in-Docker # ============================================================================= SandboxTest: runs-on: ubuntu-latest services: mongodb: image: mongo:6.0 ports: - 27017:27017 env: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: 123456 MONGO_INITDB_DATABASE: test strategy: matrix: go: ["1.25"] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '🤖 Sandbox Tests running with Docker...' }); - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Start Redis run: docker run --name redis --publish 6379:6379 --detach redis:6 - name: Setup Go Tools run: make tools - name: Setup ENV (SQLite) run: | mkdir -p ${{ github.WORKSPACE }}/../app/db echo "YAO_DB_DRIVER=sqlite3" >> $GITHUB_ENV echo "YAO_DB_PRIMARY=${{ github.WORKSPACE }}/../app/db/yao.db" >> $GITHUB_ENV - name: Pull Sandbox Test Images run: | docker pull alpine:latest docker pull yaoapp/sandbox-base:latest || true docker pull yaoapp/sandbox-claude:latest || true - name: Run Sandbox Tests env: YAO_SANDBOX_WORKSPACE: ${{ runner.temp }}/sandbox/workspace YAO_SANDBOX_IPC: ${{ runner.temp }}/sandbox/ipc run: | # Use runner's UID:GID to avoid permission issues export YAO_SANDBOX_CONTAINER_USER="$(id -u):$(id -g)" make unit-test-sandbox - name: Codecov Report uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - name: "Comment on PR - Sandbox Tests Done" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '✅ Sandbox Tests passed!' }); # ============================================================================= # Benchmark & Memory Leak Tests - Run with MySQL8.0 and SQLite3 # ============================================================================= PerfTest: runs-on: ubuntu-latest services: mcp-everything: image: yaoapp/mcp-everything:latest ports: - "3021:3021" - "3022:3022" mongodb: image: mongo:6.0 ports: - 27017:27017 env: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: 123456 MONGO_INITDB_DATABASE: test strategy: matrix: go: ["1.25"] db: [MySQL8.0, SQLite3] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Start Redis run: docker run --name redis --publish 6379:6379 --detach redis:6 - name: Install FFmpeg 7.x run: | wget https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz tar -xf ffmpeg-master-latest-linux64-gpl.tar.xz sudo cp ffmpeg-master-latest-linux64-gpl/bin/ffmpeg /usr/local/bin/ sudo cp ffmpeg-master-latest-linux64-gpl/bin/ffprobe /usr/local/bin/ sudo chmod +x /usr/local/bin/ffmpeg /usr/local/bin/ffprobe - name: Test FFmpeg run: ffmpeg -version - name: Setup Go Tools run: make tools - name: Setup ${{ matrix.db }} uses: ./.github/actions/setup-db with: kind: "${{ matrix.db }}" db: "xiang" user: "xiang" password: ${{ secrets.UNIT_PASS }} - name: Setup ENV env: PASSWORD: ${{ secrets.UNIT_PASS }} run: | echo "YAO_DB_DRIVER=$DB_DRIVER" >> $GITHUB_ENV if [ "$DB_DRIVER" = "mysql" ]; then echo "YAO_DB_PRIMARY=$DB_USER:$PASSWORD@$DB_HOST" >> $GITHUB_ENV else echo "YAO_DB_PRIMARY=${{ github.WORKSPACE }}/../app/db/yao.db" >> $GITHUB_ENV mkdir -p ${{ github.WORKSPACE }}/../app/db fi - name: Run Benchmark & Memory Leak Tests run: | make benchmark make memory-leak # ============================================================================= # Core Tests - Run with DB matrix (MySQL/SQLite/Redis/Mongo combinations) # ============================================================================= CoreTest: runs-on: ubuntu-latest services: qdrant: image: qdrant/qdrant:latest ports: - 6333:6333 # HTTP API - 6334:6334 # gRPC fastembed: image: yaoapp/fastembed:latest-amd64 env: FASTEMBED_PASSWORD: Yao@2026 ports: - 6001:8000 neo4j: image: neo4j:latest ports: - "7687:7687" env: NEO4J_AUTH: neo4j/Yao2026Neo4j mcp-everything: image: yaoapp/mcp-everything:latest ports: - "3021:3021" - "3022:3022" strategy: matrix: go: ["1.25"] db: [MySQL8.0, SQLite3] redis: [4, 5, 6] mongo: ["6.0"] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var fs = require('fs'); var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: 'Thank you for the PR! The db: ${{ matrix.db }} redis: ${{ matrix.redis }} mongo: ${{ matrix.mongo }} test workflow is running, the results of the run will be commented later.' }); - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") # Get the directory where the ZIP file is located echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Yao Startup Webapp uses: actions/checkout@v4 with: repository: yaoapp/yao-startup-webapp submodules: true token: ${{ secrets.YAO_TEST_TOKEN }} path: yao-startup-webapp - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Kun, Xun, Gou, V8Go run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ mv yao-startup-webapp ../ ls -l . ls -l ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Apple Private Key run: | mkdir -p ../app/openapi/certs/apple echo "${{ secrets.APPLE_PRIVATE_KEY_USER }}" > ../app/openapi/certs/apple/signin_client_secret_key.p8 - name: Start Redis run: docker run --name redis --publish 6379:6379 --detach redis:${{ matrix.redis }} - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Install FFmpeg 7.x run: | wget https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz tar -xf ffmpeg-master-latest-linux64-gpl.tar.xz sudo cp ffmpeg-master-latest-linux64-gpl/bin/ffmpeg /usr/local/bin/ sudo cp ffmpeg-master-latest-linux64-gpl/bin/ffprobe /usr/local/bin/ sudo chmod +x /usr/local/bin/ffmpeg /usr/local/bin/ffprobe - name: Test FFmpeg run: ffmpeg -version - name: Install pdftoppm, mutool, imagemagick run: | sudo apt update sudo apt install -y poppler-utils mupdf-tools imagemagick - name: Test pdftoppm, mutool, imagemagick run: | pdftoppm -v mutool -v convert -version - name: Start MongoDB run: | docker run --name mongodb --publish 27017:27017 \ -e MONGO_INITDB_DATABASE=test \ -e MONGO_INITDB_ROOT_USERNAME=root \ -e MONGO_INITDB_ROOT_PASSWORD=123456 \ --detach mongo:${{ matrix.mongo }} # Wait for MongoDB to be ready for i in $(seq 1 20); do if docker exec mongodb mongosh --quiet --port 27017 --username root --password 123456 --eval "db.serverStatus()" > /dev/null 2>&1; then echo "MongoDB is ready" break fi echo "Waiting for MongoDB... ($i)" sleep 1 done - name: Setup MySQL8.0 (connector) uses: ./.github/actions/setup-db with: kind: "MySQL8.0" db: "test" user: "test" password: "123456" port: "3308" - name: Setup ${{ matrix.db }} uses: ./.github/actions/setup-db with: kind: "${{ matrix.db }}" db: "xiang" user: "xiang" password: ${{ secrets.UNIT_PASS }} - name: Setup Go Tools run: | make tools - name: Setup ENV & Host env: PASSWORD: ${{ secrets.UNIT_PASS }} run: | sudo echo "127.0.0.1 local.iqka.com" | sudo tee -a /etc/hosts echo "YAO_DB_DRIVER=$DB_DRIVER" >> $GITHUB_ENV echo "GITHUB_WORKSPACE:\n" && ls -l $GITHUB_WORKSPACE if [ "$DB_DRIVER" = "mysql" ]; then echo "YAO_DB_PRIMARY=$DB_USER:$PASSWORD@$DB_HOST" >> $GITHUB_ENV elif [ "$DB_DRIVER" = "postgres" ]; then echo "YAO_DB_PRIMARY=postgres://$DB_USER:$PASSWORD@$DB_HOST" >> $GITHUB_ENV else echo "YAO_DB_PRIMARY=$YAO_ROOT/$DB_HOST" >> $GITHUB_ENV fi echo ".:\n" && ls -l . echo "..:\n" && ls -l .. ping -c 1 -t 1 local.iqka.com - name: Test Prepare run: | make vet make fmt-check make misspell-check - name: Run Core Tests (exclude AI) run: make unit-test-core - name: Codecov Report uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var fs = require('fs'); var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '✨DONE✨ db: ${{ matrix.db }} redis: ${{ matrix.redis }} mongo: ${{ matrix.mongo }} passed.' }); # ============================================================================= # Registry Client SDK Tests (requires Yao Registry Docker service) # ============================================================================= RegistryTest: runs-on: ubuntu-latest services: yao-registry: image: yaoapp/registry:latest ports: - "8080:8080" env: REGISTRY_INIT_USER: yaoagents REGISTRY_INIT_PASS: yaoagents strategy: matrix: go: ["1.25"] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '🤖 Registry Client SDK Tests running...' }); - name: Wait for Registry run: | for i in $(seq 1 15); do if curl -sf http://localhost:8080/.well-known/yao-registry > /dev/null 2>&1; then echo "Registry is ready" break fi echo "Waiting for registry... ($i)" sleep 1 done - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Run Registry Client Tests env: YAO_REGISTRY_URL: http://localhost:8080 YAO_TEST_APPLICATION: ${{ github.workspace }}/../app run: make unit-test-registry - name: Codecov Report uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - name: "Comment on PR - Registry Tests Done" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '✅ Registry Client SDK Tests passed!' }); # ============================================================================= # Tai SDK Tests (requires Tai container with Docker socket mount) # ============================================================================= TaiTest: runs-on: ubuntu-latest strategy: matrix: go: ["1.25"] if: > ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} steps: - name: "Download artifact" uses: actions/github-script@v7 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: "Read NR & SHA" run: | unzip pr.zip cat NR cat SHA echo HEAD=$(cat SHA) >> $GITHUB_ENV echo NR=$(cat NR) >> $GITHUB_ENV - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '🤖 Tai SDK Tests running...' }); - name: Checkout Kun uses: actions/checkout@v4 with: repository: yaoapp/kun path: kun - name: Checkout Xun uses: actions/checkout@v4 with: repository: yaoapp/xun path: xun - name: Checkout Gou uses: actions/checkout@v4 with: repository: yaoapp/gou path: gou - name: Checkout V8Go uses: actions/checkout@v4 with: repository: yaoapp/v8go path: v8go - name: Unzip libv8 run: | files=$(find ./v8go -name "libv8*.zip") for file in $files; do dir=$(dirname "$file") echo "Extracting $file to directory $dir" unzip -o -d $dir $file rm -rf $dir/__MACOSX done - name: Checkout Demo App uses: actions/checkout@v4 with: repository: yaoapp/yao-dev-app path: app - name: Checkout Extension uses: actions/checkout@v4 with: repository: yaoapp/yao-extensions-dev path: extension - name: Move Dependencies run: | mv kun ../ mv xun ../ mv gou ../ mv v8go ../ mv app ../ mv extension ../ - name: Checkout pull request HEAD commit uses: actions/checkout@v4 with: ref: ${{ env.HEAD }} - name: Setup Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Pull Tai & Test Images run: | docker pull yaoapp/tai:latest docker pull alpine:latest - name: Install k3d run: curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash - name: Create k3d cluster run: | k3d cluster create tai-test --no-lb --wait --api-port 0 kubectl wait --for=condition=Ready node --all --timeout=60s k3d image import alpine:latest -c tai-test - name: Start Tai (with Docker socket + K8s proxy) run: | K3D_IP=$(docker inspect k3d-tai-test-server-0 \ --format '{{.NetworkSettings.Networks.k3d-tai-test.IPAddress}}') echo "k3d server IP: ${K3D_IP}" docker run -d --name tai \ --network k3d-tai-test \ -v /var/run/docker.sock:/var/run/docker.sock \ -p 8080:8080 -p 9100:9100 -p 2375:2375 -p 6080:6080 -p 6443:6443 \ -e TAI_K8S_UPSTREAM="tcp://${K3D_IP}:6443" \ yaoapp/tai:latest for i in $(seq 1 30); do if curl -sf http://127.0.0.1:8080/healthz > /dev/null 2>&1; then echo "Tai is ready" break fi echo "Waiting for Tai... ($i)" sleep 1 done - name: Generate kubeconfig for Tai K8s proxy run: | k3d kubeconfig get tai-test > /tmp/kubeconfig-k3d.yml sed 's|server: .*|server: https://127.0.0.1:6443|' /tmp/kubeconfig-k3d.yml \ > ${{ runner.temp }}/kubeconfig-tai.yml echo "Generated kubeconfig:" grep server: ${{ runner.temp }}/kubeconfig-tai.yml - name: Run Tai SDK Tests env: TAI_TEST_HOST: "127.0.0.1" TAI_TEST_GRPC: "127.0.0.1:9100" TAI_TEST_DOCKER: "tcp://127.0.0.1:2375" TAI_TEST_K8S_HOST: "127.0.0.1" TAI_TEST_K8S_PORT: "6443" TAI_TEST_KUBECONFIG: "${{ runner.temp }}/kubeconfig-tai.yml" run: make unit-test-tai - name: Codecov Report uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - name: "Comment on PR - Tai Tests Done" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { NR } = process.env var issue_number = NR; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue_number, body: '✅ Tai SDK Tests passed!' });