Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 92 additions & 24 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#
# Flow:
# 1. Checkout code (with submodules)
# 2. Build & test Java artifacts when selected images need them
# 3. Build selected Docker images
# 2. Run the required application test workflow
# 3. Re-run all backend tests, then build selected Docker images
# 4. Push images to GHCR
# 5. SSH into server and run deployment script for selected services
#
Expand All @@ -17,8 +17,7 @@
# DEPLOY_HOST_FINGERPRINT — Server SSH host key (ssh-keyscan -H <ip>)
# ENV_INFERENCE_ORCHESTRATOR — Contents of inference-orchestrator/.env
# ENV_RAG_PIPELINE — Contents of rag-pipeline/.env
# ENV_WEB_FRONTEND — Contents of frontend/.env
# GHCR_PAT — GitHub Personal Access Token for GHCR (optional, uses GITHUB_TOKEN if not set)
# ENV_WEB_FRONTEND — Public VITE_* and SERVER_PORT frontend values
###############################################################################

name: Deploy to Production
Expand All @@ -31,28 +30,39 @@
required: false
default: "all"
skip_build:
description: "Skip build (deploy existing images on server)"
description: "Skip build and deploy images already built for this commit"
required: false
default: "false"
promote:
description: "Promote the tested images to production after building"
required: false
type: boolean
default: false

concurrency:
group: production-deploy
cancel-in-progress: false

env:
DEPLOY_PATH: /opt/codecrow
CODECROW_IMAGE_TAG: ${{ github.sha }}

permissions:
contents: read
packages: write
checks: write

jobs:
tests:
name: Required application tests
uses: ./.github/workflows/test.yml

build:
name: Build & Test → Docker Images
name: Verify and Build Docker Images
runs-on: ubuntu-latest
needs: [tests]
if: github.event.inputs.skip_build != 'true'
timeout-minutes: 30
timeout-minutes: 90

steps:
- name: Checkout code
Expand All @@ -68,6 +78,15 @@
java-version: 17
cache: maven

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
cache-dependency-path: |
python-ecosystem/inference-orchestrator/src/requirements.txt
python-ecosystem/rag-pipeline/requirements.txt

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
Expand All @@ -80,42 +99,49 @@
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and Push (ci-build.sh)
- name: Verify, Build and Push (ci-build.sh)
env:
ENV_INFERENCE_ORCHESTRATOR: ${{ secrets.ENV_INFERENCE_ORCHESTRATOR }}
ENV_RAG_PIPELINE: ${{ secrets.ENV_RAG_PIPELINE }}
ENV_WEB_FRONTEND: ${{ secrets.ENV_WEB_FRONTEND }}
PUBLIC_WEB_FRONTEND_ENV: ${{ vars.PUBLIC_WEB_FRONTEND_ENV }}
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
CODECROW_DEPLOY_SERVICES: ${{ github.event.inputs.services || 'all' }}
CODECROW_IMAGE_TAG: ${{ env.CODECROW_IMAGE_TAG }}
run: |
chmod +x deployment/ci/ci-build.sh
deployment/ci/ci-build.sh

- name: Publish test results
- name: Upload build test and coverage artifacts
if: always()
continue-on-error: true
uses: dorny/test-reporter@v1
with:
name: Java Tests
path: java-ecosystem/**/target/*-reports/TEST-*.xml
reporter: java-junit

- name: Upload CI logs and test reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: ci-logs-and-test-reports
name: build-test-and-coverage-reports-${{ env.CODECROW_IMAGE_TAG }}-${{ github.run_attempt }}
path: |
.ci-logs/
java-ecosystem/**/target/surefire-reports/
java-ecosystem/**/target/failsafe-reports/
retention-days: 7
java-ecosystem/**/target/site/jacoco/
python-ecosystem/inference-orchestrator/test-results/
python-ecosystem/rag-pipeline/test-results/
include-hidden-files: true
if-no-files-found: warn
retention-days: 14

- name: Upload immutable release image manifest
if: success()
uses: actions/upload-artifact@v4
with:
name: release-images-${{ env.CODECROW_IMAGE_TAG }}-${{ github.run_attempt }}
path: .ci-logs/release-images.txt
include-hidden-files: true
retention-days: 30

deploy:
name: Deploy to Server
runs-on: ubuntu-latest
needs: [build]
if: always() && (needs.build.result == 'success' || github.event.inputs.skip_build == 'true')
needs: [tests, build]
if: github.event.inputs.promote == 'true' && always() && needs.tests.result == 'success' && (needs.build.result == 'success' || github.event.inputs.skip_build == 'true')
timeout-minutes: 15
environment: production

Expand All @@ -132,6 +158,41 @@
# Generate with: ssh-keyscan -H <server-ip> 2>/dev/null
echo "${{ secrets.DEPLOY_HOST_FINGERPRINT }}" >> ~/.ssh/known_hosts

- name: Upload runtime service configuration
env:
DEPLOY_SERVICES: ${{ github.event.inputs.services || 'all' }}
ENV_INFERENCE_ORCHESTRATOR: ${{ secrets.ENV_INFERENCE_ORCHESTRATOR }}
ENV_RAG_PIPELINE: ${{ secrets.ENV_RAG_PIPELINE }}
run: |
source deployment/ci/service-selection.sh
codecrow_resolve_services "$DEPLOY_SERVICES"

