Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .github/workflows/erlang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ jobs:
run: make install-rebar-template
- name: Build test plugin
run: |
git clone -b release-60 "https://github.com/emqx/emqx.git" /tmp/emqx
export ERL_LIBS=/tmp/emqx/apps
./scripts/build-sample-plugin.sh \
--tag 1.0.0 \
--name my_emqx_plugin_avsc \
--with-avsc \
--output-dir /tmp \
--build-dir /tmp/emqx/plugins \
--output-dir /tmp/emqx/plugins/ \
--no-cleanup-build-dir
- name: Run dialyzer
run: make -C my_emqx_plugin_avsc dialyzer
cd /tmp/emqx/plugins/my_emqx_plugin_avsc
make dialyzer

test:
runs-on: ubuntu-latest
Expand All @@ -46,13 +49,17 @@ jobs:
version-type: strict
- name: Install rebar3 template
run: make install-rebar-template
- name: Prepare EMQX monorepo layout
run: |
git clone -b release-60 "https://github.com/emqx/emqx.git" /tmp/emqx
- name: Build test plugins
run: make build-test-plugins
run: |
export ERL_LIBS=/tmp/emqx/apps
make build-test-plugins PLUGIN_BUILD_DIR=/tmp/emqx/plugins
- name: Start EMQX
run: make up
- name: Run tests
run: make ct
- name: Stop EMQX
if: always()
run: make down

6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ REBAR = $(CURDIR)/rebar3
SCRIPTS = $(CURDIR)/scripts

TEST_ASSETS_DIR = $(CURDIR)/_build/test/lib/emqx_pt/test/assets
PLUGIN_BUILD_DIR ?=

.PHONY: all
all: compile
Expand All @@ -36,8 +37,8 @@ install-rebar-template:

.PHONY: build-test-plugins
build-test-plugins: $(REBAR)
$(SCRIPTS)/build-sample-plugin.sh --tag 1.0.0 --name my_emqx_plugin_avsc --with-avsc --output-dir $(TEST_ASSETS_DIR)
$(SCRIPTS)/build-sample-plugin.sh --tag 1.0.0 --name my_emqx_plugin --output-dir $(TEST_ASSETS_DIR)
$(SCRIPTS)/build-sample-plugin.sh --tag 1.0.0 --name my_emqx_plugin_avsc --with-avsc --output-dir $(TEST_ASSETS_DIR) $(if $(PLUGIN_BUILD_DIR),--build-dir $(PLUGIN_BUILD_DIR))
$(SCRIPTS)/build-sample-plugin.sh --tag 1.0.0 --name my_emqx_plugin --output-dir $(TEST_ASSETS_DIR) $(if $(PLUGIN_BUILD_DIR),--build-dir $(PLUGIN_BUILD_DIR))

.PHONY: fmt
fmt: $(REBAR)
Expand All @@ -62,4 +63,3 @@ up:
.PHONY: down
down:
docker compose -f .ci/docker-compose.yml down --volumes

37 changes: 18 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@

