name: Deploy to Server on: push: branches: [ main ] workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Configure npm mirror run: | echo "registry=https://registry.npmmirror.com" > ~/.npmrc npm config set registry https://registry.npmmirror.com - name: Build client (native) working-directory: ./client run: | npm install --no-audit --no-fund npm run build - name: Build server (native) working-directory: ./server run: | npm install --no-audit --no-fund npm run build - name: Install server production deps working-directory: ./server run: | rm -rf node_modules npm install --omit=dev --no-audit --no-fund - name: Prepare deploy package run: | mkdir -p deploy-pkg/app cp -r client/dist deploy-pkg/app/client/dist cp -r server/dist deploy-pkg/app/server/dist cp -r server/node_modules deploy-pkg/app/server/node_modules mkdir -p deploy-pkg/app/server/src/db cp server/src/db/schema.sql deploy-pkg/app/server/src/db/schema.sql cat > deploy-pkg/docker-compose.yml <<'EOF' services: volunteer: image: node:20-alpine container_name: volunteer-manage restart: unless-stopped working_dir: /app/server command: ["node", "dist/index.js"] ports: - "3000:3000" environment: - NODE_ENV=production - PORT=3000 - JWT_SECRET=${JWT_SECRET:-volunteer-manage-secret-key-2026} volumes: - /opt/volunteer/data:/app/server/data - /opt/volunteer/app:/app:ro EOF - name: Install SSH key uses: shimataro/ssh-key-action@v2 with: key: ${{ secrets.SSH_PRIVATE_KEY }} known_hosts: unnecessary if_key_exists: replace - name: Deploy via SSH env: SSH_HOST: ${{ secrets.SSH_HOST }} SSH_PORT: ${{ secrets.SSH_PORT || 22 }} SSH_USER: ${{ secrets.SSH_USER }} run: | PORT="${SSH_PORT:-22}" ssh -o StrictHostKeyChecking=no -p $PORT $SSH_USER@$SSH_HOST "mkdir -p /opt/volunteer/app /opt/volunteer/data" scp -o StrictHostKeyChecking=no -P $PORT -r deploy-pkg/app/* $SSH_USER@$SSH_HOST:/opt/volunteer/app/ scp -o StrictHostKeyChecking=no -P $PORT deploy-pkg/docker-compose.yml $SSH_USER@$SSH_HOST:/opt/volunteer/docker-compose.yml ssh -o StrictHostKeyChecking=no -p $PORT $SSH_USER@$SSH_HOST "cd /opt/volunteer && docker compose down || true && docker compose up -d --force-recreate && sleep 3 && docker compose ps" - name: Health check env: SSH_HOST: ${{ secrets.SSH_HOST }} SSH_PORT: ${{ secrets.SSH_PORT || 22 }} SSH_USER: ${{ secrets.SSH_USER }} run: | PORT="${SSH_PORT:-22}" ssh -o StrictHostKeyChecking=no -p $PORT $SSH_USER@$SSH_HOST "for i in 1 2 3 4 5; do if curl -fsS http://localhost:3000/u > /dev/null; then echo 'Service OK'; exit 0; fi; echo 'Wait...'; sleep 3; done; echo 'Service FAIL'; exit 1"