template: make rename portable and add explicit otp_app dev flow

This commit is contained in:
2026-04-04 19:45:32 +02:00
parent 647d5537ff
commit 54d9bdd99c
5 changed files with 70 additions and 23 deletions

View File

@@ -27,15 +27,19 @@ mix test
For local development alongside a Tribes checkout: For local development alongside a Tribes checkout:
```bash ```bash
# Build plugin code once (host loads BEAM from _build/dev/lib/<otp_app>/ebin)
cd /path/to/your-plugin
mix compile
# Symlink into the host plugins directory # Symlink into the host plugins directory
cd /path/to/tribes cd /path/to/tribes
ln -s /path/to/your-plugin plugins/your_plugin ln -s /path/to/your-plugin plugins/your_plugin
# Start Tribes dev server — your plugin loads automatically # Start Tribes dev server
iex --sname dev -S mix phx.server iex --sname dev -S mix phx.server
``` ```
Edit your plugin source. Phoenix code reloader picks up changes. When you change plugin Elixir code, re-run `mix compile` in the plugin repo.
## Project Structure ## Project Structure
@@ -63,6 +67,9 @@ your_plugin/
```json ```json
{ {
"name": "your_plugin", "name": "your_plugin",
"entry_module": "YourPlugin.Plugin",
"host_api": "1",
"otp_app": "your_plugin",
"provides": ["some_capability@1"], "provides": ["some_capability@1"],
"requires": ["ecto@1"], "requires": ["ecto@1"],
"enhances_with": ["inference@1"] "enhances_with": ["inference@1"]

View File

@@ -58,24 +58,44 @@ defmodule MyPlugin.Plugin do
end end
defp manifest_path do defp manifest_path do
# In a release, manifest.json sits alongside ebin/ in the plugin directory. project_manifest = Path.join(__DIR__, "../../manifest.json") |> Path.expand()
# In dev mode (path dep), it's at the project root.
candidates =
case :code.priv_dir(:my_plugin) do case :code.priv_dir(:my_plugin) do
{:error, :bad_name} -> {:error, :bad_name} ->
# Dev mode fallback: relative to project root [project_manifest]
Path.join(__DIR__, "../../../manifest.json") |> Path.expand()
priv_dir -> priv_dir ->
priv_dir |> to_string() |> Path.join("../manifest.json") |> Path.expand() priv_dir = to_string(priv_dir)
[
Path.join(priv_dir, "../manifest.json") |> Path.expand(),
project_manifest
]
end end
first_existing_path(candidates) || project_manifest
end end
defp migrations_path(manifest) do defp migrations_path(manifest) do
if manifest["migrations"] do if manifest["migrations"] do
candidates =
case :code.priv_dir(:my_plugin) do case :code.priv_dir(:my_plugin) do
{:error, :bad_name} -> nil {:error, :bad_name} ->
priv_dir -> priv_dir |> to_string() |> Path.join("repo/migrations") [Path.join(__DIR__, "../../priv/repo/migrations") |> Path.expand()]
priv_dir ->
[
Path.join(to_string(priv_dir), "repo/migrations") |> Path.expand(),
Path.join(__DIR__, "../../priv/repo/migrations") |> Path.expand()
]
end
first_existing_path(candidates)
end end
end end
defp first_existing_path(paths) do
Enum.find(paths, &File.exists?/1)
end end
end end

View File

@@ -4,6 +4,7 @@
"description": "TODO: Describe what this plugin does", "description": "TODO: Describe what this plugin does",
"entry_module": "MyPlugin.Plugin", "entry_module": "MyPlugin.Plugin",
"host_api": "1", "host_api": "1",
"otp_app": "my_plugin",
"provides": [], "provides": [],
"requires": ["ecto@1"], "requires": ["ecto@1"],
"enhances_with": [], "enhances_with": [],

View File

@@ -29,7 +29,7 @@ defmodule MyPlugin.MixProject do
# Host dependency — provides Tribes.Plugin behaviour, types, and test helpers. # Host dependency — provides Tribes.Plugin behaviour, types, and test helpers.
# #
# For local development alongside a tribes checkout: # For local development alongside a tribes checkout:
{:tribes, path: "../tribes", only: [:dev, :test]}, {:tribes, path: "../tribes", only: [:dev, :test], runtime: false},
# #
# For CI or standalone development (when not co-located with tribes): # For CI or standalone development (when not co-located with tribes):
# {:tribes, github: "your-org/tribes", branch: "master", only: [:dev, :test]}, # {:tribes, github: "your-org/tribes", branch: "master", only: [:dev, :test]},

View File

@@ -45,13 +45,32 @@ if [ -d "test/my_plugin" ]; then
mv "test/my_plugin" "test/$SNAKE" mv "test/my_plugin" "test/$SNAKE"
fi fi
# Replace in all text files # Replace in all text files (portable across GNU/BSD sed)
find . -type f \( -name '*.ex' -o -name '*.exs' -o -name '*.json' -o -name '*.js' -o -name '*.css' -o -name '*.md' -o -name '*.yml' -o -name '*.yaml' -o -name '.formatter.exs' \) -exec \ sed_in_place() {
file=$1
if sed --version >/dev/null 2>&1; then
sed -i \
-e "s/my_plugin/$SNAKE/g" \
-e "s/MyPlugin/$MODULE/g" \
-e "s/my-plugin/$SNAKE/g" \
"$file"
else
sed -i '' \ sed -i '' \
-e "s/my_plugin/$SNAKE/g" \ -e "s/my_plugin/$SNAKE/g" \
-e "s/MyPlugin/$MODULE/g" \ -e "s/MyPlugin/$MODULE/g" \
-e "s/my-plugin/$SNAKE/g" \ -e "s/my-plugin/$SNAKE/g" \
{} + "$file"
fi
}
while IFS= read -r -d '' file; do
sed_in_place "$file"
done < <(
find . -type f \
\( -name '*.ex' -o -name '*.exs' -o -name '*.json' -o -name '*.js' -o -name '*.css' -o -name '*.md' -o -name '*.yml' -o -name '*.yaml' -o -name '.formatter.exs' \) \
-print0
)
# Rename asset files # Rename asset files
for ext in js css; do for ext in js css; do