Deploy to Production #15
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 to Production | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| force_full: | |
| description: 'Force full deploy (rebuild and upload vendor + assets)' | |
| type: boolean | |
| default: false | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| name: Deploy via SFTP | |
| env: | |
| # Required at build time only: composer install runs package:discover, | |
| # which boots the visitor-tracker package and trips its dashboard guard | |
| # if no auth method is configured. The runner has no .env, so set this | |
| # here to match prod posture (server's .env handles the actual setting). | |
| VISITOR_TRACKER_ALLOW_UNPROTECTED: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine what changed since last release | |
| id: changes | |
| run: | | |
| set -euo pipefail | |
| if [[ "${{ github.event.inputs.force_full }}" == "true" ]]; then | |
| echo "Force full deploy requested via workflow_dispatch." | |
| echo "composer_changed=true" >> "$GITHUB_OUTPUT" | |
| echo "assets_changed=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Baseline = previous release tag (skip current HEAD if it IS a tag). | |
| # Falls back to HEAD^ if there's no prior tag in history. | |
| BASE=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || git rev-parse HEAD^) | |
| echo "Diff baseline: $BASE" | |
| CHANGED=$(git diff --name-only "$BASE" HEAD) | |
| echo "Changed files since $BASE:" | |
| echo "$CHANGED" | |
| # Vendor needs rebuild + upload only when composer dependencies change. | |
| if echo "$CHANGED" | grep -qE '^composer\.(json|lock)$'; then | |
| echo "composer_changed=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "composer_changed=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Compiled assets need rebuild + upload when their source or build | |
| # config changes. public/build/ filenames are content-hashed by Vite, | |
| # so leaving stale ones on the server is harmless. | |
| if echo "$CHANGED" | grep -qE '^(package(-lock)?\.json|vite\.config\.js|resources/(css|js)/)'; then | |
| echo "assets_changed=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "assets_changed=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Setup PHP | |
| if: steps.changes.outputs.composer_changed == 'true' | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: 8.3 | |
| extensions: mbstring, xml, ctype, json | |
| - name: Install production dependencies | |
| if: steps.changes.outputs.composer_changed == 'true' | |
| run: composer install --no-dev --optimize-autoloader --no-interaction | |
| - name: Setup Node.js | |
| if: steps.changes.outputs.assets_changed == 'true' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install npm dependencies | |
| if: steps.changes.outputs.assets_changed == 'true' | |
| run: npm ci | |
| - name: Build assets | |
| if: steps.changes.outputs.assets_changed == 'true' | |
| run: npm run build | |
| - name: Prepare production files | |
| run: | | |
| # Remove dev/test files | |
| rm -rf tests | |
| rm -rf node_modules | |
| rm -f phpunit.xml | |
| rm -f .editorconfig | |
| rm -f vite.config.js | |
| rm -f package.json | |
| rm -f package-lock.json | |
| rm -f .phpunit.result.cache | |
| # Remove git files | |
| rm -rf .git | |
| rm -rf .github | |
| rm -f .gitignore | |
| rm -f .gitattributes | |
| # Remove documentation | |
| rm -f README.md | |
| rm -f CHANGELOG.md | |
| # Remove composer files (vendor already installed) | |
| rm -f composer.json | |
| rm -f composer.lock | |
| # Remove source assets (compiled assets are in public/build) | |
| rm -rf resources/css | |
| rm -rf resources/js | |
| # Remove other unnecessary files | |
| rm -f .env.example | |
| rm -f deploy.sh | |
| # Never ship a local sqlite — would overwrite production data | |
| rm -f database/*.sqlite database/*.sqlite-journal | |
| # Force the server to re-cache config/routes after deploy | |
| rm -f bootstrap/cache/config.php | |
| rm -f bootstrap/cache/routes-v7.php | |
| rm -f bootstrap/cache/services.php | |
| rm -f bootstrap/cache/packages.php | |
| - name: Deploy via SFTP | |
| uses: wlixcc/SFTP-Deploy-Action@v1.2.4 | |
| with: | |
| server: ${{ secrets.SFTP_HOST }} | |
| username: ${{ secrets.SFTP_USERNAME }} | |
| password: ${{ secrets.SFTP_PASSWORD }} | |
| local_path: "./*" | |
| remote_path: ${{ secrets.SFTP_PATH }} | |
| sftp_only: true | |
| delete_remote_files: false |