This is a [rebar3 template](https://rebar3.org/docs/tutorials/templates/#custom-templates) to ease creation of [EMQX](https://github.com/emqx/emqx) v5 Plugins in [Erlang](https://www.erlang.org/).

The documentation refers to the EMQX of the versions `~> 5.9`.
Depending on for which EMQX version you are building the plugin, please follow the instructions respectively:

For EMQX `~> 4.3`, please see branch `emqx-v4`.

For older EMQX versions, plugin development is no longer maintained.
- EMQX v4: Checkout branch `emqx-v4`.
- EMQX v5: Checkout branch `emqx-v5`.
- EMQX v6: Checkout branch `master`.

A plugin template for Elixir (experimental) can be found at https://github.com/emqx/emqx-elixir-plugin.

> [!IMPORTANT]
> Starting from EMQX v6, plugin development is encouraged in the EMQX monorepo.
> See the guide in `PLUGIN.md`: https://github.com/emqx/emqx/blob/release-60/PLUGIN.md

## What is a Plugin?

A plugin is an Erlang application that runs inside the EMQX nodes.
Expand Down Expand Up @@ -102,30 +106,30 @@ The `rebar.config` file is used to build the plugin and pack it into a release.

The most important sections are
* dependencies (`deps`) section;
* build plugins (`plugins`) section;
* rebar plugins (`plugins`) section;
* release section (`relx`);
* plugin description (`emqx_plugin`) section.
* plugin metadata (`emqx_plugrel`) section.

In the `deps` section, you can add dependencies to other OTP applications that your plugin depends on.

```erlang
{deps,
[
{emqx_plugin_helper, {git, "https://github.com/emqx/emqx-plugin-helper.git", {tag, "v5.9.0"}}}
%% more dependencies
%% add your dependencies here
]}.
```

The skeleton adds an extra dependency to the plugin: `emqx_plugin_helper`.
It is usually needed for plugin code to make use of the record definitions and macros provided in the header files.
See [`rebar3` dependency documentation](https://www.rebar3.org/docs/configuration/dependencies/) for more details.
> NOTE
> A plugin release is a self-contained `.tar.gz` package that includes all apps listed in `relx`.
> If a dependency app is already provided by EMQX, the copy from the plugin package is not loaded.
> Avoid shipping incompatible dependency versions.

In the `plugins` section, you add rebar3 providers used for packaging.

```erlang
{plugins,
[
{emqx_plugrel, {git, "https://github.com/emqx/emqx_plugrel.git", {tag, "0.6.3"}}}
{emqx_plugrel, {git, "https://github.com/emqx/emqx_plugrel.git", {tag, "0.6.4"}}}
]}.
```

Expand All @@ -134,7 +138,6 @@ In the `relx` section, you specify the release name and version, and the list of
```erlang
{relx, [ {release, {my_emqx_plugin, "1.0.0"},
[ my_emqx_plugin
, emqx_plugin_helper
]}
...
]}.
Expand Down Expand Up @@ -174,7 +177,7 @@ The `src` directory contains the code of the plugin's OTP application.

Note the following:
* The skeleton uses `{vsn, {file, "VERSION"}}` in `.app.src`.
A `compile` pre-hook writes this `VERSION` file from the `relx` release version in `rebar.config`.
A `compile` pre-hook writes `VERSION` from the `relx` release version in `rebar.config`.
* Pay attention to the `applications` section. Since the plugin is an OTP application, plugin's start/stop/restart is
the respective operation on the plugin's application. So, if the plugin's application depends on other applications,
it should list them in the `applications` section.
Expand Down Expand Up @@ -249,7 +252,6 @@ When a plugin is built into a release, the package structure is as follows:

```
└── my_emqx_plugin-1.1.0.tar.gz
   ├── emqx_plugin_helper-5.9.1
   ├── my_emqx_plugin-0.1.0
   ├── README.md
   └── release.json
Expand Down Expand Up @@ -279,8 +281,7 @@ I.e. the tarball contains the compiled applications (listed in the `relx` sectio
"git_commit_or_build_date": "2025-04-29",
"metadata_vsn": "0.2.0",
"rel_apps": [
"my_emqx_plugin-0.1.0",
"emqx_plugin_helper-5.9.1"
"my_emqx_plugin-0.1.0"
],
"rel_vsn": "1.1.0",
"with_config_schema": true
Expand Down Expand Up @@ -635,5 +636,3 @@ So, to install a new version of the plugin,
* The new version installed.

The configuration is preserved between the installations.


9 changes: 6 additions & 3 deletions emqx-plugin-templates/rebar.config
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{{=@@ @@=}}
%% -*- mode: erlang -*-
{deps, [
{emqx_plugin_helper, {git, "https://github.com/emqx/emqx-plugin-helper.git", {tag, "@@emqx_plugin_helper_vsn@@"}}}
% you will likely need emqx_plugin_helper as dependency when building the plugin standalone
% not required when building the plugin under the 'plugins' dir in emqx monorepo
% {emqx_plugin_helper, {git, "https://github.com/emqx/emqx-plugin-helper.git", {tag, "@@emqx_plugin_helper_vsn@@"}}}
]}.

{plugins, [
{emqx_plugrel, {git, "https://github.com/emqx/emqx_plugrel.git", {tag, "0.6.3"}}}
{emqx_plugrel, {git, "https://github.com/emqx/emqx_plugrel.git", {tag, "0.6.4"}}}
]}.

{project_plugins, [{erlfmt, "1.6.0"}]}.

{erl_opts, [debug_info]}.
%% Add EMQX header include path when developing inside the EMQX monorepo.
{erl_opts, [debug_info, {i, "../../apps/emqx/include"}]}.

%% Sync release version to app version in VERSION file.
%% app.src uses: {vsn, {file, "VERSION"}}.
Expand Down
6 changes: 3 additions & 3 deletions emqx-plugin-templates/src/emqx_plugin_template.erl
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

%% for #message{} record
%% no need for this include if we call emqx_message:to_map/1 to convert it to a map
-include_lib("emqx_plugin_helper/include/emqx.hrl").
-include("emqx.hrl").

%% for hook priority constants
-include_lib("emqx_plugin_helper/include/emqx_hooks.hrl").
-include("emqx_hooks.hrl").

%% for logging
-include_lib("emqx_plugin_helper/include/logger.hrl").
-include("logger.hrl").

-export([
hook/0,
Expand Down
29 changes: 21 additions & 8 deletions scripts/build-sample-plugin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set -euo pipefail
TAG=""
NAME=""
OUTPUT_DIR=""
BUILD_DIR=""
WITH_AVSC=false
CLEANUP_BUILD_DIR=true

Expand All @@ -24,6 +25,10 @@ while [[ $# -gt 0 ]]; do
OUTPUT_DIR="$2"
shift 2
;;
--build-dir)
BUILD_DIR="$2"
shift 2
;;
--with-avsc)
WITH_AVSC=true
shift
Expand All @@ -50,6 +55,7 @@ if [ -z "$NAME" ]; then
fi

cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
ROOT_DIR="$(pwd)"

echo "Installing rebar template"

Expand All @@ -58,30 +64,37 @@ echo "Installing rebar template"
echo "Creating plugin"

rm -rf "$NAME"
./rebar3 new emqx-plugin "$NAME" version="$TAG"
"$ROOT_DIR/rebar3" new emqx-plugin "$NAME" version="$TAG"

PLUGIN_DIR="$ROOT_DIR/$NAME"
if [ -n "$BUILD_DIR" ]; then
mkdir -p "$BUILD_DIR"
rm -rf "$BUILD_DIR/$NAME"
mv "$NAME" "$BUILD_DIR/"
PLUGIN_DIR="$BUILD_DIR/$NAME"
fi

mv "$NAME/priv/config.hocon.example" "$NAME/priv/config.hocon"
mv "$PLUGIN_DIR/priv/config.hocon.example" "$PLUGIN_DIR/priv/config.hocon"

if [ "$WITH_AVSC" = true ]; then
mv "$NAME/priv/config_schema.avsc.enterprise.example" "$NAME/priv/config_schema.avsc"
mv "$NAME/priv/config_i18n.json.example" "$NAME/priv/config_i18n.json"
mv "$PLUGIN_DIR/priv/config_schema.avsc.enterprise.example" "$PLUGIN_DIR/priv/config_schema.avsc"
mv "$PLUGIN_DIR/priv/config_i18n.json.example" "$PLUGIN_DIR/priv/config_i18n.json"
fi

echo "Building plugin"
export BUILD_WITHOUT_QUIC=1
make -C "$NAME" rel
make -C "$PLUGIN_DIR" rel

echo "Copying plugin to $OUTPUT_DIR"
if [ -n "$OUTPUT_DIR" ]; then
mkdir -p "$OUTPUT_DIR"
cp "$NAME"/_build/default/emqx_plugrel/*.tar.gz "$OUTPUT_DIR"
cp "$PLUGIN_DIR"/_build/default/emqx_plugrel/*.tar.gz "$OUTPUT_DIR"
fi


if [ "$CLEANUP_BUILD_DIR" = true ]; then
echo "Cleaning up"
rm -rf "$NAME"
rm -rf "$PLUGIN_DIR"
else
echo "Skipping cleanup"
fi

Loading