Merge pull request #862 from ruska-ai/fix/worker-stability-sse-streaming #470
Workflow file for this run
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: Build | |
| permissions: | |
| contents: read | |
| packages: write | |
| on: | |
| push: | |
| tags: | |
| - "*" | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| environment: Dev | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| ############################################################### | |
| ## Frontend | |
| ############################################################### | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: "npm" | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install frontend dependencies | |
| working-directory: ./frontend | |
| run: npm ci | |
| - name: Create public directory | |
| working-directory: ./backend | |
| run: mkdir -p src/public | |
| - name: Build frontend | |
| working-directory: ./frontend | |
| run: npm run build | |
| - name: Prune frontend dependencies | |
| working-directory: ./frontend | |
| run: npm prune --production | |
| - name: Save Node dependencies cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| **/node_modules | |
| key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | |
| ############################################################### | |
| ## Backend | |
| ############################################################### | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Cache Python dependencies | |
| id: cache-python | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip- | |
| - name: Install dependencies | |
| working-directory: ./backend | |
| run: | | |
| env | sort | |
| pip install --upgrade pip | |
| pip install uv | |
| uv sync --frozen --no-cache --no-dev | |
| - name: Save Python dependencies cache | |
| if: steps.cache-python.outputs.cache-hit != 'true' | |
| id: save-cache-python | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} | |
| ############################################################### | |
| ## Docker | |
| ############################################################### | |
| - name: Copy Docker README and LICENSE to backend | |
| run: | | |
| cp docker/README.md backend/README.md | |
| cp LICENSE backend/LICENSE | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ secrets.GHCR_USERNAME || github.actor }} | |
| password: ${{ secrets.GHCR_PAT }} | |
| - name: Build and push Docker image | |
| working-directory: ./backend | |
| run: | | |
| # Use the tag name for the Docker image version | |
| DOCKER_IMAGE=ghcr.io/ruska-ai/orchestra:${GITHUB_REF#refs/tags/} | |
| DOCKER_LATEST=ghcr.io/ruska-ai/orchestra:latest | |
| echo "Building images: $DOCKER_IMAGE and $DOCKER_LATEST" | |
| docker build --squash -t $DOCKER_IMAGE -t $DOCKER_LATEST . | |
| docker push $DOCKER_IMAGE | |
| docker push $DOCKER_LATEST | |
| deploy: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| environment: Dev | |
| steps: | |
| - name: Deploy to VM | |
| env: | |
| SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
| SSH_HOST: ${{ secrets.SSH_HOST }} | |
| SSH_USER: ${{ secrets.SSH_USER }} | |
| GHCR_USERNAME: ${{ secrets.GHCR_USERNAME || github.actor }} | |
| GHCR_TOKEN: ${{ secrets.GHCR_PAT }} | |
| GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/orchestra | |
| run: | | |
| # --------------------------------------------------- | |
| # Setup SSH | |
| # --------------------------------------------------- | |
| mkdir -p ~/.ssh | |
| echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa | |
| chmod 600 ~/.ssh/id_rsa | |
| ssh-keyscan github.com >> ~/.ssh/known_hosts | |
| ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts | |
| TAG=${{ github.ref_name }} | |
| # --------------------------------------------------- | |
| # SSH into the server and execute deployment | |
| # --------------------------------------------------- | |
| ssh -i ~/.ssh/id_rsa $SSH_USER@$SSH_HOST " | |
| set -e # Exit on error | |
| cd ~/agent_api | |
| echo '--- Logging into GHCR ---' | |
| echo \"$GHCR_TOKEN\" | docker login ghcr.io -u \"$GHCR_USERNAME\" --password-stdin | |
| echo '--- Pulling new image ---' | |
| docker pull $GHCR_IMAGE:$TAG | |
| echo '--- Starting staging container (graphchat_new) on port 8006 ---' | |
| docker stop graphchat_new 2>/dev/null || true | |
| docker rm graphchat_new 2>/dev/null || true | |
| docker run -d \ | |
| --name graphchat_new \ | |
| --network graphchat_default \ | |
| --env-file ./backend/.env \ | |
| -p 8006:8000 \ | |
| $GHCR_IMAGE:$TAG | |
| echo '--- Waiting a few seconds for startup ---' | |
| sleep 10 | |
| echo '--- Health check on staging container (graphchat_new) ---' | |
| for i in {1..3}; do | |
| if curl -f http://localhost:8006/api/info; then | |
| echo 'New container on port 8006 is healthy!' | |
| break | |
| else | |
| echo \"Attempt \$i failed. Waiting 10 seconds before retry...\" | |
| sleep 10 | |
| if [ \$i -eq 3 ]; then | |
| echo 'All retry attempts failed.' | |
| exit 1 | |
| fi | |
| fi | |
| done | |
| echo '--- Stopping and removing old container (graphchat) if it exists ---' | |
| docker stop graphchat || true | |
| docker rm graphchat || true | |
| echo '--- Stopping and removing staging container (graphchat_new) ---' | |
| docker stop graphchat_new 2>/dev/null || true | |
| docker rm graphchat_new 2>/dev/null || true | |
| echo '--- Running new container on production port (8005) as graphchat ---' | |
| docker run -d \ | |
| --name graphchat \ | |
| --network graphchat_default \ | |
| --restart always \ | |
| --env-file ./backend/.env \ | |
| -e APP_VERSION=$TAG \ | |
| -e VITE_APP_VERSION=$TAG \ | |
| -p 8005:8000 \ | |
| $GHCR_IMAGE:$TAG | |
| echo '--- Ensuring redis7 is on graphchat_default network ---' | |
| docker network connect graphchat_default redis7 2>/dev/null || echo 'redis7 already on network or not found' | |
| echo '--- Stopping and removing old worker container if it exists ---' | |
| docker stop graphchat_worker 2>/dev/null || true | |
| docker rm graphchat_worker 2>/dev/null || true | |
| echo '--- Starting worker container ---' | |
| docker run -d \ | |
| --name graphchat_worker \ | |
| --network graphchat_default \ | |
| --restart always \ | |
| --env-file ./backend/.env \ | |
| -e DISTRIBUTED_WORKERS=true \ | |
| -e REDIS_URL=redis://redis7:6379/0 \ | |
| -e DB_POOL_MIN_SIZE=1 \ | |
| -e DB_POOL_MAX_SIZE=5 \ | |
| --memory=1g \ | |
| --cpus=1 \ | |
| --entrypoint uv \ | |
| $GHCR_IMAGE:$TAG \ | |
| run taskiq worker src.workers.tasks:broker | |
| echo '--- Worker deployment complete ---' | |
| echo '--- Cleaning up old images ---' | |
| docker system prune -a --filter \"until=24h\" -f | |
| echo '--- Deployment successful! ---' | |
| " |