From b6db2c5bd6b54e2a2715f73e42c5a4fff66f70ef Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Tue, 20 Jan 2026 17:41:10 +0100 Subject: [PATCH 1/7] feat: Scaleway deployment setup with CLI script, Caddy proxy and GitHub workflow --- .env.example | 6 +++ .github/workflows/build_and_deploy.yml | 49 +++++++++++++++------- Caddyfile | 8 ++++ Dockerfile | 11 +++++ compose.yml | 3 +- justfile | 4 ++ scripts/deploy_to_scaleway.sh | 56 ++++++++++++++++++++++++++ supervisord.conf | 10 +++++ 8 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 Caddyfile create mode 100755 scripts/deploy_to_scaleway.sh diff --git a/.env.example b/.env.example index 1fa559a9..6b9e55ee 100644 --- a/.env.example +++ b/.env.example @@ -53,6 +53,12 @@ XAI_API_KEY=your-xai-api-key-here # Cortex API Key CORTEX_API_KEY=your-cortex-api-key-here +# Scaleway Credentials (for deployment) +SCW_ACCESS_KEY=your-access-key +SCW_SECRET_KEY=your-secret-key +SCW_DEFAULT_ORGANIZATION_ID=your-org-id +SCW_DEFAULT_PROJECT_ID=your-project-id + # ============================================================================= # EvalAP API Configuration # ============================================================================= diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 7bb01ab4..e1df06f1 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -65,20 +65,41 @@ jobs: runs-on: ubuntu-latest needs: build-and-push steps: - - name: Trigger dev deployment + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Scaleway CLI + uses: scaleway/action-scw@v0 + with: + save-config: true + export-config: true + env: + SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }} + SCW_SECRET_KEY: ${{ secrets.SCW_SECRET_KEY }} + SCW_DEFAULT_ORGANIZATION_ID: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }} + SCW_DEFAULT_PROJECT_ID: ${{ secrets.SCW_DEFAULT_PROJECT_ID }} + SCW_DEFAULT_REGION: fr-par + SCW_DEFAULT_ZONE: fr-par-1 + + - name: Deploy to Scaleway Container run: | - RESPONSE="$(curl --request POST \ - --form token=${{ secrets.GITLAB_CI_TOKEN }} \ - --form ref=main \ - --form 'variables[pipeline_name]=${{ github.event.repository.name }} - ${{ needs.build-and-push.outputs.commit_title }}' \ - --form 'variables[docker_image_tag]=${{ env.IMAGE_TAG }}' \ - --form 'variables[application_to_deploy]=evalap' \ - --form 'variables[deployment_environment]=dev' \ - 'https://gitlab.com/api/v4/projects/58117805/trigger/pipeline')" - - if echo "$RESPONSE" | grep -q '"status":"created"'; then - echo $RESPONSE - else - echo $RESPONSE + CONTAINER_NAME="evalap" + FULL_IMAGE_NAME="${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}" + + echo "Deploying $FULL_IMAGE_NAME to container $CONTAINER_NAME..." + + # Find container ID + CONTAINER_ID=$(scw container container list region=fr-par name=$CONTAINER_NAME -o json | jq -r '.[0].id') + + if [ "$CONTAINER_ID" == "null" ] || [ -z "$CONTAINER_ID" ]; then + echo "Error: Container $CONTAINER_NAME not found." exit 1 fi + + echo "Found container ID: $CONTAINER_ID" + + # Update container image and port + scw container container update region=fr-par $CONTAINER_ID registry-image=$FULL_IMAGE_NAME port=8080 + + # Deploy + scw container container deploy region=fr-par $CONTAINER_ID diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 00000000..06a2be05 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,8 @@ +:8080 { + handle_path /api/* { + reverse_proxy localhost:8000 + } + handle { + reverse_proxy localhost:3000 + } +} diff --git a/Dockerfile b/Dockerfile index 381a921d..2ff6eb6f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,14 @@ ENV UV_COMPILE_BYTECODE=1 \ UV_LINK_MODE=copy \ UV_PYTHON_CACHE_DIR=/root/.cache/uv/python +# Install Caddy +RUN apt-get update && apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl \ + && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg \ + && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list \ + && apt-get update \ + && apt-get install -y caddy \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + # Install Python 3.12 using uv (fast!) RUN --mount=type=cache,target=/root/.cache/uv \ uv python install 3.12 @@ -47,6 +55,7 @@ COPY ./docs /app/docs COPY ./evalap /app/evalap COPY ./scripts /app/scripts COPY supervisord.conf /app/supervisord.conf +COPY Caddyfile /app/Caddyfile # Copy built Docusaurus site COPY --from=docs-builder /app/docs/build /app/docs/build @@ -63,3 +72,5 @@ RUN --mount=type=cache,target=/root/.cache/uv \ ENV PATH="/app/.venv/bin:$PATH" WORKDIR /app + +EXPOSE 8080 diff --git a/compose.yml b/compose.yml index 80ace1e8..dedc2be4 100644 --- a/compose.yml +++ b/compose.yml @@ -35,8 +35,7 @@ services: - MISTRAL_API_KEY=${MISTRAL_API_KEY} - ALBERT_API_KEY=${ALBERT_API_KEY} ports: - - "8000:8000" - - "3000:3000" + - "80:8080" volumes: - evalap_data:/data - huggingface_cache:/root/.cache/huggingface diff --git a/justfile b/justfile index 9d6a1554..c75d2e3f 100644 --- a/justfile +++ b/justfile @@ -202,3 +202,7 @@ sync: # Test a PR: list open PRs, select one, checkout its branch, migrate, and run pray: scripts/pray.sh + +# Build production image and deploy to Scaleway (requires scw and docker) +deploy-scaleway: + scripts/deploy_to_scaleway.sh diff --git a/scripts/deploy_to_scaleway.sh b/scripts/deploy_to_scaleway.sh new file mode 100755 index 00000000..46a0a41c --- /dev/null +++ b/scripts/deploy_to_scaleway.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -e + +# Configuration +CONTAINER_NAME="evalap" +REGION="fr-par" +# Try to get repository name from git, otherwise default +REPO_NAME=$(git config --get remote.origin.url | sed -E 's/.*github.com[:\/](.*)\.git/\1/' | tr '[:upper:]' '[:lower:]') +IMAGE_NAME="ghcr.io/${REPO_NAME}/evalap" +IMAGE_TAG=$(git rev-parse --short HEAD) + +# Check dependencies +if ! command -v scw &> /dev/null; then + echo "Error: scw CLI is not installed." + exit 1 +fi + +if ! command -v docker &> /dev/null; then + echo "Error: docker is not installed." + exit 1 +fi + +echo "🚀 Starting deployment of $CONTAINER_NAME to Scaleway ($REGION)" + +# 1. Build the production image +echo "📦 Building production Docker image..." +docker build --platform linux/amd64 -t "${IMAGE_NAME}:${IMAGE_TAG}" . + +# 2. Push to Registry +echo "⬆️ Pushing image to $IMAGE_NAME:$IMAGE_TAG..." +docker push "${IMAGE_NAME}:${IMAGE_TAG}" + +# 3. Find Scaleway Container ID +echo "🔍 Searching for container named '$CONTAINER_NAME'..." +CONTAINER_ID=$(scw container container list region=$REGION name=$CONTAINER_NAME -o json | jq -r '.[0].id') + +if [ "$CONTAINER_ID" == "null" ] || [ -z "$CONTAINER_ID" ]; then + echo "❌ Error: Container '$CONTAINER_NAME' not found in region $REGION." + echo "Please create the container first in the Scaleway Console or via Terraform." + exit 1 +fi + +echo "✅ Found container ID: $CONTAINER_ID" + +# 4. Update and Deploy +echo "🔄 Updating container with new image..." +scw container container update "$CONTAINER_ID" \ + region=$REGION \ + registry-image="${IMAGE_NAME}:${IMAGE_TAG}" \ + port=8080 + +echo "🚀 Triggering deployment..." +scw container container deploy "$CONTAINER_ID" region=$REGION + +echo "✨ Deployment triggered successfully!" +echo "Check the status at: https://console.scaleway.com/containers/containers/$REGION/$CONTAINER_ID" diff --git a/supervisord.conf b/supervisord.conf index 14cb17eb..6001467e 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -34,3 +34,13 @@ stderr_logfile=/dev/stderr stdout_logfile_maxbytes = 0 stderr_logfile_maxbytes = 0 priority=999 +[program:caddy] +command=caddy run --config /app/Caddyfile --adapter caddyfile +directory=/app +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stderr_logfile=/dev/stderr +stdout_logfile_maxbytes = 0 +stderr_logfile_maxbytes = 0 +priority=1 From 78bf62d005581299d63475a8fb97c1b7423bb2d7 Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Tue, 20 Jan 2026 18:31:56 +0100 Subject: [PATCH 2/7] feat: switch to scaleway container registry for deployment --- .github/workflows/build_and_deploy.yml | 10 +++++----- scripts/deploy_to_scaleway.sh | 26 ++++++++++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index e1df06f1..27e3b72a 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: env: - IMAGE_NAME: ghcr.io/${{ github.repository }}/evalap + IMAGE_NAME: rg.fr-par.scw.cloud/evalap/evalap IMAGE_TAG: ${{ github.sha }} jobs: @@ -33,12 +33,12 @@ jobs: - id: get_head_commit_title run: echo "title=$(git log --format=%B -n 1 HEAD | head -n 1)" >> $GITHUB_OUTPUT - - name: Log in to GitHub Container Registry + - name: Log in to Scaleway Container Registry uses: docker/login-action@v3 with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + registry: rg.fr-par.scw.cloud + username: nologin + password: ${{ secrets.SCW_SECRET_KEY }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/scripts/deploy_to_scaleway.sh b/scripts/deploy_to_scaleway.sh index 46a0a41c..a4bc6069 100755 --- a/scripts/deploy_to_scaleway.sh +++ b/scripts/deploy_to_scaleway.sh @@ -4,9 +4,19 @@ set -e # Configuration CONTAINER_NAME="evalap" REGION="fr-par" -# Try to get repository name from git, otherwise default -REPO_NAME=$(git config --get remote.origin.url | sed -E 's/.*github.com[:\/](.*)\.git/\1/' | tr '[:upper:]' '[:lower:]') -IMAGE_NAME="ghcr.io/${REPO_NAME}/evalap" + +# Get Scaleway Registry endpoint +echo "🔍 Fetching Scaleway Registry endpoint for namespace 'evalap'..." +REGISTRY_ENDPOINT=$(scw registry namespace list region=$REGION -o json | jq -r '.[] | select(.name == "evalap") | .endpoint') + +if [ -z "$REGISTRY_ENDPOINT" ]; then + echo "❌ Error: Scaleway Registry namespace 'evalap' not found in region $REGION." + echo "Current namespaces:" + scw registry namespace list region=$REGION + exit 1 +fi + +IMAGE_NAME="${REGISTRY_ENDPOINT}/${CONTAINER_NAME}" IMAGE_TAG=$(git rev-parse --short HEAD) # Check dependencies @@ -26,11 +36,15 @@ echo "🚀 Starting deployment of $CONTAINER_NAME to Scaleway ($REGION)" echo "📦 Building production Docker image..." docker build --platform linux/amd64 -t "${IMAGE_NAME}:${IMAGE_TAG}" . -# 2. Push to Registry +# 2. Login to Scaleway Registry +echo "🔐 Logging in to Scaleway Registry..." +scw registry login + +# 3. Push to Registry echo "⬆️ Pushing image to $IMAGE_NAME:$IMAGE_TAG..." docker push "${IMAGE_NAME}:${IMAGE_TAG}" -# 3. Find Scaleway Container ID +# 4. Find Scaleway Container ID echo "🔍 Searching for container named '$CONTAINER_NAME'..." CONTAINER_ID=$(scw container container list region=$REGION name=$CONTAINER_NAME -o json | jq -r '.[0].id') @@ -42,7 +56,7 @@ fi echo "✅ Found container ID: $CONTAINER_ID" -# 4. Update and Deploy +# 5. Update and Deploy echo "🔄 Updating container with new image..." scw container container update "$CONTAINER_ID" \ region=$REGION \ From 73237a0585240db44e1229c2f8883a213b324116 Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Fri, 23 Jan 2026 14:28:37 +0100 Subject: [PATCH 3/7] chore: add dotenv-linter to pre-commit and CI --- .github/workflows/pr_checks.yml | 5 +++++ .pre-commit-config.yaml | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml index 0f05ec34..2e7002f3 100644 --- a/.github/workflows/pr_checks.yml +++ b/.github/workflows/pr_checks.yml @@ -60,6 +60,11 @@ jobs: RUFF_OUTPUT_FORMAT: github run: uv run ruff check --config=pyproject.toml --select=F,Q,I + - name: Lint .env files + uses: dotenv-linter/action-dotenv-linter@v2 + with: + dotenv_linter_flags: --skip UnorderedKey + # - name: Check formatting # run: uv run ruff format --check --diff --config=pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 76c0bf4d..51df92f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,3 +43,9 @@ repos: # Remove trailing whitespace - id: trailing-whitespace exclude: '^(\.claude/|\.cursor/|\.specify/|\.windsurf/)' + + - repo: https://github.com/dotenv-linter/dotenv-linter + rev: v4.0.0 + hooks: + - id: dotenv-linter + args: [--skip, UnorderedKey] From ec0ed5778cb2beb283c3af469d0a46f2f7c4d533 Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Fri, 23 Jan 2026 14:35:14 +0100 Subject: [PATCH 4/7] chore: fix order of other env vars and keep SCW suppressed --- .env.example | 5 +++-- .github/workflows/pr_checks.yml | 2 -- .pre-commit-config.yaml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 6b9e55ee..ee01d1a4 100644 --- a/.env.example +++ b/.env.example @@ -40,12 +40,12 @@ MISTRAL_API_KEY=your-mistral-api-key-here # Albert API Keys (different environments) ALBERT_API_KEY=your-albert-api-key-here -ALBERT_API_KEY_STAGING=your-albert-staging-key-here ALBERT_API_KEY_DEV=your-albert-dev-key-here +ALBERT_API_KEY_STAGING=your-albert-staging-key-here # France Services API Key -MFS_API_KEY_V2=your-mfs-api-key-here MFS_API_KEY=your-mfs-api-key-here +MFS_API_KEY_V2=your-mfs-api-key-here # XAI API Key (Grok) XAI_API_KEY=your-xai-api-key-here @@ -54,6 +54,7 @@ XAI_API_KEY=your-xai-api-key-here CORTEX_API_KEY=your-cortex-api-key-here # Scaleway Credentials (for deployment) +# dotenv-linter:off UnorderedKey SCW_ACCESS_KEY=your-access-key SCW_SECRET_KEY=your-secret-key SCW_DEFAULT_ORGANIZATION_ID=your-org-id diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml index 2e7002f3..a2eebee9 100644 --- a/.github/workflows/pr_checks.yml +++ b/.github/workflows/pr_checks.yml @@ -62,8 +62,6 @@ jobs: - name: Lint .env files uses: dotenv-linter/action-dotenv-linter@v2 - with: - dotenv_linter_flags: --skip UnorderedKey # - name: Check formatting # run: uv run ruff format --check --diff --config=pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 51df92f2..2becc6e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,4 +48,4 @@ repos: rev: v4.0.0 hooks: - id: dotenv-linter - args: [--skip, UnorderedKey] + args: [check] From 41c7f61491edc54379d27999de721f8f62f73dc3 Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Fri, 23 Jan 2026 14:47:12 +0100 Subject: [PATCH 5/7] Quote variables in scw commands to avoid word-splitting/globbing --- .github/workflows/build_and_deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 27e3b72a..565ddd16 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -89,7 +89,7 @@ jobs: echo "Deploying $FULL_IMAGE_NAME to container $CONTAINER_NAME..." # Find container ID - CONTAINER_ID=$(scw container container list region=fr-par name=$CONTAINER_NAME -o json | jq -r '.[0].id') + CONTAINER_ID=$(scw container container list region=fr-par name="$CONTAINER_NAME" -o json | jq -r '.[0].id') if [ "$CONTAINER_ID" == "null" ] || [ -z "$CONTAINER_ID" ]; then echo "Error: Container $CONTAINER_NAME not found." @@ -99,7 +99,7 @@ jobs: echo "Found container ID: $CONTAINER_ID" # Update container image and port - scw container container update region=fr-par $CONTAINER_ID registry-image=$FULL_IMAGE_NAME port=8080 + scw container container update region=fr-par "$CONTAINER_ID" registry-image="$FULL_IMAGE_NAME" port=8080 # Deploy - scw container container deploy region=fr-par $CONTAINER_ID + scw container container deploy region=fr-par "$CONTAINER_ID" From 109e8065fdc0e67196085e185ea501fb8ed1a64c Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Fri, 23 Jan 2026 14:48:40 +0100 Subject: [PATCH 6/7] Install gnupg to fix gpg --dearmor failure --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2ff6eb6f..0ef6e5bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ ENV UV_COMPILE_BYTECODE=1 \ UV_PYTHON_CACHE_DIR=/root/.cache/uv/python # Install Caddy -RUN apt-get update && apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl \ +RUN apt-get update && apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg \ && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg \ && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list \ && apt-get update \ From e9bac4edb58a04b544123bb8f267d8070eb2b9bd Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Fri, 23 Jan 2026 14:50:30 +0100 Subject: [PATCH 7/7] Add a jq dependency check --- scripts/deploy_to_scaleway.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/deploy_to_scaleway.sh b/scripts/deploy_to_scaleway.sh index a4bc6069..eef522c4 100755 --- a/scripts/deploy_to_scaleway.sh +++ b/scripts/deploy_to_scaleway.sh @@ -30,6 +30,11 @@ if ! command -v docker &> /dev/null; then exit 1 fi +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed." + exit 1 +fi + echo "🚀 Starting deployment of $CONTAINER_NAME to Scaleway ($REGION)" # 1. Build the production image