“Finding new, relevant quotes for every blog post is hard”
– Chris Unwin
SPOILER ALERT – This is part 2 of a 3 part series on enabling database deployments using Redgate Deploy, so if you have not read at least the Setup section of my previous post (Part 1 which you can find here) then I would strongly advise you do so! Thanks!
Welcome to another edition of “Chris stumbles around in the dark until eventually it starts working, but don’t question it because it might stop.” – today we’re taking on GitHub actions and unlike GitLab, I’ve never used Actions before. In my previous post we managed to get 3 repositories set up: 1 for each CICD system we’ll be using, and a number of Azure SQL Databases to use as “Dev“, “Build“, “PROD” etc. just to recap the principles of what we’re trying to achieve though:
Principles
I’m setting up 3 separate pipelines in this post which will all effectively do the same thing, but for different “Prod” copies of databases, however when building and deploying in practice you will have a number of tasks you will want to accomplish in and around the process itself (such as really useful things like Unit Tests, Code Analysis etc.). To keep things simple I will be creating a 6th Database – the “Build” database which will act as our CI validation step and our process for all 3 pipelines will be:
- Invoking a Flyway Clean against the “Build” database – this step will remove every object on the database leaving it empty
- Invoking a Flyway Migrate against the “Build” database – this step will build the database from scratch to validate our baseline script and any further migrations build successfully
- Invoking a further Flyway Migrate against our respective “Prod” database, to deploy the latest scripts we have generated.
GitHub Actions
Like I said, this is the first time I’ve tried to use Actions all by myself and I’m not 100% sure where to start – I’m familiar with the GitHub interface OBVIOUSLY but at first glance I wouldn’t say it’s immediately obvious… well not to a n00b like me anyway! There’s one little “Actions button” in the center and if you miss it you’ll spend far too long not seeing the wood for the trees!

Well anyway after some investigation it turns out that GitHub Actions is called that for a reason, because you build Actions into your YAML pipeline. Duh. Not sure how I missed that… but it kind of makes perfect sense and on top of that its quite similar in a way to Azure DevOps or Jenkins plugins really.
Unfortunately though there was slim pickins’ on the GitHub “Marketplace” for Flyway Actions, the closest one, created by joshuaavalon only migrates and was specifically put together for PostgreSQL deployments:

So I did what any self-respecting person would do… I took the difficult route, forked Joshua’s repo and made the action generic, so that you could pass in whatever command you wanted to, plus changed it to use the latest Alpine version of the Flyway docker container. Plus I bumped the version number tag and fixed the docs that was using an old version of the Action, and by extension, Flyway container.

