Elixir 1.12 and Your First Mix.install Script

Elixir 1.12 was released and it includes a number of exciting things. The one I’m going to focus on here is Mix.install/2. This makes it easier to use Elixir when scripting.

When Elixir 1.12 is paired with OTP 24 (which was released one week earlier), then you get even more benefits. The most notable is BeamAsm, the JIT compiler for Erlang which, simply put, makes your Elixir code run faster!

This guide gets you updated to Elixir 1.12 with OTP 24 using the version manager tool called asdf.

Once we’re setup with Elixir 1.12, we’ll create our first Elixir script that uses Mix.install/2 to install and use the package Req, a tool that makes it easy to make web requests from a script. We’ll also use Floki for parsing the returned HTML.

I use asdf-vm for managing my Elixir and Erlang versions. This guide uses asdf to do this as well. The next sections assume you are already using asdf. Just skip those sections if you already have Elixir 1.12 installed by some other way and want to try out scripting. If you want help getting started with asdf, check out this post.

Why use asdf?

When you are working with production Elixir applications, sometimes you can’t upgrade the version of Elixir (or Erlang) that you’re using because of things like platform constraints, libraries that need to updated, or deprecation warnings that need to be cleaned up first.

Asdf is a great tool for situations like that. It helps you manage and use multiple versions of Elixir, Erlang, Node, Ruby, and a lot more too. This makes it easier to test upgrade your Elixir project while still switching back to your “production version” when needed.

Update asdf

First, make sure your asdf version is up-to-date.

$ asdf update

Update your asdf plugins

Since we are going to update Elixir and Erlang, let’s just make sure our asdf plugins for those languages are up-to-date as well.

$ asdf plugin-update --all

Install Erlang OTP 24

Let’s make sure we’re installing the latest OTP version of Erlang, I run the following command.

$ asdf list-all erlang
...
23.3
23.3.1
23.3.2
23.3.3
23.3.4
24.0-rc1
24.0-rc2
24.0-rc3
24.0
24.0.1

I found “24.0.1” as the latest version for OTP 24. Install that version. This step may take some time.

$ asdf install erlang 24.0.1
asdf_24.0.1 is not a kerl-managed Erlang/OTP installation
No build named asdf_24.0.1
Downloading OTP-24.0.1.tar.gz to /home/mark/.asdf/plugins/erlang/kerl-home/archives...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   122  100   122    0     0    434      0 --:--:-- --:--:-- --:--:--   434
100 56.1M  100 56.1M    0     0  1772k      0  0:00:32  0:00:32 --:--:-- 1721k
Extracting source code
Building Erlang/OTP 24.0.1 (asdf_24.0.1), please wait...

...

Erlang 24.0.1 has been installed. Activate globally with:

    asdf global erlang 24.0.1

Activate locally in the current folder with:

    asdf local erlang 24.0.1

Activate the new version either locally or globally. Locally means for that directory. So when you run a terminal command from that directory, it will use the version you specified. Globally means it becomes the default version to use from a terminal when running commands. A local version overrides a global one.

I’m going to make this my new global default.

$ asdf global erlang 24.0.1

Install Elixir

Now I can install Elixir 1.12. I need to make sure I’m installing the correct version that is intended for OTP 24. So this is the command I use.

$ asdf list-all elixir
...
1.12.0
1.12.0-otp-22
1.12.0-otp-23
1.12.0-otp-24
...

Near the end of the list I find this entry: 1.12.0-otp-24. That’s the version I want. It was built for OTP 24.

$ asdf install elixir 1.12.0-otp-24
==> Checking whether specified Elixir release exists...
==> Downloading 1.12.0-otp-24 to /home/mark/.asdf/downloads/elixir/1.12.0-otp-24/elixir-precompiled-1.12.0-otp-24.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 6010k  100 6010k    0     0  5779k      0  0:00:01  0:00:01 --:--:-- 5779k
==> Copying release into place

Make sure to activate this newly installed version! Again, here I’m making it the global default.

$ asdf global elixir 1.12.0-otp-24

Let’s make sure we are using the correct Elixir version.

