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 zigWe 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]]
]
]
]
]
endRun 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:startor: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
burritoorbakeware - 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.