You've already forked tribes-plugin-aether
forked from tribes/tribes-plugin-template
Align aether plugin entrypoint and docs
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
# Node
|
||||
/node_modules/
|
||||
/assets/node_modules/
|
||||
/priv/static/*
|
||||
!/priv/static/.gitkeep
|
||||
|
||||
# Generated on crash
|
||||
erl_crash.dump
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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": {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user