$ elixir -v
Erlang/OTP 24 [erts-12.0.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Elixir 1.12.0 (compiled with Erlang/OTP 24)

We are set!

Using Mix.install/2 in an Elixir Script

For this example, let’s say we want to write a quick script that can grab some information from Github. We want to return all the repositories under the elixir-lang organization. Doing something so simple doesn’t make sense to create a whole mix project. But we want to use libraries to help do the work. Previously, we’d have no choice but to create a mix project or we may just turn to a different tool to solve our need.

Now we can continue to use our favorite language to do simple scripts like this.

Using your preferred code editor, create a new file. Let’s call it elixir-repos.exs.

Then paste in the following content:

Mix.install([
  {:req, "~> 0.2"},
  {:floki, "~> 0.30.0"}
])

resp = Req.get!("https://github.com/elixir-lang")

resp.body
|> Floki.parse_document!()
|> Floki.find(~s{[data-hovercard-type="repository"]})
|> Enum.map(&Floki.text/1)
|> Enum.map(&String.trim/1)
|> Enum.each(&IO.puts/1)

That’s the whole script! Pretty small isn’t it?

The first line loads the dependencies we require. Once loaded, they can be used directly in the script.

Let’s run it.

Be warned! The first time we run it, we get a lot more output. It pulls down the needed libraries and caches them for us. It also executes the script and we see our output at the end.

$ elixir elixir-repos.exs
* Getting req (https://github.com/wojtekmach/req.git - origin/main)
remote: Enumerating objects: 433, done.
remote: Counting objects: 100% (433/433), done.
remote: Compressing objects: 100% (206/206), done.
remote: Total 433 (delta 209), reused 421 (delta 201), pack-reused 0
Resolving Hex dependencies...
Dependency resolution completed:
New:
  castore 0.1.10
  finch 0.6.3
  floki 0.30.1
  html_entities 0.5.2
  jason 1.2.2
  mime 1.6.0
  mint 1.3.0
  nimble_options 0.3.5
  nimble_pool 0.2.4
  telemetry 0.4.3
* Getting floki (Hex package)
* Getting html_entities (Hex package)
* Getting finch (Hex package)
* Getting mime (Hex package)
* Getting jason (Hex package)
* Getting castore (Hex package)
* Getting mint (Hex package)
* Getting nimble_options (Hex package)
* Getting nimble_pool (Hex package)
* Getting telemetry (Hex package)
==> nimble_options
Compiling 3 files (.ex)
Generated nimble_options app
==> nimble_pool
Compiling 2 files (.ex)
Generated nimble_pool app
===> Analyzing applications...
===> Compiling telemetry
==> jason
Compiling 8 files (.ex)
Generated jason app
==> html_entities
Compiling 2 files (.ex)
Generated html_entities app
==> floki
Compiling 1 file (.xrl)
Compiling 2 files (.erl)
Compiling 24 files (.ex)
Generated floki app
==> castore
Compiling 1 file (.ex)
Generated castore app
==> mint
Compiling 1 file (.erl)
Compiling 23 files (.ex)
Generated mint app
==> finch
Compiling 9 files (.ex)
Generated finch app
==> mime
Compiling 2 files (.ex)
Generated mime app
==> req
Compiling 3 files (.ex)
Generated req app
elixir
ex_doc
elixir-lang.github.com
gen_stage
elixir_make
elixir-windows-setup
registry

When we run the script a second time, it uses the already cached libraries and just executes the script. Nice!

If you change the dependencies, then it will re-fetch them all and cache that set.

$ elixir elixir-repos.exs
elixir
ex_doc
elixir-lang.github.com
gen_stage
elixir_make
elixir-windows-setup
registry

Next time you want to script something on your machine, now you can consider using Elixir for it!

Also, check out the Elixir 1.12 release blog post as it also outlines another Elixir 1.12 improvement System.trap_signal/3 and how that can be used to trap exits in an Elixir script.

If you want to go deeper on Mix.install/2 checkout out our interview with Wojtek Mach where he explained more of how it all works.

Enjoy!

2 Comments

  1. Jukka Välimaa on November 27, 2021 at 12:23 pm

    Thanks for writing this, great stuff!

    Getting the following error when running the code as-is, maybe fix it by changing the “req” dependency to 0.2.1:
    ***
    Unchecked dependencies for environment dev:
    1 Mix.install([
    * req (https://github.com/wojtekmach/re
    1 Mix.install([
    q.git – origin/main)
    the dependency does not match the requirement “~> 0.1.0-dev”, got “0.2.1”
    ***

    • Mark Ericksen on November 27, 2021 at 6:49 pm

      Thanks for the tip! Updated it to use the released version of the package. Thanks!

Leave a Comment

You must be logged in to post a comment.