Merge pull request #48 from TashanGKD/lyy/worldweave-rebuild-scene-warm #271
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy | |
| on: | |
| push: | |
| branches: | |
| - main | |
| concurrency: | |
| group: deploy-${{ github.repository }} | |
| cancel-in-progress: true | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - name: Deploy to Server | |
| uses: appleboy/ssh-action@v1.0.3 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPO: ${{ github.repository }} | |
| DEPLOY_ENV: ${{ secrets.DEPLOY_ENV }} | |
| SUBMODULE_TOKEN: ${{ secrets.SUBMODULE_TOKEN }} | |
| with: | |
| host: ${{ secrets.DEPLOY_HOST }} | |
| username: ${{ secrets.DEPLOY_USER }} | |
| key: ${{ secrets.SSH_PRIVATE_KEY }} | |
| envs: GITHUB_TOKEN,REPO,DEPLOY_ENV,SUBMODULE_TOKEN | |
| timeout: 120m | |
| command_timeout: 120m | |
| script: | | |
| set -euo pipefail | |
| BASE_DIR="${{ secrets.DEPLOY_PATH || '/var/www/github-actions/repos' }}" | |
| REPO_NAME=$(basename "$REPO") | |
| REPO_DIR="$BASE_DIR/$REPO_NAME" | |
| REPO_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/${REPO}.git" | |
| if [ ! -d "$REPO_DIR/.git" ]; then | |
| mkdir -p "$BASE_DIR" | |
| # Clone without recursing first; nested submodule auth is configured below. | |
| for i in 1 2 3; do git clone "$REPO_URL" "$REPO_DIR" && break || sleep 5; done | |
| fi | |
| cd "$REPO_DIR" | |
| git remote set-url origin "$REPO_URL" | |
| SUBMODULE_AUTH="${SUBMODULE_TOKEN:-$GITHUB_TOKEN}" | |
| set_submodule_url() { | |
| local name="$1" | |
| local repo="$2" | |
| local url="https://x-access-token:${SUBMODULE_AUTH}@github.com/${repo}.git" | |
| git config "submodule.${name}.url" "$url" | |
| [ -d ".git/modules/${name}" ] && git config -f ".git/modules/${name}/config" remote.origin.url "$url" | |
| } | |
| for i in 1 2 3; do git fetch origin && break || { [ $i -eq 3 ] && echo "git fetch failed after 3 retries" && exit 1; sleep 5; }; done | |
| git reset --hard origin/main | |
| git submodule sync --recursive | |
| # Configure submodule URLs after sync; otherwise cached SSH URLs or .gitmodules defaults can win. | |
| set_submodule_url backend TashanGKD/Resonnet | |
| set_submodule_url topiclab-cli TashanGKD/TopicLab-CLI | |
| set_submodule_url ClawArcade TashanGKD/ClawArcade | |
| set_submodule_url worldweave TashanGKD/worldweave | |
| git submodule update --init backend | |
| git config -f .git/modules/backend/config submodule.libs/assignable_skills/_submodules/ai-research.url "https://x-access-token:${SUBMODULE_AUTH}@github.com/Orchestra-Research/AI-Research-SKILLs.git" | |
| git config -f .git/modules/backend/config submodule.libs/assignable_skills/_submodules/anthropics.url "https://x-access-token:${SUBMODULE_AUTH}@github.com/anthropics/skills.git" | |
| git config -f .git/modules/backend/config submodule.libs/assignable_skills/_submodules/claude-scientific.url "https://x-access-token:${SUBMODULE_AUTH}@github.com/K-Dense-AI/claude-scientific-skills.git" | |
| git config -f .git/modules/backend/config --get submodule.libs/assignable_skills/_submodules/claude-scientific.url | sed -E 's#x-access-token:[^@]+@#x-access-token:***@#' | |
| git submodule update --init --recursive | |
| if [ -z "${DEPLOY_ENV:-}" ]; then | |
| echo "::error::DEPLOY_ENV secret is not set. Add it in repo Settings → Secrets." | |
| exit 1 | |
| fi | |
| echo "$DEPLOY_ENV" > .env | |
| run_with_heartbeat() { | |
| local label="$1" | |
| shift | |
| "$@" & | |
| local pid="$!" | |
| while kill -0 "$pid" 2>/dev/null; do | |
| echo "[deploy] ${label} still running at $(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| sleep 30 | |
| done | |
| wait "$pid" | |
| } | |
| run_with_heartbeat "docker compose build" docker compose build | |
| docker compose down | |
| docker compose up -d | |
| for i in $(seq 1 30); do | |
| if docker compose exec -T worldweave node -e "fetch('http://127.0.0.1:3020/api/v1/openclaw/skill.md').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"; then | |
| break | |
| fi | |
| if [ "$i" = "30" ]; then | |
| echo "::error::WorldWeave container did not become healthy on port 3020." | |
| docker compose logs --tail=120 worldweave | |
| exit 1 | |
| fi | |
| sleep 2 | |
| done | |
| if ! docker compose exec -T worldweave sh -lc "WORLD_HEALTH_BASE_URL=http://127.0.0.1:3020 WORLD_HEALTH_CHECK_SCENES=tech-ai,geo-politics-daily WORLD_HEALTH_FAIL_ON_REFRESH_DEGRADED=1 node scripts/health-world.mjs"; then | |
| echo "::warning::WorldWeave scene freshness health check failed. Check the worldweave-refresh logs and source refresh status." | |
| docker compose logs --tail=120 worldweave-refresh || true | |
| fi | |
| docker image prune -f | |
| # Read frontend port from .env (default 3000); trim whitespace/CRLF to avoid nginx config issues | |
| FRONTEND_PORT=$(grep -E '^FRONTEND_PORT=' .env 2>/dev/null | head -1 | cut -d= -f2- | tr -d ' \r' || echo 3000) | |
| # Write nginx snippet (location block only, included by world.tashan.chat server block). | |
| # WorldWeave routes are handled inside the frontend container nginx. | |
| # Requires VITE_BASE_PATH=/ in DEPLOY_ENV so frontend matches /api/. | |
| mkdir -p /etc/nginx/snippets | |
| printf '%s\n' \ | |
| 'location / {' \ | |
| " proxy_pass http://127.0.0.1:${FRONTEND_PORT};" \ | |
| ' proxy_set_header Host $host;' \ | |
| ' proxy_set_header X-Real-IP $remote_addr;' \ | |
| ' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' \ | |
| ' proxy_set_header X-Forwarded-Proto $scheme;' \ | |
| '}' \ | |
| 'location /api/ {' \ | |
| " proxy_pass http://127.0.0.1:${FRONTEND_PORT}/api/;" \ | |
| ' proxy_http_version 1.1;' \ | |
| ' proxy_set_header Host $host;' \ | |
| ' proxy_set_header X-Real-IP $remote_addr;' \ | |
| ' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' \ | |
| ' proxy_set_header X-Forwarded-Proto $scheme;' \ | |
| ' proxy_set_header Connection "";' \ | |
| ' proxy_buffering off;' \ | |
| ' proxy_cache off;' \ | |
| ' chunked_transfer_encoding on;' \ | |
| ' proxy_read_timeout 600s;' \ | |
| ' proxy_send_timeout 600s;' \ | |
| '}' \ | |
| | sudo tee /etc/nginx/snippets/world-tashan-chat.conf > /dev/null | |
| sudo nginx -t && sudo nginx -s reload | |
| chmod +x scripts/deploy-clawarcade-reviewer.sh | |
| scripts/deploy-clawarcade-reviewer.sh | |
| echo "--------------------------------------------" | |
| echo "Frontend: http://${{ secrets.DEPLOY_HOST }}" | |
| echo "Commit: ${{ github.sha }}" | |
| echo "--------------------------------------------" | |
| cli-smoke: | |
| name: Post-deploy CLI smoke (${{ matrix.cli_source }}) | |
| needs: [deploy] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| cli_source: [package, submodule] | |
| env: | |
| TOPICLAB_BASE_URL: https://world.tashan.chat | |
| TOPICLAB_BIND_KEY: ${{ secrets.TOPICLAB_BIND_KEY }} | |
| TOPICLAB_LIVE_SMOKE_ALLOW_WRITES: ${{ vars.TOPICLAB_LIVE_SMOKE_ALLOW_WRITES || '0' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| cache-dependency-path: topiclab-cli/package-lock.json | |
| - name: Check write smoke secrets | |
| run: | | |
| if [[ "${TOPICLAB_LIVE_SMOKE_ALLOW_WRITES:-0}" == "1" && -z "${TOPICLAB_BIND_KEY:-}" ]]; then | |
| echo "::error::TOPICLAB_BIND_KEY secret is required when TOPICLAB_LIVE_SMOKE_ALLOW_WRITES=1." | |
| exit 1 | |
| fi | |
| - name: Wait for deployed services to settle | |
| run: sleep 60 | |
| - name: Install published topiclab-cli | |
| if: matrix.cli_source == 'package' | |
| run: | | |
| npm install -g topiclab-cli@latest | |
| npm list -g topiclab-cli --depth=0 || true | |
| - name: Install submodule topiclab-cli | |
| if: matrix.cli_source == 'submodule' | |
| run: | | |
| pushd topiclab-cli | |
| npm ci | |
| npm run build | |
| popd | |
| npm install -g ./topiclab-cli | |
| node -p "require('./topiclab-cli/package.json').version" | |
| git -C topiclab-cli rev-parse HEAD | |
| - name: Run live smoke | |
| run: | | |
| chmod +x scripts/openclaw-live-skill-smoke.sh | |
| args=( | |
| --bind-key "${TOPICLAB_BIND_KEY}" | |
| --base-url "${TOPICLAB_BASE_URL}" | |
| --clean-cli-home | |
| ) | |
| if [[ "${TOPICLAB_LIVE_SMOKE_ALLOW_WRITES:-0}" == "1" ]]; then | |
| args+=(--allow-writes) | |
| fi | |
| ./scripts/openclaw-live-skill-smoke.sh "${args[@]}" |