A workflow for creating, publishing, and sharing mix archives.
When I first got into Elixir, one thing that
immediately jumped out as me was the convenience of working with
Mix. Mix makes elixir
tooling, fast, simple, and fun! Working with
Phoenix made this apparent to me. The
workflow to get started was a simple mix archive install
and mix
phoenix.new
. So, when I started bulding my own elixir archive,
Gatling, I wanted the setup to be that
easy.
Overview
Let's learn about Mix archives; what they are, how we can create them, and what seems to be a good way to share then with the community.
Mix Archives
A Mix archive is essentially a .zip
of your elixir project in the Erlang
Archive format. But what does that mean
for us? Simply put, this allows us to install a mix arhive and use it
like a command line tool on our system. This is how Phoenix achieves the mix
phoenix.new
task on your system. We run mix archive.install
https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
, and
Mix downloads the archive from the given url, and adds it to ~/.mix/archives
on your system.
Build Your Own Archive
Any time we create a new Elixir project with Mix by running mix new
<project_name>
, we can also create an archive with mix archive.build
. This
will create an archive called <project_name>@x.x.x.ez
.
Share Your Archives
Once you have built an archive, anyone can download and use it with mix. So, we could just push it up to Github and call it a day. But we should do our customers/users justice by versioning our software; allowing users to revert back to an old version if they so choose. So, I'd like to have a separate repository to store just the archives, and I want to make it convenient to add new versions. So lets create a mix project and walk through a nice workflow to facilitate the management of our mix archives.
$ mix new foo #create a new mix project
Now that we have our new project, here is what I want to do every time I release a new version of our archive.
- Update the version of our project
- Commit and push our project
- Build a new archive (
foo-x.x.x.ez
file`). This will be the versioned/historical version of our archive. - Build a new archive (
foo.ez
file`) This will be the "current version" of our archive. - Move both archives into a separate git repo called
foo_archives
- Commit and push/publish the archive repository separately from project
How it's Made
Move into our ./foo
dir and let's get started:
$ cd foo
$ mkdir foo_archives
$ echo "foo_archives/" >> .gitignore # ignore our archives directory. It will be it's own repo
$ git add .
$ git commit -m 'Initial Commit'
$ mkdir foo_archives
$ cd foo_archives
$ git init
$ cd ../
Now, let's create a custom mix task in our project to simplify the creation of releases.
Inside our our ./mix.exs
file our project/0
function should look like this:
def project do
[app: :foo,
version: "0.1.0",
elixir: "~> 1.3",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
end
We're going to clean it up a little, pull the "version" out into a function so
we can use it later, and add an aliases
option to it.
def project do
[
app: :foo,
version: version,
elixir: "~> 1.3",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps(),
aliases: aliases,
]
end
Define version/0
:
def version, do: "1.0.0"
Define aliases/0
:
defp aliases do
[
build: [ &build_releases/1],
]
end
This creates a mix task called build
to our project. When we call $ mix
build
it will call a function named build_releasese/1
so lets define that:
defp build_releases(_) do
Mix.Tasks.Compile.run([])
Mix.Tasks.Archive.Build.run([])
Mix.Tasks.Archive.Build.run(["--output=foo.ez"])
File.rename("foo.ez", "./foo_archives/foo.ez")
File.rename("foo-#{version}.ez", "./foo_archives/foo-#{version}.ez")
end
Because we want to create our archive in production mode, we explicitly compile
in the build task since running mix tasks with MIX_ENV=prod
does not compile
automatically. Then we create our two archives, and move them into
foo_archives
With this new build
task, the workflow for publishing a new release would
look like this:
$ MIX_ENV=prod mix build`
$ cd foo_archive
$ git add .
$ git commit "Release vx.x.x"
$ git push origin master
Now when users want to install the most recent version of your mix project from ,say Github, the command would consistently look something like like:
$ mix archive.install mix archive.install https://github.com/<username>/foo_archives/raw/master/foo.ez
This is a very simple way to maintain historical versions as well as a default version of your mix archives for fun and profit!