name: Nexus CI/CD on: push: branches: [main, develop] pull_request: branches: [main] env: PYTHON_VERSION: "3.12" jobs: # ── CI: Test ── test: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install dependencies run: | pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-asyncio aiosqlite coverage ruff mypy - name: Lint (ruff) run: ruff check server/ tests/ - name: Type check (mypy) run: mypy server/ --ignore-missing-imports continue-on-error: true # Type errors don't block deployment yet - name: Run fast tests with coverage run: | coverage run -m pytest tests/ -m "not chain and not slow" -q --tb=short coverage report --fail-under=50 coverage xml - name: Run chain tests run: pytest tests/chain -m chain -q --tb=short continue-on-error: true - name: Upload coverage report uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage.xml # ── CI: E2E (main only, optional secrets) ── e2e: runs-on: ubuntu-latest needs: test if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" - name: Install frontend dependencies working-directory: frontend run: npm ci - name: Install Playwright Chromium working-directory: frontend run: npx playwright install chromium - name: Run Playwright E2E working-directory: frontend env: NEXUS_E2E_BASE: ${{ secrets.NEXUS_E2E_BASE || 'http://127.0.0.1:8600' }} NEXUS_E2E_USER: ${{ secrets.NEXUS_E2E_USER || 'admin' }} NEXUS_E2E_PASSWORD: ${{ secrets.NEXUS_E2E_PASSWORD }} run: | if [ -z "$NEXUS_E2E_PASSWORD" ]; then echo "NEXUS_E2E_PASSWORD not set — skip E2E" exit 0 fi npm run test:e2e continue-on-error: true # ── CD: Deploy ── deploy: needs: test # Only deploy if tests pass runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' # Only deploy from main branch steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to production server uses: appleboy/ssh-action@v1 with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} script: | cd ${{ secrets.DEPLOY_PATH || '/opt/nexus' }} git pull origin main pip install -r requirements.txt # Restart Python backend (Supervisor/systemd) supervisorctl restart nexus # PHP changes are picked up automatically (no restart needed) echo "Nexus deployed successfully at $(date)"