name: Deploy Soul to GKE # Triggers on push to main — after the soul binary is built and published # by ci.yaml, this workflow builds the Docker image and blue-green deploys # to the neuron-prod namespace on GKE. # # This workflow runs AFTER ci.yaml has published the neuron-soul generic # artifact to Artifact Registry. The Docker build downloads that binary. on: push: branches: - main workflow_dispatch: inputs: slot: description: "Target blue-green slot (blue or green)" required: false default: "green" jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | apt-get update -qq apt-get install -y --no-install-recommends \ ca-certificates curl gnupg apt-transport-https kubectl echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \ > /etc/apt/sources.list.d/google-cloud-sdk.list curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \ | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg apt-get update -qq && apt-get install -y google-cloud-cli google-cloud-cli-gke-gcloud-auth-plugin - name: Authenticate to GCP env: GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }} run: | echo "${GCP_SA_KEY}" > /tmp/gcp-key.json gcloud auth activate-service-account --key-file=/tmp/gcp-key.json gcloud config set project neuron-785695 gcloud auth configure-docker us-central1-docker.pkg.dev --quiet - name: Get GKE credentials run: | gcloud container clusters get-credentials neuron-platform \ --region=us-central1 \ --project=neuron-785695 - name: Determine image tag and slot id: vars run: | SHA="${GITEA_SHA:0:8}" IMAGE="us-central1-docker.pkg.dev/neuron-785695/neuron-api/neuron-soul:${SHA}" echo "sha=${SHA}" >> "$GITEA_OUTPUT" echo "image=${IMAGE}" >> "$GITEA_OUTPUT" # Determine which slot is currently idle (0 replicas = idle slot) # If both are at 0 (fresh deploy), default to blue BLUE_REPLICAS=$(kubectl get deployment/neuron-mcp-blue \ -n neuron-prod \ -o jsonpath='{.spec.replicas}' 2>/dev/null || echo "0") GREEN_REPLICAS=$(kubectl get deployment/neuron-mcp-green \ -n neuron-prod \ -o jsonpath='{.spec.replicas}' 2>/dev/null || echo "0") echo " Blue replicas: ${BLUE_REPLICAS}" echo " Green replicas: ${GREEN_REPLICAS}" # Use workflow_dispatch override if provided, otherwise pick the idle slot if [ "${{ github.event.inputs.slot }}" != "" ]; then SLOT="${{ github.event.inputs.slot }}" elif [ "${GREEN_REPLICAS}" -eq 0 ] && [ "${BLUE_REPLICAS}" -gt 0 ]; then SLOT="green" elif [ "${BLUE_REPLICAS}" -eq 0 ] && [ "${GREEN_REPLICAS}" -gt 0 ]; then SLOT="blue" else # Fresh cluster — deploy to blue first SLOT="blue" fi echo "slot=${SLOT}" >> "$GITEA_OUTPUT" echo " Deploying to slot: ${SLOT}" - name: Clone engram source for Docker build context run: | # The Dockerfile builds engram from source (no published AR package). # Clone the engram repo into ./engram/ so it's available in the build context. git clone http://34.31.145.131/neuron-technologies/engram.git \ --depth=1 --branch=main \ engram echo "Engram source ready at ./engram/src/server.el" - name: Build and push Docker image env: GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }} run: | IMAGE="${{ steps.vars.outputs.image }}" SHA="${{ steps.vars.outputs.sha }}" echo "Building ${IMAGE}..." docker build \ --build-arg SOUL_VERSION="${SHA}" \ --secret id=gcp_sa_key,env=GCP_SA_KEY \ --tag "${IMAGE}" \ --tag "us-central1-docker.pkg.dev/neuron-785695/neuron-api/neuron-soul:latest" \ . echo "Pushing ${IMAGE}..." docker push "${IMAGE}" docker push "us-central1-docker.pkg.dev/neuron-785695/neuron-api/neuron-soul:latest" - name: Blue-green deploy to GKE run: | chmod +x scripts/blue-green-deploy.sh scripts/blue-green-deploy.sh \ --image "${{ steps.vars.outputs.image }}" \ --slot "${{ steps.vars.outputs.slot }}" - name: Verify deployment run: | SLOT="${{ steps.vars.outputs.slot }}" echo "Verifying neuron-mcp-${SLOT} is healthy..." kubectl rollout status deployment/"neuron-mcp-${SLOT}" \ --namespace=neuron-prod \ --timeout=3m echo "Active service endpoints:" kubectl get endpoints neuron-mcp -n neuron-prod echo "Pod status:" kubectl get pods -n neuron-prod -l app=neuron-mcp - name: Cleanup if: always() run: rm -f /tmp/gcp-key.json