You've already forked tribes-plugin-template
Add AGENTS.md with usage_rules-generated guidance, host-aware smoke checks, TypeScript asset defaults, and plugin contract docs. Split Tribes path dependencies for dev/test so templates compile entry-module beams for host loading while preserving runtime-backed tests, and update rename.sh for bridge modules and TS assets.
This commit is contained in:
@@ -16,6 +16,10 @@ devenv.local.nix
|
||||
/node_modules/
|
||||
/assets/node_modules/
|
||||
|
||||
# Built plugin assets
|
||||
/priv/static/*.css
|
||||
/priv/static/*.js
|
||||
|
||||
# Generated on crash
|
||||
erl_crash.dump
|
||||
|
||||
|
||||
@@ -15,11 +15,12 @@ cd your-plugin
|
||||
|
||||
3. Edit `manifest.json` — set description, capabilities, requirements
|
||||
4. Implement your plugin in `lib/your_plugin/plugin.ex`
|
||||
5. Run validation and tests:
|
||||
5. Run validation, smoke checks, and tests:
|
||||
|
||||
```bash
|
||||
mix deps.get
|
||||
mix tribes.plugin.validate
|
||||
scripts/plugin smoke
|
||||
mix test
|
||||
```
|
||||
|
||||
@@ -27,6 +28,7 @@ If you are using the shared `tribes` devenv, run through the local helper instea
|
||||
|
||||
```bash
|
||||
devenv shell -- plugin validate
|
||||
devenv shell -- plugin smoke
|
||||
devenv shell -- plugin test
|
||||
devenv shell -- plugin precommit
|
||||
```
|
||||
@@ -68,6 +70,7 @@ Inside the plugin repo's devenv shell, the `plugin` helper forwards to the host
|
||||
|
||||
```bash
|
||||
plugin validate
|
||||
plugin smoke
|
||||
plugin test
|
||||
plugin precommit
|
||||
```
|
||||
@@ -75,7 +78,7 @@ plugin precommit
|
||||
If your plugin does not need a custom frontend pipeline, you can skip
|
||||
`assets/package.json` and write browser-ready files directly under `assets/js`
|
||||
and `assets/css`; the host dev watcher will copy them into `priv/static` for
|
||||
you in development.
|
||||
you in development. The default template uses TypeScript under `assets/ts`.
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -90,8 +93,10 @@ your_plugin/
|
||||
│ │ └── application.ex # OTP supervision tree (optional)
|
||||
│ └── your_plugin_web/
|
||||
│ └── live/ # LiveView pages
|
||||
├── assets/ # JS/CSS (one bundle per plugin)
|
||||
├── assets/ # TS/CSS (one bundle per plugin)
|
||||
│ ├── ts/ # TypeScript browser entry points
|
||||
│ ├── package.json # Optional build script used by dev + Guix packaging
|
||||
│ ├── tsconfig.json # TypeScript compiler settings
|
||||
│ └── package-lock.json # Optional, recommended for reproducible builds
|
||||
├── priv/
|
||||
│ ├── static/ # Built assets for release
|
||||
@@ -156,6 +161,7 @@ Run them with:
|
||||
|
||||
```bash
|
||||
mix tribes.plugin.validate
|
||||
scripts/plugin smoke
|
||||
mix test
|
||||
```
|
||||
|
||||
@@ -166,7 +172,7 @@ so the plugin test suite reuses the host database and services.
|
||||
|
||||
```bash
|
||||
MIX_ENV=prod mix compile
|
||||
npm run build --prefix assets
|
||||
devenv shell -- npm run build --prefix assets
|
||||
|
||||
mkdir -p dist/your_plugin
|
||||
cp -r _build/prod/lib/your_plugin/ebin dist/your_plugin/
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// Plugin JavaScript entry point.
|
||||
//
|
||||
// This file is served by the host at /plugins-assets/my_plugin/my_plugin.js
|
||||
// and included in the page layout if declared in manifest.json assets.global_js.
|
||||
//
|
||||
// To register LiveView hooks:
|
||||
//
|
||||
// window.TribesPluginHooks = window.TribesPluginHooks || {};
|
||||
// window.TribesPluginHooks["MyPluginHook"] = {
|
||||
// mounted() {
|
||||
// console.log("MyPlugin hook mounted");
|
||||
// }
|
||||
// };
|
||||
|
||||
console.log("my_plugin loaded");
|
||||
Generated
+18
-1
@@ -6,7 +6,24 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "my-plugin-assets",
|
||||
"version": "0.1.0"
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -3,6 +3,10 @@
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"build": "mkdir -p ../priv/static && cp -r css/. ../priv/static && cp -r js/. ../priv/static"
|
||||
"build": "npm run check && mkdir -p ../priv/static && cp -r css/. ../priv/static && tsc --project tsconfig.json",
|
||||
"check": "tsc --project tsconfig.json --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Plugin TypeScript entry point.
|
||||
//
|
||||
// This file is compiled to /priv/static/my_plugin.js, served by the host at
|
||||
// /plugins-assets/my_plugin/my_plugin.js, and included when declared in
|
||||
// manifest.json assets.global_js.
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
TribesPluginHooks?: Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
|
||||
window.TribesPluginHooks = window.TribesPluginHooks ?? {};
|
||||
|
||||
console.info("my_plugin loaded");
|
||||
|
||||
export {};
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "../priv/static",
|
||||
"rootDir": "ts"
|
||||
},
|
||||
"include": ["ts/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Checklist
|
||||
|
||||
## After Renaming
|
||||
|
||||
- Run `./scripts/rename.sh your_plugin YourPlugin`.
|
||||
- Confirm `manifest.json` `name`, `otp_app`, and `entry_module`.
|
||||
- Confirm the bridge module path is `lib/tribes/plugins/your_plugin/plugin.ex`.
|
||||
- Rename asset files and update manifest asset names if needed.
|
||||
- Run `mix deps.get`.
|
||||
- Run `scripts/plugin smoke`.
|
||||
- Run `scripts/plugin test`.
|
||||
|
||||
## Before Committing
|
||||
|
||||
- Run `mix format`.
|
||||
- Run `scripts/plugin precommit`.
|
||||
- If assets changed, run `devenv shell -- npm run build --prefix assets`.
|
||||
- Check `git status --short` for generated files that should not be committed.
|
||||
- Commit with a semantic subject and a useful body unless the patch is minimal.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Development
|
||||
|
||||
## Local Setup
|
||||
|
||||
This template expects a sibling Tribes checkout at `../tribes`.
|
||||
|
||||
```bash
|
||||
mix deps.get
|
||||
devenv shell -- plugin test
|
||||
```
|
||||
|
||||
To load the plugin in the host during development:
|
||||
|
||||
```bash
|
||||
cd ../tribes
|
||||
ln -s ../tribes-plugin-template plugins/my_plugin
|
||||
iex --sname dev -S mix phx.server
|
||||
```
|
||||
|
||||
Set `TRIBES_HOST_ROOT=/path/to/tribes` if your host checkout is not a sibling.
|
||||
|
||||
## Command Wrapper
|
||||
|
||||
Use `scripts/plugin` for host-aware workflows:
|
||||
|
||||
```bash
|
||||
scripts/plugin validate
|
||||
scripts/plugin test
|
||||
scripts/plugin precommit
|
||||
scripts/plugin smoke
|
||||
```
|
||||
|
||||
Inside the template devenv shell, the `plugin` command is available and forwards
|
||||
through the same wrapper.
|
||||
|
||||
## Assets
|
||||
|
||||
Browser code lives in `assets/ts` and is compiled to `priv/static`:
|
||||
|
||||
```bash
|
||||
devenv shell -- npm run build --prefix assets
|
||||
```
|
||||
|
||||
Use the wrapper form for installs too:
|
||||
|
||||
```bash
|
||||
devenv shell -- npm install --prefix assets
|
||||
```
|
||||
@@ -0,0 +1,43 @@
|
||||
# Plugin Contract
|
||||
|
||||
## Manifest
|
||||
|
||||
`manifest.json` is the runtime contract consumed by Tribes:
|
||||
|
||||
- `name` and `otp_app` must match the Mix application name.
|
||||
- `entry_module` must be a loadable `Tribes.Plugins.*.Plugin` module.
|
||||
- `provides` declares capabilities exported by the plugin.
|
||||
- `requires` declares hard host capabilities.
|
||||
- `enhances_with` declares optional host capabilities.
|
||||
- `migrations` should be `true` when `priv/repo/migrations` contains plugin migrations.
|
||||
- `children` should be `true` when the plugin starts its own supervision tree.
|
||||
|
||||
## Entry Modules
|
||||
|
||||
The template uses a thin host bridge:
|
||||
|
||||
```elixir
|
||||
defmodule Tribes.Plugins.MyPlugin.Plugin do
|
||||
defdelegate register(context), to: MyPlugin.Plugin
|
||||
end
|
||||
```
|
||||
|
||||
Keep plugin implementation code in `MyPlugin.Plugin`. Keep the bridge stable so
|
||||
the host plugin manager can load it from the plugin build output.
|
||||
|
||||
## Host Dependencies
|
||||
|
||||
The template intentionally splits the `:tribes` path dependency by environment:
|
||||
|
||||
- In `:dev`, `:tribes` is compile-only. This lets `mix compile` create the
|
||||
entry-module beam expected by the host plugin manager without starting a
|
||||
nested Tribes application.
|
||||
- In `:test`, `:tribes` is runtime-enabled. Host-backed tests need the real
|
||||
repository, endpoint pipeline, and plugin contract helpers.
|
||||
|
||||
## Ash Resources
|
||||
|
||||
For cluster-synced plugin data, add Ash resources under the plugin namespace and
|
||||
use `extensions: [AshNostrSync]` only for data that should replicate across the
|
||||
cluster. Prefer one UUID primary key per synced resource. Document any local-only
|
||||
tables, retention policies, or side effects in plugin docs.
|
||||
@@ -9,7 +9,8 @@ defmodule MyPlugin.MixProject do
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
aliases: aliases()
|
||||
aliases: aliases(),
|
||||
usage_rules: usage_rules()
|
||||
]
|
||||
end
|
||||
|
||||
@@ -37,12 +38,24 @@ defmodule MyPlugin.MixProject do
|
||||
# For CI or standalone development, this can be replaced with a published
|
||||
# package once tribes_plugin_api is released.
|
||||
{:tribes_plugin_api, path: "../tribes/tribes_plugin_api", runtime: false},
|
||||
{:tribes, path: "../tribes", only: :test},
|
||||
{:ash, "~> 3.0", only: [:dev, :test], runtime: false},
|
||||
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
||||
{:lazy_html, ">= 0.1.0", only: :test},
|
||||
{:phoenix, "~> 1.8"},
|
||||
{:phoenix_html, "~> 4.1"},
|
||||
{:phoenix_live_view, "~> 1.1.0"}
|
||||
{:phoenix_live_view, "~> 1.1.0"},
|
||||
{:usage_rules, "~> 1.2", only: :dev}
|
||||
] ++ tribes_deps(Mix.env())
|
||||
end
|
||||
|
||||
defp tribes_deps(:dev), do: [{:tribes, path: "../tribes", only: :dev, runtime: false}]
|
||||
defp tribes_deps(:test), do: [{:tribes, path: "../tribes", only: :test}]
|
||||
defp tribes_deps(_), do: []
|
||||
|
||||
defp usage_rules do
|
||||
[
|
||||
file: "AGENTS.md",
|
||||
usage_rules: [:elixir, :otp, :phoenix, ~r/^phoenix_/, :ash]
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -43,8 +43,10 @@
|
||||
"finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"},
|
||||
"fine": {:hex, :fine, "0.1.6", "4bf7151493443c454aac9f2fa2f34f5fefd0346a83fb5586a016c4a135c63247", [:mix], [], "hexpm", "5638eb4495488e885ebec167fa57973e5c35e1a50c344eb7666c90ec1c4e3b12"},
|
||||
"gettext": {:hex, :gettext, "1.0.2", "5457e1fd3f4abe47b0e13ff85086aabae760497a3497909b8473e0acee57673b", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "eab805501886802071ad290714515c8c4a17196ea76e5afc9d06ca85fb1bfeb3"},
|
||||
"glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
|
||||
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||
"igniter": {:hex, :igniter, "0.7.9", "8c573440b8127fd80be8220fb197e7422317a81072054fcc0b336029f035a416", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "123513d09f3af149db851aad8492b5b49f861d2c466a72031b2a0cbd9f45526f"},
|
||||
"iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"},
|
||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||
"joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"},
|
||||
@@ -57,6 +59,7 @@
|
||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
|
||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||
"owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"},
|
||||
"parrhesia": {:hex, :parrhesia, "0.12.0", "67f33b62a6d7d32ce8d801e3dd09f3e8f46d82a21e9b15dfda6a161d2c7f7e09", [:mix], [{:bandit, "~> 1.5", [hex: :bandit, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.12", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:lib_secp256k1, "~> 0.7", [hex: :lib_secp256k1, repo: "hexpm", optional: false]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus, "~> 1.1", [hex: :telemetry_metrics_prometheus, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 1.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5", [hex: :websock_adapter, repo: "hexpm", optional: false]}, {:websockex, "~> 0.4", [hex: :websockex, repo: "hexpm", optional: false]}], "hexpm", "e91f757972746c73f888706cb084ba745ebf1a602a0f2f6a7c380555c6066cc3"},
|
||||
"phoenix": {:hex, :phoenix, "1.8.5", "919db335247e6d4891764dc3063415b0d2457641c5f9b3751b5df03d8e20bbcf", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "83b2bb125127e02e9f475c8e3e92736325b5b01b0b9b05407bcb4083b7a32485"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.7.0", "75c4b9dfb3efdc42aec2bd5f8bccd978aca0651dbcbc7a3f362ea5d9d43153c6", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "1d75011e4254cb4ddf823e81823a9629559a1be93b4321a6a5f11a5306fbf4cc"},
|
||||
@@ -75,8 +78,11 @@
|
||||
"ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"},
|
||||
"reactor": {:hex, :reactor, "1.0.1", "ca3b5cf3c04ec8441e67ea2625d0294939822060b1bfd00ffdaaf75b7682d991", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, ">= 2.3.3 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}, {:ymlr, "~> 5.0", [hex: :ymlr, repo: "hexpm", optional: false]}], "hexpm", "3497db2b204c9a3cabdaf1b26d2405df1dfbb138ce0ce50e616e9db19fec0043"},
|
||||
"req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},
|
||||
"rewrite": {:hex, :rewrite, "1.3.0", "67448ba7975690b35ba7e7f35717efcce317dbd5963cb0577aa7325c1923121a", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "d111ac7ff3a58a802ef4f193bbd1831e00a9c57b33276e5068e8390a212714a5"},
|
||||
"slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"},
|
||||
"sourceror": {:hex, :sourceror, "1.12.0", "da354c5f35aad3cc1132f5d5b0d8437d865e2661c263260480bab51b5eedb437", [:mix], [], "hexpm", "755703683bd014ebcd5de9acc24b68fb874a660a568d1d63f8f98cd8a6ef9cd0"},
|
||||
"spark": {:hex, :spark, "2.6.1", "b0100216d3883c6a281cb2434af45afbd808695aadb034923cbaf7d8a2ba46ab", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "77bbefa5263bb6b70e1195bc0fc662ddb8ef5937a356a77ae072e56983ad13f0"},
|
||||
"spitfire": {:hex, :spitfire, "0.3.11", "79dfcb033762470de472c1c26ea2b4e3aca74700c685dbffd9a13466272c323d", [:mix], [], "hexpm", "eb6e2dadf63214e8bfe65ca9788cef2b03b01027365d78d3c0e3d9ebd3d5b7b4"},
|
||||
"splode": {:hex, :splode, "0.3.1", "9843c54f84f71b7833fec3f0be06c3cfb5be6b35960ee195ea4fad84b1c25030", [:mix], [], "hexpm", "8f2309b6ec2ecbb01435656429ed1d9ed04ba28797a3280c3b0d1217018ecfbd"},
|
||||
"stream_data": {:hex, :stream_data, "1.3.0", "bde37905530aff386dea1ddd86ecbf00e6642dc074ceffc10b7d4e41dfd6aac9", [:mix], [], "hexpm", "3cc552e286e817dca43c98044c706eec9318083a1480c52ae2688b08e2936e3c"},
|
||||
"swoosh": {:hex, :swoosh, "1.25.0", "d60dcba6d1ce538b1994f8712a3d55bc9519ffba4654cc4665a75683881d11dd", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c59db3d838b595b95954a3d0a13782e56881cecfe7ba7b793b1a1a6775273a6e"},
|
||||
@@ -85,8 +91,10 @@
|
||||
"telemetry_metrics_prometheus": {:hex, :telemetry_metrics_prometheus, "1.1.0", "1cc23e932c1ef9aa3b91db257ead31ea58d53229d407e059b29bb962c1505a13", [:mix], [{:plug_cowboy, "~> 2.1", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}], "hexpm", "d43b3659b3244da44fe0275b717701542365d4519b79d9ce895b9719c1ce4d26"},
|
||||
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.1", "c9755987d7b959b557084e6990990cb96a50d6482c683fb9622a63837f3cd3d8", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5e2c599da4983c4f88a33e9571f1458bf98b0cf6ba930f1dc3a6e8cf45d5afb6"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
|
||||
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
|
||||
"usage_rules": {:hex, :usage_rules, "1.2.6", "a7b3f8d6e5d265701139d5714749c37c54bb82230a4c51ec54a12a1e4769b9d1", [:mix], [{:igniter, ">= 0.6.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "608411b9876a16a9d62a427dbaf42faf458e4cd0a508b3bd7e5ee71502073582"},
|
||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||
"websock_adapter": {:hex, :websock_adapter, "0.5.9", "43dc3ba6d89ef5dec5b1d0a39698436a1e856d000d84bf31a3149862b01a287f", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5534d5c9adad3c18a0f58a9371220d75a803bf0b9a3d87e6fe072faaeed76a08"},
|
||||
"websockex": {:hex, :websockex, "0.5.1", "9de28d37bbe34f371eb46e29b79c94c94fff79f93c960d842fbf447253558eb4", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8ef39576ed56bc3804c9cd8626f8b5d6b5721848d2726c0ccd4f05385a3c9f14"},
|
||||
|
||||
+53
-1
@@ -7,6 +7,7 @@ Usage:
|
||||
plugin validate
|
||||
plugin test [mix test args...]
|
||||
plugin precommit [mix precommit args...]
|
||||
plugin smoke
|
||||
plugin shell
|
||||
EOF
|
||||
}
|
||||
@@ -22,6 +23,52 @@ host_root="${TRIBES_HOST_ROOT:-$plugin_root/../tribes}"
|
||||
host_root="$(cd "$host_root" && pwd -P)"
|
||||
host_script="$host_root/scripts/plugin"
|
||||
|
||||
json_field() {
|
||||
local field="$1"
|
||||
|
||||
FIELD="$field" mix run --no-start -e '
|
||||
field = System.fetch_env!("FIELD")
|
||||
manifest = "manifest.json" |> File.read!() |> Jason.decode!()
|
||||
value = Map.fetch!(manifest, field)
|
||||
IO.write(value)
|
||||
'
|
||||
}
|
||||
|
||||
run_smoke() {
|
||||
cd "$plugin_root"
|
||||
|
||||
mix compile
|
||||
|
||||
local otp_app
|
||||
local entry_module
|
||||
otp_app="$(json_field otp_app)"
|
||||
entry_module="$(json_field entry_module)"
|
||||
|
||||
local beam_path="_build/dev/lib/$otp_app/ebin/Elixir.$entry_module.beam"
|
||||
[[ -f "$beam_path" ]] || fail "expected entry module beam at $beam_path"
|
||||
|
||||
cd "$host_root"
|
||||
PLUGIN_ROOT="$plugin_root" ENTRY_MODULE="$entry_module" mix run --no-start -e '
|
||||
plugin_root = System.fetch_env!("PLUGIN_ROOT")
|
||||
entry_module = System.fetch_env!("ENTRY_MODULE")
|
||||
|
||||
plugin_root
|
||||
|> Path.join("_build/dev/lib/*/ebin")
|
||||
|> Path.wildcard()
|
||||
|> Enum.each(&(:code.add_patha(String.to_charlist(&1))))
|
||||
|
||||
module = String.to_atom("Elixir." <> entry_module)
|
||||
|
||||
case Code.ensure_loaded(module) do
|
||||
{:module, ^module} ->
|
||||
IO.puts("Loaded #{entry_module}")
|
||||
|
||||
other ->
|
||||
raise "failed to load #{entry_module}: #{inspect(other)}"
|
||||
end
|
||||
'
|
||||
}
|
||||
|
||||
command_name="${1:-}"
|
||||
if [[ -z "$command_name" ]]; then
|
||||
usage
|
||||
@@ -30,7 +77,7 @@ fi
|
||||
shift
|
||||
|
||||
case "$command_name" in
|
||||
validate | test | precommit | shell) ;;
|
||||
validate | test | precommit | smoke | shell) ;;
|
||||
-h | --help | help)
|
||||
usage
|
||||
exit 0
|
||||
@@ -43,6 +90,11 @@ esac
|
||||
|
||||
[[ -x "$host_script" || -f "$host_script" ]] || fail "expected host plugin script at $host_script"
|
||||
|
||||
if [[ "$command_name" == "smoke" ]]; then
|
||||
run_smoke "$@"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${DEVENV_ROOT:-}" == "$plugin_root" ]]; then
|
||||
command -v devenv >/dev/null 2>&1 || fail "devenv is required when running from the plugin devenv shell"
|
||||
cd "$host_root"
|
||||
|
||||
+7
-4
@@ -44,6 +44,9 @@ fi
|
||||
if [ -d "test/my_plugin" ]; then
|
||||
mv "test/my_plugin" "test/$SNAKE"
|
||||
fi
|
||||
if [ -d "lib/tribes/plugins/my_plugin" ]; then
|
||||
mv "lib/tribes/plugins/my_plugin" "lib/tribes/plugins/$SNAKE"
|
||||
fi
|
||||
|
||||
# Replace in all text files (portable across GNU/BSD sed)
|
||||
sed_in_place() {
|
||||
@@ -67,13 +70,13 @@ sed_in_place() {
|
||||
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' \) \
|
||||
find . \( -name ".devenv" -o -name ".git" -o -name "deps" -o -name "node_modules" \) -prune -o -type f \
|
||||
\( -name '*.ex' -o -name '*.exs' -o -name '*.json' -o -name '*.js' -o -name '*.ts' -o -name '*.css' -o -name '*.md' -o -name '*.yml' -o -name '*.yaml' -o -name '.formatter.exs' \) \
|
||||
-print0
|
||||
)
|
||||
|
||||
# Rename asset files
|
||||
for ext in js css; do
|
||||
for ext in js ts css; do
|
||||
if [ -f "assets/$ext/my_plugin.$ext" ]; then
|
||||
mv "assets/$ext/my_plugin.$ext" "assets/$ext/$SNAKE.$ext"
|
||||
fi
|
||||
@@ -84,4 +87,4 @@ done
|
||||
|
||||
echo "Done. Review the changes, then:"
|
||||
echo " 1. Edit manifest.json — set description, capabilities"
|
||||
echo " 2. Run: mix deps.get && mix test"
|
||||
echo " 2. Run: mix deps.get && scripts/plugin smoke && scripts/plugin test"
|
||||
|
||||
Reference in New Issue
Block a user