if codecrow_includes_service "inference-orchestrator" "${CODECROW_RESOLVED_SERVICES[@]}"; then

Check warning on line 170 in .github/workflows/deploy.yml

View check run for this annotation

Codecrow AI / CodeCrow Analysis

Potential missing environment variable check

In the `Upload runtime service configuration` step, the script checks for `ENV_INFERENCE_ORCHESTRATOR` and `ENV_RAG_PIPELINE` if the service is `inference-orchestrator`. However, it does not check for `ENV_WEB_FRONTEND` even though that secret is used in the `build` job and the `deployment/build/production-build.sh` script expects a `.env` for the frontend. If the frontend is deployed without this check, it might fail silently or with a generic error later.
Raw output
Suggested fix:
Add a validation check for `ENV_WEB_FRONTEND` within the deployment configuration step if the frontend service is selected.
test -n "$ENV_INFERENCE_ORCHESTRATOR" || {
echo "Missing ENV_INFERENCE_ORCHESTRATOR secret" >&2
exit 1
}
test -n "$ENV_RAG_PIPELINE" || {
echo "Missing ENV_RAG_PIPELINE secret" >&2
exit 1
}

ssh -i ~/.ssh/deploy_key \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
"set -eu; install -d -m 700 '${{ env.DEPLOY_PATH }}/config/inference-orchestrator' '${{ env.DEPLOY_PATH }}/config/rag-pipeline'; rm -f '${{ env.DEPLOY_PATH }}/config/inference-orchestrator/.env.next' '${{ env.DEPLOY_PATH }}/config/rag-pipeline/.env.next'"
printf '%s' "$ENV_INFERENCE_ORCHESTRATOR" | \
ssh -i ~/.ssh/deploy_key \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
"set -eu; umask 077; cat > '${{ env.DEPLOY_PATH }}/config/inference-orchestrator/.env.next'; test -s '${{ env.DEPLOY_PATH }}/config/inference-orchestrator/.env.next'"
printf '%s' "$ENV_RAG_PIPELINE" | \
ssh -i ~/.ssh/deploy_key \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
"set -eu; umask 077; cat > '${{ env.DEPLOY_PATH }}/config/rag-pipeline/.env.next'; test -s '${{ env.DEPLOY_PATH }}/config/rag-pipeline/.env.next'"
ssh -i ~/.ssh/deploy_key \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
"set -eu; chmod 644 '${{ env.DEPLOY_PATH }}/config/inference-orchestrator/.env.next' '${{ env.DEPLOY_PATH }}/config/rag-pipeline/.env.next'; mv '${{ env.DEPLOY_PATH }}/config/inference-orchestrator/.env.next' '${{ env.DEPLOY_PATH }}/config/inference-orchestrator/.env'; mv '${{ env.DEPLOY_PATH }}/config/rag-pipeline/.env.next' '${{ env.DEPLOY_PATH }}/config/rag-pipeline/.env'"
fi

- name: Upload docker-compose.prod.yml and deploy script
run: |
scp -i ~/.ssh/deploy_key \
Expand All @@ -143,18 +204,25 @@
- name: Deploy on server
env:
DEPLOY_SERVICES: ${{ github.event.inputs.services || 'all' }}
IMAGE_TAG: ${{ env.CODECROW_IMAGE_TAG }}
run: |
DEPLOY_SERVICES_QUOTED=$(printf '%q' "$DEPLOY_SERVICES")
REPO_OWNER_QUOTED=$(printf '%q' "${{ github.repository_owner }}")
IMAGE_TAG_QUOTED=$(printf '%q' "$IMAGE_TAG")
ssh -i ~/.ssh/deploy_key \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
"chmod +x ${{ env.DEPLOY_PATH }}/server-deploy.sh && GITHUB_REPOSITORY_OWNER=$REPO_OWNER_QUOTED CODECROW_DEPLOY_SERVICES=$DEPLOY_SERVICES_QUOTED ${{ env.DEPLOY_PATH }}/server-deploy.sh"
"chmod +x ${{ env.DEPLOY_PATH }}/server-deploy.sh && GITHUB_REPOSITORY_OWNER=$REPO_OWNER_QUOTED CODECROW_DEPLOY_SERVICES=$DEPLOY_SERVICES_QUOTED CODECROW_IMAGE_TAG=$IMAGE_TAG_QUOTED ${{ env.DEPLOY_PATH }}/server-deploy.sh"

- name: Verify deployment
env:
IMAGE_TAG: ${{ env.CODECROW_IMAGE_TAG }}
REPO_OWNER: ${{ github.repository_owner }}
run: |
IMAGE_TAG_QUOTED=$(printf '%q' "$IMAGE_TAG")
REPO_OWNER_QUOTED=$(printf '%q' "$REPO_OWNER")
ssh -i ~/.ssh/deploy_key \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
"cd ${{ env.DEPLOY_PATH }} && docker compose -f docker-compose.prod.yml ps --format 'table {{.Name}}\t{{.Status}}'"
"cd ${{ env.DEPLOY_PATH }} && GITHUB_REPOSITORY_OWNER=$REPO_OWNER_QUOTED CODECROW_IMAGE_TAG=$IMAGE_TAG_QUOTED docker compose --env-file .env -f docker-compose.prod.yml ps --format 'table {{.Name}}\t{{.Status}}'"

- name: Cleanup SSH key
if: always()
Expand Down
Loading
Loading