- Introduced MySQL and PostgreSQL environment variables in the unit test workflow for better database integration during testing. - Configured MySQL and PostgreSQL services in the workflow to ensure proper database connectivity and health checks. - Updated the sandbox-v2 environment file to include database connection details for local testing. Made-with: Cursor
427 lines
16 KiB
YAML
427 lines
16 KiB
YAML
name: Unit Test V1
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
tags:
|
|
description: "Version"
|
|
|
|
env:
|
|
CI_VERSION: "1.0.0"
|
|
REPO_KUN: ${{ github.repository_owner }}/kun
|
|
REPO_XUN: ${{ github.repository_owner }}/xun
|
|
REPO_GOU: ${{ github.repository_owner }}/gou
|
|
|
|
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@"
|
|
|
|
YAO_EXTENSION_ROOT: ${{ github.WORKSPACE }}/../extension
|
|
YAO_TEST_APPLICATION: ${{ github.WORKSPACE }}/../app
|
|
|
|
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
|
|
|
|
MYSQL_TEST_HOST: "127.0.0.1"
|
|
MYSQL_TEST_PORT: "3308"
|
|
MYSQL_TEST_USER: "test"
|
|
MYSQL_TEST_PASS: "123456"
|
|
|
|
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"
|
|
|
|
PG_TEST_HOST: "127.0.0.1"
|
|
PG_TEST_PORT: "5432"
|
|
PG_TEST_USER: "test"
|
|
PG_TEST_PASS: "123456"
|
|
|
|
jobs:
|
|
# =============================================================================
|
|
# Environment Setup & Verification
|
|
# Build Yao, start services, connect Tai via gRPC tunnel, verify everything.
|
|
# No tests are run — this job validates the CI environment is healthy.
|
|
# =============================================================================
|
|
setup-and-verify:
|
|
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
|
|
|
|
mysql:
|
|
image: mysql:8.0
|
|
ports:
|
|
- 3308:3306
|
|
env:
|
|
MYSQL_ROOT_PASSWORD: 123456
|
|
MYSQL_USER: test
|
|
MYSQL_PASSWORD: 123456
|
|
MYSQL_DATABASE: test
|
|
options: >-
|
|
--health-cmd="mysqladmin ping -h 127.0.0.1"
|
|
--health-interval=10s
|
|
--health-timeout=5s
|
|
--health-retries=5
|
|
--character-set-server=utf8mb4
|
|
--collation-server=utf8mb4_general_ci
|
|
|
|
postgres:
|
|
image: postgres:14
|
|
ports:
|
|
- 5432:5432
|
|
env:
|
|
POSTGRES_USER: test
|
|
POSTGRES_PASSWORD: 123456
|
|
POSTGRES_DB: test
|
|
options: >-
|
|
--health-cmd="pg_isready -U test"
|
|
--health-interval=10s
|
|
--health-timeout=5s
|
|
--health-retries=5
|
|
|
|
strategy:
|
|
matrix:
|
|
go: ["1.25"]
|
|
|
|
steps:
|
|
# ==== Phase 1: Checkout & Setup ====
|
|
- name: Checkout Yao
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Build Environment
|
|
uses: ./.github/actions/setup-yao
|
|
with:
|
|
repo-kun: ${{ env.REPO_KUN }}
|
|
repo-xun: ${{ env.REPO_XUN }}
|
|
repo-gou: ${{ env.REPO_GOU }}
|
|
checkout-init: "true"
|
|
apple-private-key: ${{ secrets.APPLE_PRIVATE_KEY_USER }}
|
|
|
|
- name: Load sandbox-v2 env
|
|
run: grep -vE '^\s*#|^\s*$' .github/env/sandbox-v2.env >> $GITHUB_ENV
|
|
|
|
- name: Setup SQLite
|
|
run: |
|
|
mkdir -p ${{ github.WORKSPACE }}/../app/db
|
|
echo "YAO_DB_PRIMARY=${{ github.WORKSPACE }}/../app/db/yao.db" >> $GITHUB_ENV
|
|
|
|
- name: Start Redis
|
|
run: docker run --name redis -d -p 6379:6379 redis:6
|
|
|
|
# ==== Phase 2: Build Yao & ci-token ====
|
|
- name: Build Yao
|
|
run: go build -v -o $RUNNER_TEMP/yao .
|
|
|
|
- name: Build ci-token
|
|
run: go build -tags ci -v -o $RUNNER_TEMP/ci-token ./cmd/ci-token
|
|
|
|
# ==== Phase 3: Prepare & Start Yao ====
|
|
- name: Prepare test app directory
|
|
run: |
|
|
cp -r ${{ github.WORKSPACE }}/../yao-init $RUNNER_TEMP/yao-test-app
|
|
mkdir -p $RUNNER_TEMP/yao-test-app/db
|
|
|
|
- name: Start Yao server
|
|
run: |
|
|
cd $RUNNER_TEMP/yao-test-app
|
|
YAO_ROOT=$(pwd) \
|
|
YAO_HOST=0.0.0.0 \
|
|
YAO_PORT=5099 \
|
|
YAO_GRPC_HOST=0.0.0.0 \
|
|
YAO_GRPC_PORT=9099 \
|
|
YAO_DB_DRIVER=sqlite3 \
|
|
YAO_DB_PRIMARY=$(pwd)/db/yao.db \
|
|
YAO_SESSION=memory \
|
|
YAO_ENV=development \
|
|
YAO_JWT_SECRET="${{ env.YAO_JWT_SECRET }}" \
|
|
YAO_DB_AESKEY="${{ env.YAO_DB_AESKEY }}" \
|
|
$RUNNER_TEMP/yao start &
|
|
|
|
# Wait for Yao HTTP to be ready (up to 120s)
|
|
for i in $(seq 1 60); do
|
|
if curl -sf http://127.0.0.1:5099/.well-known/yao > /dev/null 2>&1; then
|
|
echo "Yao HTTP ready"
|
|
curl -s http://127.0.0.1:5099/.well-known/yao | jq .
|
|
break
|
|
fi
|
|
echo "Waiting for Yao... ($i/60)"
|
|
sleep 2
|
|
done
|
|
|
|
curl -sf http://127.0.0.1:5099/.well-known/yao > /dev/null 2>&1 || {
|
|
echo "::error::Yao HTTP failed to start"
|
|
exit 1
|
|
}
|
|
|
|
# ==== Phase 4: Generate Tai credentials ====
|
|
- name: Generate Tai credentials
|
|
run: |
|
|
gen_cred() {
|
|
local CID=$1 TID=$2 OUT=$3
|
|
local TOKEN
|
|
TOKEN=$($RUNNER_TEMP/ci-token \
|
|
--app $RUNNER_TEMP/yao-test-app \
|
|
--client-id "$CID" \
|
|
--subject "${YAO_CI_OAUTH_SUBJECT:-ci-tai}" \
|
|
--user-id "${YAO_CI_OAUTH_USER_ID}" \
|
|
--team-id "${YAO_CI_OAUTH_TEAM_ID}" \
|
|
--scope "${YAO_CI_OAUTH_SCOPE:-tai:tunnel}" \
|
|
--ttl "${YAO_CI_OAUTH_TTL:-24h}" 2>/dev/null | tail -1 | tr -d '[:space:]')
|
|
|
|
local JSON="{\"client_id\":\"$CID\",\"tai_id\":\"$TID\",\"machine_id\":\"ci-runner\",\"server\":\"http://${YAO_CI_BRIDGE_IP}:${YAO_CI_HTTP_PORT}\",\"yao_grpc_addr\":\"${YAO_CI_BRIDGE_IP}:${YAO_CI_GRPC_PORT}\",\"access_token\":\"$TOKEN\",\"scope\":\"${YAO_CI_OAUTH_SCOPE}\",\"expires_at\":\"2099-01-01T00:00:00Z\",\"registered\":true}"
|
|
echo -n "$JSON" | base64 -w0 > "$OUT"
|
|
echo ""
|
|
echo "Credentials JSON (debug): $JSON" | head -c 200
|
|
echo "..."
|
|
echo "Generated credentials for $CID → $OUT"
|
|
}
|
|
|
|
gen_cred tai-ci-docker tai-docker-001 $RUNNER_TEMP/tai-docker-credentials
|
|
gen_cred tai-ci-k8s tai-k8s-001 $RUNNER_TEMP/tai-k8s-credentials
|
|
|
|
# ==== Phase 5: Pull images & Setup K8s ====
|
|
- name: Pull test images
|
|
run: |
|
|
docker pull yaoapp/tai-sandbox-test:latest || true
|
|
docker pull yaoapp/tai:latest
|
|
docker pull alpine:latest
|
|
|
|
- name: Install k3d & create cluster
|
|
run: |
|
|
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
|
|
k3d cluster create tai-test --no-lb --wait --api-port 16443
|
|
kubectl wait --for=condition=Ready node --all --timeout=60s
|
|
k3d image import alpine:latest -c tai-test
|
|
|
|
- name: Generate kubeconfig
|
|
run: |
|
|
K3D_IP=$(docker inspect k3d-tai-test-server-0 | jq -r '.[0].NetworkSettings.Networks["k3d-tai-test"].IPAddress')
|
|
echo "k3d server IP: ${K3D_IP}"
|
|
|
|
k3d kubeconfig get tai-test > /tmp/kubeconfig-k3d.yml
|
|
|
|
# For tai-k8s container (uses k3d internal IP)
|
|
sed "s|server: .*|server: https://${K3D_IP}:6443|" /tmp/kubeconfig-k3d.yml \
|
|
> /tmp/kubeconfig-tai-k8s.yml
|
|
echo "Container kubeconfig server:"
|
|
grep server: /tmp/kubeconfig-tai-k8s.yml
|
|
|
|
# For test runner (uses localhost via port-mapped 6443)
|
|
sed 's|server: .*|server: https://127.0.0.1:6443|' /tmp/kubeconfig-k3d.yml \
|
|
> $RUNNER_TEMP/kubeconfig-tai.yml
|
|
echo "Test runner kubeconfig server:"
|
|
grep server: $RUNNER_TEMP/kubeconfig-tai.yml
|
|
|
|
# Export for later steps
|
|
echo "TAI_TEST_KUBECONFIG=$RUNNER_TEMP/kubeconfig-tai.yml" >> $GITHUB_ENV
|
|
echo "YAO_CI_TAI_KUBECONFIG=$RUNNER_TEMP/kubeconfig-tai.yml" >> $GITHUB_ENV
|
|
|
|
# ==== Phase 6: Start Tai instances ====
|
|
- name: Start tai-docker
|
|
run: |
|
|
docker run -d --name tai-docker \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-v $RUNNER_TEMP/tai-docker-credentials:/root/.tai/credentials:ro \
|
|
-e TAI_YAO_SERVER=http://${YAO_CI_BRIDGE_IP}:${YAO_CI_HTTP_PORT} \
|
|
-p ${YAO_CI_TAI_GRPC_PORT}:19100 \
|
|
-p ${YAO_CI_TAI_HTTP_PORT}:8099 \
|
|
-p ${YAO_CI_TAI_DOCKER_PORT}:12375 \
|
|
-p ${YAO_CI_TAI_VNC_PORT}:16080 \
|
|
yaoapp/tai:latest server \
|
|
-grpc 0.0.0.0:19100 -http 0.0.0.0:8099 -vnc 0.0.0.0:16080 -docker 0.0.0.0:12375
|
|
|
|
for i in $(seq 1 30); do
|
|
if curl -sf http://127.0.0.1:${YAO_CI_TAI_HTTP_PORT}/healthz > /dev/null 2>&1; then
|
|
echo "tai-docker HTTP ready"
|
|
break
|
|
fi
|
|
echo "Waiting for tai-docker HTTP... ($i/30)"
|
|
sleep 1
|
|
done
|
|
|
|
- name: Start tai-k8s
|
|
run: |
|
|
K3D_IP=$(docker inspect k3d-tai-test-server-0 | jq -r '.[0].NetworkSettings.Networks["k3d-tai-test"].IPAddress')
|
|
|
|
docker run -d --name tai-k8s \
|
|
--network k3d-tai-test \
|
|
-v $RUNNER_TEMP/tai-k8s-credentials:/root/.tai/credentials:ro \
|
|
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
|
-v /tmp/kubeconfig-tai-k8s.yml:/etc/tai/kubeconfig.yml:ro \
|
|
-e TAI_YAO_SERVER=http://${YAO_CI_BRIDGE_IP}:${YAO_CI_HTTP_PORT} \
|
|
-e TAI_K8S_UPSTREAM="tcp://${K3D_IP}:6443" \
|
|
-e TAI_KUBECONFIG=/etc/tai/kubeconfig.yml \
|
|
-p ${YAO_CI_TAI_K8S_GRPC_PORT}:19100 \
|
|
-p 8100:8099 \
|
|
-p ${YAO_CI_TAI_K8S_PORT}:16443 \
|
|
-p 16081:16080 \
|
|
yaoapp/tai:latest server \
|
|
-grpc 0.0.0.0:19100 -http 0.0.0.0:8099 -vnc 0.0.0.0:16080 -k8s 0.0.0.0:16443
|
|
|
|
for i in $(seq 1 30); do
|
|
if curl -sf http://127.0.0.1:8100/healthz > /dev/null 2>&1; then
|
|
echo "tai-k8s HTTP ready"
|
|
break
|
|
fi
|
|
echo "Waiting for tai-k8s HTTP... ($i/30)"
|
|
sleep 1
|
|
done
|
|
|
|
# ==== Phase 7: Environment Verification (fail fast) ====
|
|
- name: Verify Environment
|
|
run: |
|
|
echo "CI Environment v${CI_VERSION}"
|
|
echo ""
|
|
|
|
FAILED=0
|
|
check() {
|
|
local name=$1; shift
|
|
if "$@" > /dev/null 2>&1; then
|
|
echo " [PASS] $name"
|
|
else
|
|
echo " [FAIL] $name"
|
|
FAILED=$((FAILED + 1))
|
|
fi
|
|
}
|
|
|
|
echo "=== Environment Verification ==="
|
|
|
|
# ── 1. Service Health ──
|
|
echo ""
|
|
echo "--- 1. Service Health ---"
|
|
|
|
echo "[Yao]"
|
|
check "Yao HTTP (/.well-known/yao)" curl -sf http://127.0.0.1:5099/.well-known/yao
|
|
check "Yao gRPC port" nc -z 127.0.0.1 9099
|
|
|
|
echo "[tai-docker]"
|
|
check "tai-docker HTTP (/healthz)" curl -sf http://127.0.0.1:${YAO_CI_TAI_HTTP_PORT}/healthz
|
|
check "tai-docker gRPC port" nc -z 127.0.0.1 ${YAO_CI_TAI_GRPC_PORT}
|
|
|
|
echo "[tai-k8s]"
|
|
check "tai-k8s HTTP (/healthz)" curl -sf http://127.0.0.1:8100/healthz
|
|
check "tai-k8s gRPC port" nc -z 127.0.0.1 ${YAO_CI_TAI_K8S_GRPC_PORT}
|
|
|
|
echo "[K8s (k3d)]"
|
|
check "kubectl get nodes" kubectl --kubeconfig=$RUNNER_TEMP/kubeconfig-tai.yml get nodes
|
|
|
|
echo "[Data Stores]"
|
|
MONGO_CID=$(docker ps -qf "ancestor=mongo:6.0" | head -1)
|
|
check "MongoDB ping" docker exec "$MONGO_CID" mongosh --quiet \
|
|
-u ${MONGO_TEST_USER} -p ${MONGO_TEST_PASS} --authenticationDatabase admin \
|
|
--eval "db.runCommand({ping:1})"
|
|
|
|
MYSQL_CID=$(docker ps -qf "ancestor=mysql:8.0" | head -1)
|
|
check "MySQL ping" docker exec "$MYSQL_CID" mysqladmin ping -h 127.0.0.1 \
|
|
-u ${MYSQL_TEST_USER} -p${MYSQL_TEST_PASS}
|
|
|
|
PG_CID=$(docker ps -qf "ancestor=postgres:14" | head -1)
|
|
check "PostgreSQL ping" docker exec "$PG_CID" pg_isready -U ${PG_TEST_USER}
|
|
|
|
check "Redis ping" docker exec redis redis-cli ping
|
|
|
|
# ── 2. Network Topology ──
|
|
echo ""
|
|
echo "--- 2. Network Topology ---"
|
|
|
|
BRIDGE_IP=${YAO_CI_BRIDGE_IP}
|
|
echo " docker0 bridge IP: ${BRIDGE_IP}"
|
|
|
|
echo "[Runner → Bridge]"
|
|
check "Bridge IP exists" ip addr show docker0
|
|
check "Bridge IP reachable" ping -c1 -W2 ${BRIDGE_IP}
|
|
|
|
echo "[Runner → Tai Docker API]"
|
|
check "Tai Docker API (tcp://127.0.0.1:${YAO_CI_TAI_DOCKER_PORT})" \
|
|
nc -z 127.0.0.1 ${YAO_CI_TAI_DOCKER_PORT}
|
|
|
|
echo "[Runner → Tai K8s API proxy]"
|
|
check "Tai K8s API (127.0.0.1:${YAO_CI_TAI_K8S_PORT})" \
|
|
nc -z 127.0.0.1 ${YAO_CI_TAI_K8S_PORT}
|
|
|
|
echo "[tai-docker → Yao via bridge]"
|
|
check "tai-docker→Yao HTTP (bridge)" \
|
|
docker exec tai-docker wget -q -O /dev/null --timeout=3 \
|
|
http://${BRIDGE_IP}:${YAO_CI_HTTP_PORT}/.well-known/yao
|
|
check "tai-docker→Yao gRPC (bridge)" \
|
|
docker exec tai-docker nc -z -w3 ${BRIDGE_IP} ${YAO_CI_GRPC_PORT}
|
|
|
|
echo "[tai-k8s → Yao via bridge]"
|
|
check "tai-k8s→Yao HTTP (bridge)" \
|
|
docker exec tai-k8s wget -q -O /dev/null --timeout=3 \
|
|
http://${BRIDGE_IP}:${YAO_CI_HTTP_PORT}/.well-known/yao
|
|
check "tai-k8s→Yao gRPC (bridge)" \
|
|
docker exec tai-k8s nc -z -w3 ${BRIDGE_IP} ${YAO_CI_GRPC_PORT}
|
|
|
|
echo "[tai-k8s → k3d API server]"
|
|
K3D_IP=$(docker inspect k3d-tai-test-server-0 2>/dev/null \
|
|
| jq -r '.[0].NetworkSettings.Networks["k3d-tai-test"].IPAddress' 2>/dev/null || echo "")
|
|
if [ -n "$K3D_IP" ] && [ "$K3D_IP" != "null" ]; then
|
|
check "tai-k8s→k3d API (${K3D_IP}:6443)" \
|
|
docker exec tai-k8s nc -z -w3 ${K3D_IP} 6443
|
|
else
|
|
echo " [SKIP] k3d IP not found"
|
|
fi
|
|
|
|
# ── 3. Tai Tunnel Registration ──
|
|
echo ""
|
|
echo "--- 3. Tai Tunnel Registration ---"
|
|
sleep 5
|
|
|
|
echo "tai-docker tunnel logs:"
|
|
docker logs tai-docker 2>&1 | grep -iE "tunnel|register|connect" | tail -5 || true
|
|
echo "tai-k8s tunnel logs:"
|
|
docker logs tai-k8s 2>&1 | grep -iE "tunnel|register|connect" | tail -5 || true
|
|
|
|
check "tai-docker tunnel connected" \
|
|
bash -c 'docker logs tai-docker 2>&1 | grep -qiE "tunnel.*(connected|registered|established)"'
|
|
check "tai-k8s tunnel connected" \
|
|
bash -c 'docker logs tai-k8s 2>&1 | grep -qiE "tunnel.*(connected|registered|established)"'
|
|
|
|
WELL_KNOWN=$(curl -sf http://127.0.0.1:5099/.well-known/yao 2>/dev/null || echo "{}")
|
|
echo "Yao .well-known/yao:"
|
|
echo "$WELL_KNOWN" | jq . 2>/dev/null || echo "$WELL_KNOWN"
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
if [ $FAILED -gt 0 ]; then
|
|
echo "::error::$FAILED verification check(s) FAILED"
|
|
echo ""
|
|
echo "=== Diagnostic Info ==="
|
|
echo "--- Docker containers ---"
|
|
docker ps -a
|
|
echo ""
|
|
echo "--- tai-docker full logs ---"
|
|
docker logs tai-docker 2>&1 | tail -50
|
|
echo ""
|
|
echo "--- tai-k8s full logs ---"
|
|
docker logs tai-k8s 2>&1 | tail -50
|
|
echo ""
|
|
echo "--- Yao process ---"
|
|
ps aux | grep yao || true
|
|
exit 1
|
|
else
|
|
echo "All verification checks PASSED"
|
|
fi
|