Deploy #23
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
| name: Deploy | |
| # Triggers: | |
| # - Automatically after CI workflow completes successfully on main | |
| # - Manually via workflow_dispatch for hotfixes or rollbacks | |
| on: | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: [completed] | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: "Target environment" | |
| required: true | |
| default: "production" | |
| type: choice | |
| options: [production, staging] | |
| run_migrations: | |
| description: "Run database migrations" | |
| required: true | |
| default: true | |
| type: boolean | |
| run_scout_import: | |
| description: "Re-index Typesense (slow — only if schema changed)" | |
| required: true | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: deploy-${{ github.ref }} | |
| cancel-in-progress: false # never cancel an in-flight deploy | |
| jobs: | |
| deploy: | |
| name: Deploy to ${{ github.event.inputs.environment || 'production' }} | |
| runs-on: ubuntu-latest | |
| # Only deploy if CI succeeded (or triggered manually) | |
| if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} | |
| environment: ${{ github.event.inputs.environment || 'production' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| # ── Build frontend assets on CI runner (npm not available on server) ── | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Build frontend assets | |
| run: npm run build | |
| # ── SSH deploy ────────────────────────────────────────── | |
| # Required GitHub secrets: | |
| # DEPLOY_HOST — server hostname or IP (e.g. aljebal-albeedos.com) | |
| # DEPLOY_PORT — SSH port (e.g. 21098) | |
| # DEPLOY_USER — SSH username (e.g. aljedkrq) | |
| # DEPLOY_KEY — private SSH key (RSA or Ed25519) | |
| # DEPLOY_PATH — absolute path to the app on the server | |
| # DEPLOY_PHP — path to PHP binary on server (e.g. /home/aljedkrq/php84) | |
| # ──────────────────────────────────────────────────────── | |
| - name: Setup SSH agent | |
| uses: webfactory/ssh-agent@v0.9.0 | |
| with: | |
| ssh-private-key: ${{ secrets.DEPLOY_KEY }} | |
| - name: Add server to known hosts | |
| run: ssh-keyscan -p ${{ secrets.DEPLOY_PORT }} -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts | |
| - name: Upload built assets | |
| env: | |
| DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} | |
| DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }} | |
| DEPLOY_USER: ${{ secrets.DEPLOY_USER }} | |
| DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} | |
| run: | | |
| scp -P $DEPLOY_PORT -r public/build $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/public/ | |
| - name: Deploy | |
| env: | |
| DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} | |
| DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }} | |
| DEPLOY_USER: ${{ secrets.DEPLOY_USER }} | |
| DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} | |
| DEPLOY_PHP: ${{ secrets.DEPLOY_PHP }} | |
| RUN_MIGRATIONS: ${{ github.event.inputs.run_migrations || 'true' }} | |
| RUN_SCOUT_IMPORT: ${{ github.event.inputs.run_scout_import || 'false' }} | |
| run: | | |
| ssh -p $DEPLOY_PORT $DEPLOY_USER@$DEPLOY_HOST << EOF | |
| set -e | |
| PHP="${DEPLOY_PHP:-php}" | |
| cd $DEPLOY_PATH | |
| echo "==> Pulling latest code" | |
| git pull origin main | |
| echo "==> Installing PHP dependencies" | |
| COMPOSER=\$(which composer 2>/dev/null || echo "\$HOME/bin/composer") | |
| \$PHP \$COMPOSER install --no-dev --no-interaction --optimize-autoloader --prefer-dist | |
| echo "==> Caching configuration" | |
| \$PHP artisan config:cache | |
| \$PHP artisan route:cache | |
| \$PHP artisan view:cache | |
| \$PHP artisan event:cache | |
| if [ "\$RUN_MIGRATIONS" = "true" ]; then | |
| echo "==> Running database migrations" | |
| \$PHP artisan migrate --force | |
| fi | |
| echo "==> Restarting queue workers" | |
| \$PHP artisan queue:restart | |
| if [ "\$RUN_SCOUT_IMPORT" = "true" ]; then | |
| echo "==> Re-indexing Typesense" | |
| \$PHP artisan scout:flush "App\Domain\Product\Models\Product" | |
| \$PHP artisan scout:import "App\Domain\Product\Models\Product" | |
| \$PHP artisan scout:flush "App\Domain\Category\Models\Category" | |
| \$PHP artisan scout:import "App\Domain\Category\Models\Category" | |
| \$PHP artisan scout:flush "App\Domain\Order\Models\Order" | |
| \$PHP artisan scout:import "App\Domain\Order\Models\Order" | |
| fi | |
| echo "==> Done" | |
| EOF | |
| - name: Notify on failure | |
| if: failure() | |
| run: | | |
| echo "::error::Deployment failed. Check the logs above." | |
| # Add Slack/email notification here if needed |