Hashrocket.com / blog

Large mix archives

Create and Publish Your Own Elixir Mix Archives

posted on and written by Micah Cooper in

Image 100x100 micah cooper

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!

Posted in Elixir and tagged with Elixir, mix