fix: scan #55
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
| # Deploy FastAPI (api/) to the VPS — venv + systemd + Nginx reload. | |
| # MariaDB may be remote (e.g. o2switch): DATABASE_URL in secrets. | |
| # On the VPS: install xvfb + xauth and run Uvicorn under xvfb-run (headed Chromium if VINTED_BROWSER_HEADLESS=false). | |
| # Optional secret VINTED_BROWSER_HEADLESS: missing → true (legacy); false → less likely flagged by Vinted on Xvfb. | |
| name: Deploy API (FastAPI) | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'api/**' | |
| - '.github/workflows/deploy-api.yml' | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - 'api/**' | |
| workflow_dispatch: | |
| concurrency: | |
| group: goupixdex-api-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint-pr: | |
| name: Sanity check (PR — sans déploiement) | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| sparse-checkout: | | |
| api | |
| sparse-checkout-cone-mode: true | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install deps & import check | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r api/requirements.txt | |
| cd api && PYTHONPATH=. python -c "import main; print('import ok')" | |
| deploy: | |
| name: Deploy API | |
| if: github.event_name == 'workflow_dispatch' || github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout (api only) | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| sparse-checkout: | | |
| api | |
| sparse-checkout-cone-mode: true | |
| - name: Write api/.env for upload | |
| env: | |
| DATABASE_URL: ${{ secrets.DATABASE_URL }} | |
| JWT_SECRET: ${{ secrets.JWT_SECRET }} | |
| SEED_USER_EMAIL: ${{ secrets.SEED_USER_EMAIL }} | |
| SEED_USER_PASSWORD: ${{ secrets.SEED_USER_PASSWORD }} | |
| SEED_MARGIN_PERCENT: ${{ secrets.SEED_MARGIN_PERCENT }} | |
| SEED_DEV_ARTICLES: ${{ secrets.SEED_DEV_ARTICLES }} | |
| SUPABASE_URL: ${{ secrets.SUPABASE_URL }} | |
| SUPABASE_API_KEY: ${{ secrets.SUPABASE_API_KEY }} | |
| SUPABASE_STORAGE_BUCKET: ${{ secrets.SUPABASE_STORAGE_BUCKET }} | |
| POKE_WALLET_API_KEY: ${{ secrets.POKE_WALLET_API_KEY }} | |
| POKE_WALLET_BASE_URL: ${{ secrets.POKE_WALLET_BASE_URL }} | |
| POKE_WALLET_PROXY_SECRET: ${{ secrets.POKE_WALLET_PROXY_SECRET }} | |
| GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} | |
| VINTED_EMAIL_OR_USERNAME: ${{ secrets.VINTED_EMAIL_OR_USERNAME }} | |
| VINTED_PASSWORD: ${{ secrets.VINTED_PASSWORD }} | |
| CORS_ORIGINS: ${{ secrets.CORS_ORIGINS }} | |
| VINTED_CHROME_EXECUTABLE: ${{ secrets.VINTED_CHROME_EXECUTABLE }} | |
| VINTED_BROWSER_HEADLESS: ${{ secrets.VINTED_BROWSER_HEADLESS }} | |
| EBAY_CLIENT_ID: ${{ secrets.EBAY_CLIENT_ID }} | |
| EBAY_CLIENT_SECRET: ${{ secrets.EBAY_CLIENT_SECRET }} | |
| EBAY_REDIRECT_URI: ${{ secrets.EBAY_REDIRECT_URI }} | |
| EBAY_USE_SANDBOX: ${{ secrets.EBAY_USE_SANDBOX }} | |
| EBAY_FR_FULFILLMENT_CARRIER_CODE: ${{ secrets.EBAY_FR_FULFILLMENT_CARRIER_CODE }} | |
| EBAY_FR_FULFILLMENT_SERVICE_CODE: ${{ secrets.EBAY_FR_FULFILLMENT_SERVICE_CODE }} | |
| run: | | |
| set -euo pipefail | |
| f=api/.env | |
| CHROME_PATH="${VINTED_CHROME_EXECUTABLE:-/usr/bin/chromium}" | |
| { | |
| echo "DATABASE_URL=${DATABASE_URL}" | |
| echo "JWT_SECRET=${JWT_SECRET}" | |
| echo "SEED_USER_EMAIL=${SEED_USER_EMAIL:-}" | |
| echo "SEED_USER_PASSWORD=${SEED_USER_PASSWORD:-}" | |
| echo "SEED_MARGIN_PERCENT=${SEED_MARGIN_PERCENT:-60}" | |
| echo "SEED_DEV_ARTICLES=${SEED_DEV_ARTICLES:-0}" | |
| echo "SUPABASE_URL=${SUPABASE_URL:-}" | |
| echo "SUPABASE_API_KEY=${SUPABASE_API_KEY:-}" | |
| echo "SUPABASE_STORAGE_BUCKET=${SUPABASE_STORAGE_BUCKET:-GoupixDex}" | |
| echo "POKE_WALLET_API_KEY=${POKE_WALLET_API_KEY:-}" | |
| echo "POKE_WALLET_BASE_URL=${POKE_WALLET_BASE_URL:-https://api.pokewallet.io}" | |
| echo "POKE_WALLET_PROXY_SECRET=${POKE_WALLET_PROXY_SECRET:-}" | |
| echo "GROQ_API_KEY=${GROQ_API_KEY:-}" | |
| echo "VINTED_EMAIL_OR_USERNAME=${VINTED_EMAIL_OR_USERNAME:-}" | |
| echo "VINTED_PASSWORD=${VINTED_PASSWORD:-}" | |
| echo "CORS_ORIGINS=${CORS_ORIGINS:-*}" | |
| echo "VINTED_BROWSER_HEADLESS=${VINTED_BROWSER_HEADLESS:-true}" | |
| echo "VINTED_CHROME_EXECUTABLE=${CHROME_PATH}" | |
| echo "EBAY_CLIENT_ID=${EBAY_CLIENT_ID:-}" | |
| echo "EBAY_CLIENT_SECRET=${EBAY_CLIENT_SECRET:-}" | |
| echo "EBAY_REDIRECT_URI=${EBAY_REDIRECT_URI:-}" | |
| echo "EBAY_USE_SANDBOX=${EBAY_USE_SANDBOX:-true}" | |
| echo "EBAY_FR_FULFILLMENT_CARRIER_CODE=${EBAY_FR_FULFILLMENT_CARRIER_CODE:-Colissimo}" | |
| echo "EBAY_FR_FULFILLMENT_SERVICE_CODE=${EBAY_FR_FULFILLMENT_SERVICE_CODE:-FR_ColiposteColissimo}" | |
| } > "$f" | |
| - name: Pack api tree | |
| run: tar -czf api-bundle.tar.gz api | |
| - name: Upload to VPS | |
| uses: appleboy/scp-action@v0.1.7 | |
| with: | |
| host: ${{ secrets.SSH_HOST }} | |
| port: ${{ secrets.SSH_PORT }} | |
| username: ${{ secrets.SSH_USERNAME }} | |
| key: ${{ secrets.SSH_PRIVATE_KEY }} | |
| source: api-bundle.tar.gz | |
| target: ${{ secrets.DEPLOY_API_DIR }} | |
| - name: Extract, venv, systemd, nginx | |
| uses: appleboy/ssh-action@v1.0.3 | |
| env: | |
| DEPLOY_API_DIR: ${{ secrets.DEPLOY_API_DIR }} | |
| API_SYSTEMD_SERVICE: ${{ secrets.API_SYSTEMD_SERVICE }} | |
| API_PORT: ${{ secrets.API_APP_PORT }} | |
| DEPLOY_USER: ${{ secrets.SSH_USERNAME }} | |
| with: | |
| host: ${{ secrets.SSH_HOST }} | |
| port: ${{ secrets.SSH_PORT }} | |
| username: ${{ secrets.SSH_USERNAME }} | |
| key: ${{ secrets.SSH_PRIVATE_KEY }} | |
| envs: DEPLOY_API_DIR,API_SYSTEMD_SERVICE,API_PORT,DEPLOY_USER | |
| script_stop: true | |
| script: | | |
| set -euo pipefail | |
| TARGET="${DEPLOY_API_DIR%/}" | |
| SVC="${API_SYSTEMD_SERVICE:-goupixdex-api.service}" | |
| PORT="${API_PORT:-8000}" | |
| mkdir -p "$TARGET" | |
| rm -rf "$TARGET/api" | |
| tar -xzf "$TARGET/api-bundle.tar.gz" -C "$TARGET" | |
| rm -f "$TARGET/api-bundle.tar.gz" | |
| APP_ROOT="$TARGET/api" | |
| sudo chown -R "$USER:$USER" "$APP_ROOT" || true | |
| sudo apt-get update -y | |
| sudo apt-get install -y python3-venv python3-pip chromium fonts-liberation xvfb xauth | |
| if [ ! -x "$APP_ROOT/.venv/bin/python" ]; then | |
| python3 -m venv "$APP_ROOT/.venv" | |
| fi | |
| . "$APP_ROOT/.venv/bin/activate" | |
| python -m pip install --upgrade pip wheel | |
| pip install -r "$APP_ROOT/requirements.txt" --no-cache-dir | |
| deactivate | |
| # SQL migrations (files not already recorded in schema_migrations) | |
| "$APP_ROOT/.venv/bin/python" "$APP_ROOT/migrations/run_migrations.py" | |
| # Bootstrap DB if empty tables (user / margins; articles only if SEED_DEV_ARTICLES=1 on the VPS) | |
| "$APP_ROOT/.venv/bin/python" "$APP_ROOT/seeders/conditional_seed.py" | |
| sudo tee "/etc/systemd/system/${SVC}" > /dev/null <<EOF | |
| [Unit] | |
| Description=GoupixDex FastAPI (Uvicorn) | |
| After=network.target | |
| [Service] | |
| Type=simple | |
| User=${DEPLOY_USER} | |
| WorkingDirectory=${APP_ROOT} | |
| EnvironmentFile=${APP_ROOT}/.env | |
| # Affichage virtuel : Chromium avec VINTED_BROWSER_HEADLESS=false a besoin d'un DISPLAY (xauth requis pour xvfb-run). | |
| ExecStart=/usr/bin/xvfb-run -a --server-args="-screen 0 1920x1080x24" ${APP_ROOT}/.venv/bin/uvicorn main:app --host 127.0.0.1 --port ${PORT} --workers 1 --proxy-headers | |
| Restart=always | |
| RestartSec=5 | |
| [Install] | |
| WantedBy=multi-user.target | |
| EOF | |
| sudo systemctl daemon-reload | |
| sudo systemctl enable "$SVC" | |
| sudo systemctl restart "$SVC" | |
| sudo systemctl --no-pager --full status "$SVC" || true | |
| sudo nginx -t | |
| sudo systemctl reload nginx |