2 things on this:
- I would assume that there will be an official Flyway Action at some point in the future, and there’s an argument to be made that using different Flyway commands should be different Actions in themselves — so as a result, I’m not going to publish this Action to the marketplace nor am I going to raise a PR for Joshua’s repo. But it was good to test out the theory.
- Thanks to Matt and Andrew here at RG for helping me out with a rather annoying problem involving invoking the correct version – this was solved by them and saved me banging my head against a brick wall for MANY MANY hours.
Anyway once I had the Action itself working (and it turns out it just needs to be sat in a public repo for you to invoke it, which is pretty neat – you don’t HAVE to publish if you don’t want/need to) it was time to move on to properly testing my YAML to build and deploy the contents of GitHub_Flyway. This was what I started off with:
name: CI
on:
push:
branches: [ trunk ]
pull_request:
branches: [ trunk ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ChrisUnwin/flyway-action@master
with:
url: jdbc:sqlserver://dmnonproduction.database.windows.net:1433;database=DMDatabase_Build
user: username
password: password
command: clean
…and everything seemed wonderful:


But then I tried to extend my YAML to also include the Build and Deploy components for the pipeline…

I have 2 problems:
- It turns out it’s not looking at the correct filesystem location (e.g. “.\SQL doesn’t exist”), which is obvious, because I’m a numpty and forgot to change this to instead point at my “migrations” location instead. So I’ll fix that now.
- I forgot to include the Baseline on Migrate and Baseline Migration Version arguments like I did with GitLab. Flyway Desktop generates the baseline, mine being V001_20211130101136__baseline.sql so I need it to baseline (but not run) V001_20211130101136 when Flyway goes to deploy. Otherwise Flyway will, like I experienced, fail the deployment because there are existing objects in the PROD database… obviously.
The solution to #1 is SUPER easy fortunately but the solution to #2 has me thinking. If i add -baselineOnMigrate=true to the action, then it will baseline my Build database too, meaning it won’t run the baseline and my build will fail; so I will need to pass an additional argument to my YAML somehow… *sigh* back to the Action.
I also by luck happened to notice that all my actions steps were running at the same time – but thanks to this blog post from Bryan Hogan I was able to find the neat little “uses” command, so that fixed that issue….
So the final version of the Action was:
name: Flyway Pipeline Action
description: Migrate database in Github Action using latest Flyway Docker Container
branding:
icon: database
inputs:
url:
description: The jdbc url to use to connect to the database
required: true
user:
description: The user to use to connect to the database
required: false
password:
description: The password to use to connect to the database
required: false
locations:
description: Comma-separated list of locations to scan recursively for migrations
required: true
default: filesystem:./sql
command:
description: Which of the Flyway commands you would like Flyway to run
required: true
default: migrate
disableclean:
description: Would you like clean disabled (true or false)
required: false
default: false
baselineonmigrate:
description: Would you like to baseline on migrate (true or false)
required: false
default: false
baselineversion:
description: What is your baseline version
required: false
default: 0.0
runs:
using: docker
image: docker://flyway/flyway:latest-alpine
env:
FLYWAY_URL: ${{ inputs.url }}
FLYWAY_USER: ${{ inputs.user }}
FLYWAY_PASSWORD: ${{ inputs.password }}
FLYWAY_LOCATIONS: ${{ inputs.locations }}
FLYWAY_CLEAN_DISABLED: ${{ inputs.disableclean }}
FLYWAY_BASELINE_ON_MIGRATE: ${{ inputs.baselineonmigrate }}
FLYWAY_BASELINE_VERSION: ${{ inputs.baselineversion }}
args:
- ${{ inputs.command }}
I just re-invented the wheel (which I know you shouldn’t do) but it was super easy – I added the environment variables for Flyway to provide the -baselineOnMigrate and -baselineVersion switches much like I had to in the last post.
I never thought I’d be so happy to see those little green dots:

This resulted from the following pipeline YAML:
name: CI
on:
push:
branches: [ trunk ]
pull_request:
branches: [ trunk ]
workflow_dispatch:
jobs:
clean-build-schema:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ChrisUnwin/flyway-action@master
with:
url: jdbc:sqlserver://dmnonproduction.database.windows.net:1433;database=DMDatabase_Build
user: username
password: password
command: clean
build:
needs: clean-build-schema
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ChrisUnwin/flyway-action@master
with:
url: jdbc:sqlserver://dmnonproduction.database.windows.net:1433;database=DMDatabase_Build
user: username
password: password
locations: /GitLab-Flyway/migrations
command: migrate
deploy-to-prod:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ChrisUnwin/flyway-action@v4
with:
url: jdbc:sqlserver://dmproduction.database.windows.net:1433;database=DMDatabase_PROD_GitHub
user: username
password: password
locations: /GitLab-Flyway/migrations
command: migrate
disableclean: false
baselineonmigrate: true
baselineversion: 001.20211130101136
Can we take a minute to appreciate the fact that I have only just now realized that I accidentally named the top level folder GitLab-Flyway across all 3 of my repos? Well, this pipeline has nothing to do with GitLab, but it works and that’s what I care about!:

Conclusion
Getting my head around how actions work as part of the pipeline was definitely one of the hardest aspects to this challenge – however, given that yet again we can simply include the Docker container for Flyway as part of the pipeline, we can very quickly and easily get up and running with Flyway for our Redgate Deploy pipeline, you just need the right YAML for the job.