Align aether plugin entrypoint and docs

This commit is contained in:
2026-04-08 13:11:39 +02:00
parent f833791991
commit 63e10ad5bc
7 changed files with 96 additions and 106 deletions
+2
View File
@@ -6,6 +6,8 @@
# Node
/node_modules/
/assets/node_modules/
/priv/static/*
!/priv/static/.gitkeep
# Generated on crash
erl_crash.dump
+58 -67
View File
@@ -1,21 +1,11 @@
# Tribes Plugin Template
# Aether
Template for creating [Tribes](https://github.com/your-org/tribes) plugins.
`aether` is an external Tribes plugin that provides the `aether@1`
capability.
## Getting Started
1. Click **"Use this template"** on GitHub to create your own repo
2. Clone and rename:
```bash
git clone https://github.com/you/your-plugin.git
cd your-plugin
./scripts/rename.sh your_plugin YourPlugin
```
3. Edit `manifest.json` — set description, capabilities, requirements
4. Implement your plugin in `lib/your_plugin/plugin.ex`
5. Run tests:
Install deps and run the test suite:
```bash
mix deps.get
@@ -27,85 +17,86 @@ mix test
For local development alongside a Tribes checkout:
```bash
# Symlink into the host plugins directory
# Symlink into the host plugins directory once
cd /path/to/tribes
ln -s /path/to/your-plugin plugins/your_plugin
ln -s /path/to/tribes-plugin-aether plugins/aether
# Start Tribes dev server — your plugin loads automatically
# Start the Tribes dev server
iex --sname dev -S mix phx.server
```
Edit your plugin source. Phoenix code reloader picks up changes.
Then edit `aether` in its own repo. In development, the Tribes host watches
symlinked external plugins and automatically:
- runs `mix compile` for Elixir, HEEx, manifest, and migration changes
- runs `npm run build --prefix assets`
- reloads the plugin in the running Tribes VM
- triggers a Phoenix browser reload after the rebuild finishes
That means the normal loop is:
```bash
cd /path/to/tribes-plugin-aether
mix deps.get
mix test
# in another terminal
cd /path/to/tribes
iex --sname dev -S mix phx.server
```
## Project Structure
```
your_plugin/
├── manifest.json # Plugin metadata (Nix build + runtime)
├── mix.exs # Dependencies
aether/
├── manifest.json
├── mix.exs
├── lib/
│ ├── your_plugin/
│ │ ├── plugin.ex # Tribes.Plugin entry point
│ │ └── application.ex # OTP supervision tree (optional)
│ └── your_plugin_web/
│ └── live/ # LiveView pages
├── assets/ # JS/CSS (one bundle per plugin)
│ ├── aether/
│ │ ├── application.ex
│ │ └── plugin.ex
│ └── aether_web/
│ └── live/
├── assets/
│ ├── css/aether.css
│ ├── js/aether.js
│ ├── package.json
│ └── package-lock.json
├── priv/
│ ├── static/ # Built assets for release
│ └── repo/migrations/ # Ecto migrations
└── test/
```
## Manifest
`manifest.json` declares your plugin's identity and capabilities:
`manifest.json` declares the plugin contract:
```json
{
"name": "your_plugin",
"provides": ["some_capability@1"],
"requires": ["ecto@1"],
"enhances_with": ["inference@1"]
"name": "aether",
"version": "0.1.0",
"entry_module": "Tribes.Plugins.Aether.Plugin",
"host_api": "1",
"otp_app": "aether",
"provides": ["aether@1"],
"requires": ["ecto@1", "phoenix@1"],
"enhances_with": [],
"assets": {
"global_js": ["aether.js"],
"global_css": ["aether.css"]
}
}
```
- **provides** — capabilities this plugin makes available
- **requires** — hard dependencies (build fails without them)
- **enhances_with** — optional dependencies (plugin degrades gracefully)
See the [Plugin System docs](https://github.com/your-org/tribes/blob/master/docs/PLUGINS.md) for the full specification.
## Testing
Three test levels:
- **Unit tests** (`test/your_plugin/`) — plugin logic in isolation
- **Manifest tests** (`test/your_plugin/manifest_test.exs`) — manifest schema validation
- **Contract tests** (`test/contract_test.exs`) — runtime spec matches manifest
Run all: `mix test`
For DB setup/migrations in local development, run:
```bash
mix tribes.migrate
```
This runs Tribes + Parrhesia + plugin migrations via `Tribes.Release`.
See the host plugin docs in [PLUGINS.md](https://github.com/your-org/tribes/blob/master/docs/PLUGINS.md)
for the full manifest and runtime contract.
## Building for Release
```bash
MIX_ENV=prod mix compile
mkdir -p dist/your_plugin
cp -r _build/prod/lib/your_plugin/ebin dist/your_plugin/
cp -r priv dist/your_plugin/
cp manifest.json dist/your_plugin/
npm run build --prefix assets
```
For Nix-based deployment, add your plugin to the host's `plugins.json`.
## Licence
TODO: Choose a licence.
Guix packaging assembles the plugin artifact from the compiled BEAM output,
`priv/`, and `manifest.json`. Enable it from the `guix-tribes` channel-side node
configuration.
-34
View File
@@ -1,34 +0,0 @@
defmodule Aether.Plugin do
@moduledoc """
Tribes plugin entry point.
Implements the `Tribes.Plugin` behaviour. This module is referenced by
`entry_module` in manifest.json and is called by the host's PluginManager
during startup.
"""
use Tribes.Plugin.Base, otp_app: :aether
@impl true
def register(context) do
super(context)
|> Map.merge(%{
nav_items: [
%{
label: "Aether",
path: "/aether",
icon: nil,
requires: [],
order: 50
}
],
pages: [
%{
path: "/aether",
live_view: AetherWeb.TimelineLive,
layout: nil
}
],
})
end
end
+31 -2
View File
@@ -1,5 +1,34 @@
defmodule Tribes.Plugins.Aether.Plugin do
@moduledoc false
@moduledoc """
Tribes plugin entry point.
defdelegate register(context), to: Aether.Plugin
Implements the `Tribes.Plugin` behaviour. This module is referenced by
`entry_module` in manifest.json and is called by the host's PluginManager
during startup.
"""
use Tribes.Plugin.Base, otp_app: :aether
@impl true
def register(context) do
super(context)
|> Map.merge(%{
nav_items: [
%{
label: "Aether",
path: "/aether",
icon: nil,
requires: [],
order: 50
}
],
pages: [
%{
path: "/aether",
live_view: AetherWeb.TimelineLive,
layout: nil
}
]
})
end
end
+1 -1
View File
@@ -5,7 +5,7 @@
"entry_module": "Tribes.Plugins.Aether.Plugin",
"host_api": "1",
"otp_app": "aether",
"provides": ["timeline_ui@1"],
"provides": ["aether@1"],
"requires": ["ecto@1", "phoenix@1"],
"enhances_with": [],
"assets": {
+1 -1
View File
@@ -17,7 +17,7 @@ if Code.ensure_loaded?(Tribes.PluginRegistry) and Code.ensure_loaded?(TribesWeb.
test "registers timeline capability and /aether route with host", %{spec: spec} do
assert spec.name == "aether"
assert %{name: "aether"} = Tribes.PluginRegistry.provider!("timeline_ui@1")
assert %{name: "aether"} = Tribes.PluginRegistry.provider!("aether@1")
assert {:ok, "aether", %{path: "/aether"}} =
Tribes.PluginRegistry.page_for_path("/aether/tribe-123")
+3 -1
View File
@@ -1,10 +1,12 @@
defmodule Aether.PluginTest do
use ExUnit.Case, async: true
@plugin Tribes.Plugins.Aether.Plugin
describe "register/1" do
setup do
context = %{pubsub: nil, repo: nil}
%{spec: Aether.Plugin.register(context)}
%{spec: @plugin.register(context)}
end
test "returns plugin name and version", %{spec: spec} do