The Flyway command line is a bit like magic. Just download it, and it runs across Windows, Mac, and Linux - even without an installer.
In this post I'll explain some of the technical details that make it all work.
In my opinion, the Flyway command line does some pretty cool stuff:
flyway
in your terminal, and it just worksI think this setup is really versatile. The manual install/uninstall process might seem clunky to some. However, this setup means we can just offload that work to package managers like Homebrew or Chocolatey.
You might know that Flyway is written in Java. Since Java apps can be run cross platform, perhaps it seems obvious that Flyway can just be deployed on any mainstream operating system?
In the grand scheme, it's true. Using Java gives Flyway it's cross platform capability. However, Flyway does some clever things beyond what a normal Java build process might offer.
Let's have a look at the Flyway command line archive.
There's a bunch of folders, conf
, drivers
, lib
, and so on. Let's ignore those for now. The interesting part is the flyway
and flyway.cmd
files.
When you run flyway
on the command line, it's one of these two files that are actually executed, depending on your operating system. flyway
is for Linux and MacOS, whereas flyway.cmd
is for Windows.
I'm a Windows guy, so let's open up flyway.cmd
in a text editor.
flyway.cmd
is a batch script file that sets up a lot of variables, then calls the Java executable. Skip to the end to find the line that looks like this:
%JAVA_CMD% -Djava.library.path="%INSTALLDIR%\native" %JAVA_ARGS%
-cp "%CLASSPATH%;%INSTALLDIR%\lib\*;%INSTALLDIR%\lib\%FLYWAY_EDITION%\*;%INSTALLDIR%\drivers\*"
org.flywaydb.commandline.Main %*
There's a lot going on here, right? Let's break it down.
First, let's figure out what %JAVA_CMD%
is. This is a variable, and scrolling up the file you can see how it's defined:
@REM Set the current directory to the installation directory
call :getCurrentBatch INSTALLDIR1
set INSTALLDIR=%INSTALLDIR1%
set INSTALLDIR=%INSTALLDIR:~0,-10%
if exist "%INSTALLDIR%\jre\bin\java.exe" (
set JAVA_CMD="%INSTALLDIR%\jre\bin\java.exe"
) else (
@REM Use JAVA_HOME if it is set
if "%JAVA_HOME%"=="" (
set JAVA_CMD=java
) else (
set JAVA_CMD="%JAVA_HOME%\bin\java.exe"
)
)
The comments are helpful here. We set %JAVA_CMD%
to this location - "%INSTALLDIR%\jre\bin\java.exe"
. If that isn't found, then we use JAVA_HOME
, which is an environment variable set by Java when you install it.
So what is "%INSTALLDIR%\jre\bin\java.exe"
? Look again at the Flyway installation zip. There's a folder called jre
, and within that bin
. Open that up, and you'll see hundreds of Java specific files.
So, Flyway actually bundles the Java Runtime Environment (JRE), if you don't already have it installed.
Flyway brings it's platform with it, so it doesn't need any other installation. I think this is a really elegant approach.
So, %JAVA_CMD%
gets set to a path that can call a Java executable. The next step is to set up the Java Virtual Machine (JVM) to load in all the Flyway specific dependencies (JARs).
Java looks for dependencies on something called the 'classpath'. My understanding is, the classpath is just a list of folders on your machine where Java will load in JARs. Flyway sets up the classpath with the -cp
argument. It sets:
"%INSTALLDIR%\lib\*
, %INSTALLDIR%\lib\%FLYWAY_EDITION%\*
, and %INSTALLDIR%\drivers\*"
Ah, lib
, that's familiar, right? Open it up in the zip archive and you see the following:
community/
enterprise/
gson-*.jar
jansi-*.jar
So, we add gson
and jansi
to the classpath. Then we add everything in the community
or enterprise
folder, depending on which edition of Flyway the user has selected. Open up community
or enterprise
, and you'll see the JARs for Flyway itself.
Flyway also sets this thing called java.library.path
to the native
folder. This is a JVM property, used to configure the JVM's behavior. In this case we're telling Java, 'hey, look in this native
folder for some stuff'. For Flyway, this is needed to add some DLLs for Windows Authentication in SQL Server connections.
Finally, we tell the JVM where to start executing code, org.flywaydb.commandline.Main
.
So, now you've seen a bit more about how the Flyway command line manages to work 'out-of-the-box'. I've glossed over some things, but perhaps you aren't desperate to know much more? :)
In summary, Flyway bundles everything it needs to run in a self-contained way.
I personally really like this approach, however there are some pitfalls. You may have noticed that Flyway is bundling some things only a few users will care about. For instance the DLLs to support Windows authentication. Does every user need it? Probably not!
So Flyway runs the risk of being a bit bloated, in order to provide a nice setup experience for all it's users. I think this is an acceptable risk, really. Maybe some people are really concerned about shaving MBs off the size of their artifacts, but I doubt most people notice.