[!WARNING] ๐ง Low maintenance. The author currently only responds to pull requests. Don't use
master; the current release line is0.1.7.
๐ญ Why?
Build apps whose features are activated as plugins at runtime โ registration, SMS, social login โ without touching the core source. Each feature becomes an event; anyone can attach plugins to it from outside your app.
โจ Features
- ๐ Events & Hooks โ register plugins for an event; they run in priority + dependency order.
- โก Fast dispatch โ each event compiles to a module, so
Hook.call/3is a direct call: no GenServer, no DB on the read path. - ๐ฉบ Health checks โ optional per-plugin
health_check/0, inspected viaHook.event_health/1. - ๐ Multi-node โ the Mnesia-backed store joins & replicates across a cluster automatically.
- ๐ฆ Runtime installer โ load pre-built
ebinartifacts (local path, URL, or GitHub release). Works in amix release.
๐ Events & Hooks
Define a plugin for an event โ it auto-registers and runs whenever the event is called:
defmodule RegisterEmailSender do
use MishkaInstaller.Event.Hook, event: "after_success_login"
@impl true
def call(entries), do: {:reply, entries}
end# tweak defaults
use MishkaInstaller.Event.Hook,
event: "after_success_login",
initial: %{depends: [SomeEvent], priority: 20}Call every plugin registered for an event:
alias MishkaInstaller.Event.Hook
Hook.call("after_success_login", params)
Hook.call("after_success_login", params, private: keep_this) # extra data, untouched by plugins
Hook.call("after_success_login", params, return: true) # return the original inputTo start a plugin automatically, add its module to your supervision tree:
children = [RegisterEmailSender, ...][!NOTE] A plugin's
dependsalways run before it (cycles are rejected at registration), and a plugin can return{:reply, :halt, state}to stop the rest of the chain. SeeMishkaInstaller.Event.Hook.
๐ฆ Installer
Load an already-compiled ebin at runtime โ no source compilation, so it's release-safe:
alias MishkaInstaller.Installer.Installer
Installer.install(%{app: "demo", version: "0.1.0", type: :path, path: "/ext/demo-0.1.0"})
Installer.install(%{app: "demo", version: "0.1.0", type: :url, path: "https://.../demo-ebin.tar.gz"})
Installer.install(%{app: "demo", version: "0.1.0", type: :github_tag, path: "owner/repo", tag: "0.1.0"})
Installer.install(%{app: "demo", version: "0.1.0", type: :github_latest_release, path: "owner/repo"})
Installer.uninstall(%{app: "demo", version: "0.1.0"})[!NOTE] Remote installs (
:url/:github_*) are fail-closed: they require the source host/repo inconfig :mishka_installer, :allowlist, url_hosts:/github_repos:. Optionalchecksum:(sha256) pins the artifact;:protected_appsguards apps from being overwritten/removed. SeeMishkaInstaller.Installer.Installer.
๐ญ Production deployment
An installed library is a pre-built ebin on disk plus a Mnesia record; on every boot it is replayed (put back on the code path and started). So both the ebins and the Mnesia data must live on a persistent (mounted) volume โ otherwise installs do not survive a restart/redeploy. Point both at your volume (here /data):
# config/runtime.exs โ /data is your mounted volume
config :mishka_installer,
project_path: "/data",
extensions_path: "/data/extensions"
config :mishka_installer, MishkaInstaller.MnesiaRepo,
mnesia_dir: "/data/mnesia",
essential: [MishkaInstaller.Event.Event, MishkaInstaller.Installer.Installer]:extensions_path is where ebins are written; mnesia_dir is where the records live; :essential are the tables created on boot (the plugin and install stores โ keep both). Just depend on :mishka_installer; its supervision tree starts automatically outside :test.
A real release restart proof lives in
test/integration/production_release/โ run it withmix test --only production_release.
๐ Installation
def deps do
[{:mishka_installer, "~> 0.1.7"}]
endDocs: hexdocs.pm/mishka_installer
๐ Funding & sponsorship
MishkaInstaller is open-source software developed by Mishka Group. If your team or company benefits from it, please consider supporting continued development:
Thank you. ๐
๐ License
Apache License 2.0 โ see LICENSE.
Copyright ยฉ Mishka Group and contributors.