name: Nix Build & Deploy — All Services on: push: branches: [main] paths: - 'apps/**' - 'infra/**' - 'flake.nix' - 'flake.lock' - '.github/workflows/nix-build.yml' workflow_dispatch: concurrency: group: nix-deploy cancel-in-progress: false permissions: contents: read env: VPS_HOST: ${{ secrets.VPS_HOST }} VPS_USER: ${{ secrets.VPS_USER }} jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: service: [hub, scraper, tools-gateway, tools-workers, tools-frontend, llm-api] outputs: store-paths: ${{ steps.store-paths.outputs.paths }} steps: - name: Checkout uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Install Nix uses: DeterminateSystems/nix-installer-action@v16 with: extra-conf: | sandbox = false accept-flake-config = true - name: Cache Nix uses: DeterminateSystems/magic-nix-cache-action@v8 - name: Build ${{ matrix.service }} id: build run: | nix build .#${{ matrix.service }} --impure --option sandbox false --print-build-logs STORE_PATH=$(readlink result) echo "store-path=$STORE_PATH" >> "$GITHUB_OUTPUT" echo "✅ ${{ matrix.service }}: $STORE_PATH" - name: Setup SSH key if: github.ref == 'refs/heads/main' env: SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} run: | mkdir -p ~/.ssh echo "$SSH_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 # Strip \r\n and validate key format sed -i 's/\r$//' ~/.ssh/id_ed25519 ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null 2>&1 || { echo "SSH key invalid"; exit 1; } ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts 2>/dev/null - name: Nix copy to VPS if: github.ref == 'refs/heads/main' run: | nix copy --to "ssh://$VPS_USER@$VPS_HOST" "${{ steps.build.outputs.store-path }}" deploy: needs: build if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - name: Setup SSH key env: SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} run: | mkdir -p ~/.ssh echo "$SSH_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 sed -i 's/\r$//' ~/.ssh/id_ed25519 ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null 2>&1 || { echo "SSH key invalid"; exit 1; } ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts 2>/dev/null - name: Deploy all services on VPS run: | ssh "$VPS_USER@$VPS_HOST" " set -e for service in hub scraper tools-gateway tools-workers tools-frontend llm-api; do PROFILE=\"/nix/var/nix/profiles/\$service\" LATEST=\$(ls -1td /nix/store/*-\$service-0.1.0 2>/dev/null | head -1) if [ -n \"\$LATEST\" ]; then echo \"=== Deploying \$service: \$LATEST ===\" sudo /nix/var/nix/profiles/default/bin/nix-env --profile \"\$PROFILE\" --set \"\$LATEST\" 2>&1 sudo systemctl restart \"\$service\" 2>&1 || echo \" ⚠️ restart failed (may not be enabled yet)\" echo \" ✅ \$service deployed\" else echo \" ⚠️ \$service: no store path found\" fi done " - name: Verify services run: | echo "=== Service Status ===" ssh "$VPS_USER@$VPS_HOST" " for service in hub scraper tools-gateway tools-workers tools-frontend llm-api; do state=\$(systemctl is-active \$service 2>/dev/null || echo 'not-found') echo \" \$service: \$state\" done "