Generate a Mix Release with a binary executable launcher, instead of batch/shell scripts.
Relexe
uses Burrito with a modified build phase. The build phase uses Zig to build an executable launcher with your defined CLI. The CLI can include some or all of the usual commands plus any custom eval
and/or rpc
commands you define.
Install the pre-requisites (using scoop):
scoop install 7zip extras/vcredist2022 zig@0.9.1
scoop hold zig
We hold Zig at version 0.9.1
as relexe
hasn't been tested on anything newer.
Create a release in mix.exs
like so:
def project do
[
...
releases: [
bananas: [
steps: [:assemble, &Relexe.assemble/1],
relexe: [
default_command: :start,
commands: [
:start,
:stop,
:service,
{:remote, hidden: true},
{:eval, hidden: true},
{:rpc, hidden: true},
{"migrate",
help: "Run database migrations",
eval: "Bananas.Release.migrate()"
},
{"create-admin",
help: "Create an admin user",
eval: {Bananas.Release, :create_admin, [:username, :password]}
},
{"something",
help: "Do something",
rpc: "Bananas.Release.something()"
}
],
env: [
windows: [
RELEASE_DISTRIBUTION: "none",
RELEASE_NODE: "bananas69"
]
]
targets: [windows: [os: :windows, cpu: :x86_64]]
]
]
]
]
end
Run mix release
and you'll have bananas.exe
in the bin
folder of your release. The environment variables specified in env
are written to releases/<version>/.env
, which is checked when starting your app. See Environment Variables for more details.
The help
for this example would be:
USAGE:
bananas.exe [COMMAND]
COMMANDS:
start Start bananas (default)
stop Stop bananas
service <COMMAND> Add, remove, start or stop the bananas Windows Service
migrate Run database migrations
create-admin Create an admin user
something Do something
HELP:
help <COMMAND>
Running bananas create-admin
would prompt the user for the username
and password
:
> bananas.exe create-admin
username: Eric
password: BananaMan
The responses will be passed to the {Bananas.Release, :create_admin, ["Eric", "BananaMan"]}
MFA
via eval
.
default_command
: the command to run if no args are passed. Can be either:start
or:help
. Defaults tohelp
.commands
: optional list of commands to include in the release.env
: optional list of environment variables for each target.targets
: list of targets, as defined by Burrito.
One of the main goals of this project is to create a release executable with custom CLI commands. For example, you might want to create a migrate
command to run database migrations or a gen-secret
so the user can generate a secret for their self-hosted Phoenix app.
The default commands are the same commands included with a regular Mix
release, except that install
, which installs a Windows service is replaced by service add
. The default commands are start start_iex service eval rpc remote restart stop pid version
on Windows and start start_iex daemon daemon_iex eval rpc remote restart stop pid version
on Unixesque OSes.
The Windows Service controls are managed through the release executable. In a Mix
release, you use bananas.bat install
to create the service and then manage the service using erlsrv.exe
. With Relexe
, you manage the service with bananas.exe service [add|remove|start|stop|list|help]
.
eval
and rpc
commands are defined as a three-element keyword list with a name
, help
string and eval
or rpc
command. The command can be either a straight function call, e.g. Bananas.Release.migrate()
, or a {Module, :function, [arg_names]}
tuple, e.g. {Bananas.Release, :create_admin, [:username, :password]}
. An MFA command will prompt the user for each argument value, as it is not feasible to handle args on the command line in Windows.
# Function call
[
name: "migrate",
help: "Run database migrations",
eval: "Bananas.Release.migrate()"
],
# MFA style
[
name: "create-admin",
help: "Create an admin user",
rpc: {Bananas.Release, :create_admin, [:username, :password]}
]
The environment variables that you define are written to <app>/releases/<version>/.env
and/or <app>/releases/<version>/.env.<command>
. They are defined per target
and can be configured either by setting the relexe[:env]
option in your release config or by creating a /rel/relexe/.env.<target>[.command].eex
file. If both exist, the options take precendence.
Specify the environment variables for each target in a keyword list in the env
option. If you want to load a different set of environment variables for certain commands, specify those with the command as the key under the target. If you specify a command specific file, only that file will be parsed, i.e. the .env
will be ignored.
For example, the contrived example below will output two files: bananas/releases/1.0.0/.env
and bananas/releases/1.0.0/.env.sales
. If you call bananas.exe start
it will load the environment variables from .env
. If you call bananas.exe sales
it will load the environment variables from .env.sales
.
releases: [
bananas: [
steps: [:assemble, &Relexe.assemble/1],
relexe: [
commands: [:start, :stop, :sales],
env: [
windows: [
RELEASE_DISTRIBUTION: "none",
RELEASE_NODE: "bananas69",
sales: [
RELEASE_DISTRIBUTION: "name",
RELEASE_NODE: "banana_sales"
]
]
],
targets: [
windows: [os: :windows, cpu: :x86_64]
]
]
]
]
# .env
RELEASE_DISTRIBUTION=none
RELEASE_NODE=bananas69
# .env.sales
RELEASE_DISTRIBUTION=name
RELEASE_NODE=banana_sales
Environment variables can also be specified by way of an EEx
template. Template files shall be created in /rel/relexe
and be named .env.<target>.eex
or .env.<target>.<command>.eex
, where <target>
is windows
, linux
or macos
and <command>
is the name of the command that these environment variables are for.
For example, to recreate the Mix Release daemon
environment variables, we create a /rel/relexe/.env.linux.daemon.eex
.
HEART_COMMAND="<%= @context.mix_release.path %>/bin/<%= @context.mix_release.name %> daemon"
ELIXIR_ERL_OPTIONS="-heart"
NOTE: I'm not sure if this actually works, as I don't deploy to linux/macos. Please let me know if it doesn't :)
TODO
TODO
- with env vars
- with empdless/empdlessless/etc
- if you want a single binary, use
burrito
orbakeware
- if you want to add CLI commands to a Mix Release, consider using
relexe
- if end users are installing your software, consider using
relexe
- if you're deploying to your own infrastructure, use whatever you like
Mix.Release | Burrito | Relexe | |
---|---|---|---|
single file? | ❌ | ✅ | ❌ |
plugins | ❌ | ✅ | ✅ (Burrito's) |
launcher | batch/shell scripts | binary executable | binary executable |
windows service control | <release>.bat install then through erlsrv.exe |
non-goal, i.e. DIY, e.g. NSSM | <release>.exe service CLI |
run laucher w/o args | help |
start |
start or help |
- CI
- tests for
EnvVars
- rename erl.exe and change icon
- test on macos & linux
- plugins
- I've only tested this on Windows.
- I have no idea what I'm doing when it comes to writing Zig code.