Step 3: Make a Command-Line Executable

Although we can run our code by calling the run function via mix, it isn’t friendly for other users. So let’s create something we can run from the command line.

Mix can package our code, along with its dependencies, into a single file that can be run on any Unix-based platform. This uses Erlang’s escript utility, which can run precompiled programs stored as a Zip archive. In our case, the program will be run as issues.

When escript runs a program, it looks in your mix.exs file for the option escript. This should return a keyword list of escript configuration settings. The most important of these is main_module:, which must be set to the name of a module containing a main function. It passes the command-line arguments to this main function as a list of character lists (not binaries). As this seems to be a command-line concern, we’ll put the main function in Issues.CLI. Here’s the update to mix.exs:

project/4/issues/mix.exs
 defmodule​ Issues.MixProject ​do
 use​ Mix.Project
 
 def​ project ​do
  [
 app:​ ​:issues​,
»escript:​ escript_config(),
 version:​ ​"​​0.1.0"​,
 elixir:​ ​"​​~> 1.6-dev"​,
 start_permanent:​ Mix.env() == ​:prod​,
 deps:​ deps()
  ]
 end
 
 def​ application ​do
  [
 extra_applications:​ [​:logger​]
  ]
 end
 
 defp​ deps ​do
  [
  { ​:httpoison​, ​"​​~> 1.0.0"​ },
  { ​:poison​, ​"​​~> 3.1"​ },
  ]
 end
»defp​ escript_config ​do
» [
»main_module:​ Issues.CLI
» ]
»end
 end

Now let’s add a main function to our CLI. In fact, all we need to do is rename the existing run function:

project/4/issues/lib/issues/cli.ex
 def​ main(argv) ​do
  argv
  |> parse_args
  |> process
 end

Then we package our program using mix:

 $ ​​mix​​ ​​escript.build
 Generated escript issues

Now we can run the app locally. We can also send it to a friend—it will run on any computer that has Erlang installed.

 $ ​​./issues​​ ​​pragdave​​ ​​earmark​​ ​​4
 num | created_at | title
 ----+----------------------+-----------------------------------------------
 ----------
 159 | 2017-09-21T10:01:24Z | Block level HTML ... messes up formatting
 161 | 2017-10-11T09:12:59Z | Be clear in README ... GFM are supported.
 162 | 2017-10-11T16:59:50Z | Working on ​#161, looking at rendering
 171 | 2017-12-03T11:08:40Z | Fix typespecs