The release of Laravel 8 introduced us to many great new features, and I think it is one of the best releases we have ever had! 🎉 😍
However, to me, the schema dump command is far by the
most useful for me in some personal projects and for the large projects I work on every day at work. I couldn't wait
to delete the migrations from some project where we had hundreds of migration files. In this post I will take you
through the challenges faced when I decided to add schema:dump
into our release process and how we automated it
using GitHub Actions.
This post assumes that you have some working knowledge of GitHub Actions, their yaml
workflow
files, and that you know what the schema command does. If not please check the Laravel
documentation first.
When to trigger the schema dump workflow?
This will probably be different from project to project and how you manage your releases. At work, we use GitHub
releases for both non-prod (test and staging) and for production. Our nonprod release tags usually contain the
words alpha
or beta
, for example, 2.4.11-alpha.4
or 2.4.11-beta.12
where the final number is a release
candidate identifier. When we create these nonprod releases in github we tick the This is a pre-release
checkbox and
within a GitHub action you are able to check whether a published release is a pre-release or not.
I knew from the start that we wanted to run schema:dump
whenever we published a release but not when the published
release was marked as a pre-release. This made setting up the first part of the schema-dump.yml
easy:
1name: "Schema Dump" 2 3on: 4 release: 5 types: [published] 6 7jobs: 8 dump: 9 if: github.event.release.prerelease != true10 # ...
Running the schema dump command
Once I had copied all the boilerplate MySQL, PHP, Composer, Cache and Laravel setup from our existing
Run tests
action it was time to add the step to actually run the schema:dump
command. As you can see in the below
snippet it runs the migrate
command first to go through all the current migrations.
Then we run the schema:dump
command, the --prune
argument tells the command to delete any migration files as the
generated mysql-schema.sql
file will contain an absolute version of your database structure, so the migration files
are no longer needed.
1- name: Run migration and schema dump2 run: |3 php artisan migrate --force4 php artisan schema:dump --prune5 env:6 DB_HOST: 127.0.0.17 DB_PORT: ${{ job.services.mysql.ports[3306] }}
Automatically creating a pull request from the changes
Fortunately, the GitHub Actions has an action for almost anything you need and there is an action created specifically to make creating a PR from local changes really simple. The Create Pull Request action allows you to customise anything you can think of from the PR title, body, commit message, the new branch and much more.
1- name: Create Pull Request2 uses: peter-evans/create-pull-request@v33 with:4 base: master5 branch: schema-dump6 branch-suffix: short-commit-hash7 commit-message: "Run schema dump to clean up migrations"8 title: "Run schema dump to clean up migrations"9 body: "This PR was generated by the schema-dump GitHub Action"
If the schema:dump
has made any changes then the above step will create a branch with a name like
schema-dump-ae234ab
where the suffix is from the commit hash of the changes. The other properties are
self-explanatory please see the action for the documentation.
Generated PRs won't run other workflows 💥
I assume, in an attempt to prevent someone from accidentally creating an infinite loop, the above step successfully
creates the pull request, but it does not trigger my other workflows that will run tests using the newly generated
mysql-schema.sql
file.
Assuming those previously using the 'create pull request action' will have encountered this I searched the Issues on that action and came across an answer which linked to some documentation the maintainer has provided on the various options available to make the PR trigger further workflow runs.
Creating a Schema Dumper GitHub App and using it in the workflow
I quickly decided that the best option was to create a GitHub App and install on my organization so that it can be
used in multiple projects. Again, the create pull request
maintainer has graciously provided documentation on how to
use this, and the
changes required to make it work within the workflow
which means I don't need to go through those steps in this post.
1- name: Generate new token using app 2 uses: tibdex/github-app-token@v1 3 id: generate-token 4 with: 5 app_id: ${{ secrets.SCHEMA_DUMPER_APP_ID }} 6 private_key: ${{ secrets.SCHEMA_DUMPER_APP_PRIVATE_KEY }} 7 8- name: Create Pull Request 9 uses: peter-evans/create-pull-request@v310 with:11 base: master12 branch: schema-dump13 branch-suffix: short-commit-hash14 commit-message: "Run schema dump to clean up migrations"15 title: "Run schema dump to clean up migrations"16 body: "This PR was generated by the schema-dump GitHub Action"17 token: ${{ steps.generate-token.outputs.token }}
Once this was in place the PRs were being created and the Run tests
workflows were running automatically on those
PRs. The schema-dumper
bot was now the author of the PR which rounds it off nicely.
Conclusion
I hope this post has inspired you to automate your schema dumps or perhaps try automating some other parts of
your release process. Let me know on Twitter if you find any issues with what I have described or if you want to talk
about it. Please find my full schema-dump.yml
workflow file below.
1name: "Schema Dump" 2 3on: 4 release: 5 types: [published] 6 7jobs: 8 test: 9 10 if: github.event.release.prerelease != true11 12 runs-on: ubuntu-latest13 14 name: Schema Dump (${{ github.event.release.tag_name }})15 16 services:17 mysql:18 image: mysql:8.019 env:20 MYSQL_ALLOW_EMPTY_PASSWORD: yes21 MYSQL_DATABASE: services_test22 ports:23 - 330624 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=325 26 steps:27 - name: Checkout code28 uses: actions/checkout@v229 30 - name: Cache composer dependencies31 uses: actions/cache@v132 with:33 path: ~/.composer/cache/files34 key: dependencies-php-7.4-composer-${{ hashFiles('composer.lock') }}35 restore-keys: |36 dependencies-php-7.4-composer-37 38 - name: Setup PHP39 uses: shivammathur/setup-php@v240 with:41 php-version: 7.442 extensions: curl, dom, fileinfo, filter, iconv, json, libxml, mbstring, openssl, pcntl, pcre, pdo, simplexml, sqlite, pdo_sqlite, tokenizer, xml, xmlwriter, zip43 44 - name: Install PHP dependencies (composer)45 run: |46 composer config "http-basic.nova.laravel.com" "${{ secrets.NOVA_USERNAME }}" "${{ secrets.NOVA_PASSWORD }}"47 composer install --no-interaction --no-suggest48 49 - name: Prepare Laravel Application50 run: cp .env.testing .env51 52 - name: Run migration and schema dump53 run: |54 php artisan migrate --force55 php artisan schema:dump --prune56 env:57 DB_HOST: 127.0.0.158 DB_PORT: ${{ job.services.mysql.ports[3306] }}59 60 - name: Generate new token using app61 uses: tibdex/github-app-token@v162 id: generate-token63 with:64 app_id: ${{ secrets.SCHEMA_DUMPER_APP_ID }}65 private_key: ${{ secrets.SCHEMA_DUMPER_APP_PRIVATE_KEY }}66 67 - name: Create Pull Request68 uses: peter-evans/create-pull-request@v369 with:70 base: master71 branch: schema-dump72 branch-suffix: short-commit-hash73 commit-message: "Run schema dump to clean up migrations"74 title: "Run schema dump to clean up migrations"75 body: "This PR was generated by the schema-dump GitHub Action"76 token: ${{ steps.generate-token.outputs.token }}
Syntax highlighting by Torchlight.dev