How to use Github Actions with Laravel Vapor
Tags: laravel, github
Published: 2022-01-06 12:31:41


Laravel Vapor has been the hot new technology to deploy an infinitely* scalable app powered by AWS Lambda. But with new technology, comes new additional hurdles to deploy your app.


*Note - Albeit limited by budget and resource limits

#### TLDR: Use this YAML file

```
name: Deploy to production

on:
  push:
    branches: [ master ]

jobs:
  vapor:
    name: Check out, build and deploy using Vapor
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: 8.0
        tools: composer:v2
        coverage: none
    - name: Require Vapor CLI
      run: composer global require laravel/vapor-cli
    - name: Deploy Environment
      run: vapor deploy production
      env:
        VAPOR_API_TOKEN: ${{ secrets.VAPOR_API_TOKEN }}
```

YAML File to deploy right from your Github Repository

And ensure VAPOR_API_TOKEN is set in your Github repository secrets. You can generate the token from this link.

Note - This file does NOT run tests before deploying. Testing-step added in the next section.
Running tests before deploying

If you are deploying to any production-level server, you probably want to run your tests to get a sanity check on every feature working as intended.

A couple of things to note while running tests for your Laravel Application in a CI
1. You want to avoid SQLite and use MySQL whenever possible. This is because of some edge case migrations which show up as errors, even though they work fine on MySQL
2. If you are using PHPUnit instead of Pest (Highly recommended) , change the action named "Run Tests"

```
name: Test and deploy to Vapor
on:
  push:
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: test
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    steps:
      - name: Check out code
        uses: actions/checkout@v2
      - name: Cache dependencies
        uses: actions/cache@v2
        with:
          path: ~/.composer/cache/files
          key: dependencies-composer-${{ hashFiles('composer.json') }}
      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.0
          extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql
      - name: Copy environment config file
        run: php -r "file_exists('.env') || copy('.env.ci', '.env');"
      - name: Validate Composer
        run: composer validate
      - name: Install Composer dependencies
        run: composer install --prefer-dist --no-interaction --no-suggest
      - name: Generate key
        run: php artisan key:generate
      - name: Directory Permissions
        run: chmod -R 777 storage bootstrap/cache
      - name: Run DB migrations
        run : php artisan migrate
      - name: Run Tests
        run: ./vendor/bin/pest
        env:
          APP_ENV: testing
          DB_CONNECTION: mysql
          DB_DATABASE: test
          DB_HOST: 127.0.0.1
          DB_PORT: 3306
          DB_USERNAME: root
          DB_PASSWORD: password
      - name: Upload artifacts
        uses: actions/upload-artifact@v2
        if: failure()
        with:
          name: Logs
          path: ./storage/logs
  deploy:
    name: Deploy to Vapor
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master'
    needs:
      - test
    steps:
      - name: Check out code
        uses: actions/checkout@v2
      - name: Validate Composer
        run: composer validate
      - name: Install Composer dependencies
        run: composer install --prefer-dist --no-interaction --no-suggest --no-dev
      - name: Deploy code
        run: ./vendor/bin/vapor deploy ${{ env.APP_ENV }} --commit="${{ github.sha }}"
        env:
          APP_ENV: ${{ github.ref == 'refs/heads/master' && 'production' || 'staging' }}
          VAPOR_API_TOKEN: ${{ secrets.VAPOR_API_TOKEN }}
      - name: Upload artifacts
        uses: actions/upload-artifact@v2
        if: failure()
        with:
          name: Logs
          path: ./storage/logs

```
Entire CI file for running tests before deploying

A few salient features of this CI file are - 

- It runs tests before deploying
- If tests fail, the artefacts/logs are available immediately as downloadable zip files
- Develop branch deploys to staging, Master/Main branch deploys to production automatically. Single file across both the branches.
- Github allows granular control over each step, and this remains highly customizable as per your needs 
- Every step is broken down into clean modular sections, with verbose logging if any step fails

Builds take around 3-5 minutes but depend a lot on the number and size of your tests. This will greatly reduce if you use the multithreaded option for PHPUnit, but that's an optimization I can leave for you.