Skip to content

Commit 25376cc

Browse files
committed
Add sample external plugin collection
1 parent c29d861 commit 25376cc

8 files changed

Lines changed: 134 additions & 0 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,3 +496,6 @@ python scripts/check-python39-compat.py
496496

497497

498498
Release packages can be validated with `scripts/package-release-smoke.sh --output-dir dist/package-release-smoke`.
499+
500+
501+
A sample external plugin collection is available in `examples/external-plugins/`.

docs/guides/creating-plugins.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,6 @@ writes `automax-plugins.lock.json` with the installed package checksum.
115115
directory to `--plugin-path` loads every top-level `.py` file and verified,
116116
lock-matching `.zip` package it contains, so operators can keep a stable plugin
117117
directory instead of listing each package path on every command.
118+
119+
120+
A packaged sample collection is available in `examples/external-plugins/` and exports `company.demo.hello`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!--
2+
Copyright (C) 2026 Marco Fortina
3+
SPDX-License-Identifier: AGPL-3.0-or-later
4+
-->
5+
6+
# Sample external plugin collection
7+
8+
Automax ships a small external plugin example under `examples/external-plugins/`. It validates the external plugin SDK packaging workflow without requiring a private plugin repository.
9+
10+
```bash
11+
automax plugins check examples/external-plugins/company/plugins
12+
automax plugins package examples/external-plugins/company/plugins --output dist/company-demo-plugins.zip --force
13+
automax plugins verify-package dist/company-demo-plugins.zip
14+
automax plugins index dist/company-demo-plugins.zip --output dist/automax-plugins-index.json
15+
automax plugins install-package dist/company-demo-plugins.zip --dest-dir .automax/plugins --force
16+
```
17+
18+
The sample plugin exports `company.demo.hello` and is intentionally deterministic so it can be used in release and packaging smoke checks.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!--
2+
Copyright (C) 2026 Marco Fortina
3+
SPDX-License-Identifier: AGPL-3.0-or-later
4+
-->
5+
6+
# Sample external plugins
7+
8+
This directory contains a minimal external plugin collection used to validate the package, verify, index and install workflows.
9+
10+
```bash
11+
automax plugins check examples/external-plugins/company/plugins
12+
automax plugins package examples/external-plugins/company/plugins --output dist/company-demo-plugins.zip --force
13+
automax plugins verify-package dist/company-demo-plugins.zip
14+
automax plugins index dist/company-demo-plugins.zip --output dist/automax-plugins-index.json
15+
automax plugins install-package dist/company-demo-plugins.zip --dest-dir .automax/plugins --force
16+
```
17+
18+
The sample plugin is `company.demo.hello`.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright (C) 2026 Marco Fortina
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
4+
"""Sample external Automax plugin."""
5+
6+
from __future__ import annotations
7+
8+
from typing import Dict
9+
10+
from automax.core.models import ExecutionContext, PluginResult
11+
from automax.plugins.base import BasePlugin
12+
13+
14+
class CompanyDemoHelloPlugin(BasePlugin):
15+
"""Return a deterministic greeting for package and install smoke tests."""
16+
17+
name = "company.demo.hello"
18+
description = "Return a deterministic external plugin greeting."
19+
category = "company"
20+
required_params = ("message",)
21+
optional_params = ("changed",)
22+
parameter_schema = {
23+
"message": {
24+
"type": "string",
25+
"description": "Greeting message returned by the plugin.",
26+
"non_empty": True,
27+
},
28+
"changed": {
29+
"type": "boolean",
30+
"default": False,
31+
"description": "Whether the plugin should report a change.",
32+
},
33+
}
34+
examples = (
35+
"""- id: hello\n use: company.demo.hello\n with:\n message: hello""",
36+
)
37+
result_fields = {
38+
"message": "Rendered greeting.",
39+
"changed": "Whether the plugin reported a change.",
40+
}
41+
supports_dry_run = True
42+
supports_check_mode = True
43+
44+
def execute(self, params: Dict[str, object], context: ExecutionContext) -> PluginResult:
45+
message = str(params["message"])
46+
changed = bool(params.get("changed", False))
47+
return PluginResult.success(
48+
changed=changed,
49+
message=message,
50+
data={"message": message, "changed": changed},
51+
)

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ nav:
7777
- Package Release Smoke: guides/package-release-smoke.md
7878
- Example Indexes: guides/example-indexes.md
7979
- External Plugin Signing Policy: guides/external-plugin-signing-policy.md
80+
- Sample External Plugin: guides/sample-external-plugin.md
8081
- Run Event Stream: guides/run-event-stream.md
8182
- Publishing Documentation: guides/publishing-docs.md
8283
- Reference:

tests/test_documentation_and_regressions.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,6 +2097,23 @@ def test_runtime_evidence_suite_is_documented_and_optional():
20972097
assert "set AUTOMAX_SSH_HOST and AUTOMAX_SSH_USER" in result.stderr
20982098

20992099

2100+
def test_sample_external_plugin_collection_is_documented():
2101+
docs = "\n".join(
2102+
path.read_text(encoding="utf-8")
2103+
for path in [
2104+
Path("README.md"),
2105+
Path("examples/external-plugins/README.md"),
2106+
Path("docs/guides/sample-external-plugin.md"),
2107+
Path("docs/guides/creating-plugins.md"),
2108+
Path("mkdocs.yml"),
2109+
]
2110+
)
2111+
2112+
assert "company.demo.hello" in docs
2113+
assert "examples/external-plugins" in docs
2114+
assert "Sample External Plugin: guides/sample-external-plugin.md" in docs
2115+
2116+
21002117
def test_external_plugin_signing_policy_is_documented_and_release_wired(tmp_path: Path):
21012118
script = Path("scripts/check-plugin-index-policy.sh").read_text(encoding="utf-8")
21022119
release = Path("scripts/release-check.sh").read_text(encoding="utf-8")

tests/test_engine.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,6 +2083,29 @@ def test_plugins_init_check_package_external_sdk(tmp_path: Path):
20832083
assert validate.exit_code == 0, validate.output
20842084

20852085

2086+
def test_sample_external_plugin_collection_can_be_packaged_and_indexed(tmp_path: Path):
2087+
from automax.plugins.sdk import (
2088+
build_plugin_package_index,
2089+
package_external_plugin_path,
2090+
verify_external_plugin_package,
2091+
)
2092+
2093+
package = tmp_path / "company-demo-plugins.zip"
2094+
2095+
package_external_plugin_path(
2096+
"examples/external-plugins/company/plugins",
2097+
str(package),
2098+
force=True,
2099+
)
2100+
verified = verify_external_plugin_package(str(package))
2101+
indexed = build_plugin_package_index([str(package)])
2102+
2103+
assert verified["ok"] is True
2104+
assert verified["plugins"] == ["company.demo.hello"]
2105+
assert indexed["ok"] is True
2106+
assert indexed["plugin_count"] == 1
2107+
2108+
20862109
def test_plugins_index_writes_verified_package_repository_index(tmp_path: Path):
20872110
plugin_dir = tmp_path / "plugins"
20882111
package_path = tmp_path / "packages" / "demo-plugin.zip"

0 commit comments

Comments
 (0)