moving from "min_noctalia" to "plugin_api"

This commit is contained in:
Lemmy
2026-07-16 09:30:13 -04:00
parent 759ceb0ecb
commit ef085ecebf
19 changed files with 38 additions and 26 deletions
+3 -2
View File
@@ -25,7 +25,8 @@
- [ ] Tested on Hyprland - [ ] Tested on Hyprland
- [ ] Tested on Sway - [ ] Tested on Sway
- [ ] Tested on another compositor: - [ ] Tested on another compositor:
- **Noctalia version tested against:** <!-- must be >= min_noctalia in plugin.toml --> - **Noctalia version tested against:**
- **Plugin API level:** <!-- must match plugin_api in plugin.toml -->
## Screenshots / Videos ## Screenshots / Videos
@@ -39,7 +40,7 @@
[README template](https://github.com/noctalia-dev/community-plugins/blob/main/README_TEMPLATE.md), documents [README template](https://github.com/noctalia-dev/community-plugins/blob/main/README_TEMPLATE.md), documents
every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable. every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable.
- [ ] I created `thumbnail.webp` with the [thumbnail generator](https://assets.noctalia.dev/plugins/thumbnail-generator.html). - [ ] I created `thumbnail.webp` with the [thumbnail generator](https://assets.noctalia.dev/plugins/thumbnail-generator.html).
- [ ] `version` follows semver and is bumped in this PR; `min_noctalia` is the version I tested against. - [ ] `version` follows semver and is bumped in this PR; `plugin_api` is the oldest API level this plugin requires.
- [ ] Every non-English translation in this PR uses a locale supported by Noctalia core, and I can read, write, and - [ ] Every non-English translation in this PR uses a locale supported by Noctalia core, and I can read, write, and
understand that language well enough to review and maintain it (no unreviewed machine/LLM translations). understand that language well enough to review and maintain it (no unreviewed machine/LLM translations).
- [ ] I did not edit `catalog.toml`; CI generates it. - [ ] I did not edit `catalog.toml`; CI generates it.
+8 -2
View File
@@ -10,7 +10,7 @@ from pathlib import Path
ROOT_DIR = Path(__file__).resolve().parents[2] ROOT_DIR = Path(__file__).resolve().parents[2]
CATALOG_PATH = ROOT_DIR / "catalog.toml" CATALOG_PATH = ROOT_DIR / "catalog.toml"
REQUIRED_FIELDS = ("id", "name", "version", "author", "min_noctalia", "tags") REQUIRED_FIELDS = ("id", "name", "version", "author", "plugin_api", "tags")
OPTIONAL_STRING_FIELDS = ("license", "icon", "description") OPTIONAL_STRING_FIELDS = ("license", "icon", "description")
OPTIONAL_BOOL_FIELDS = ("deprecated",) OPTIONAL_BOOL_FIELDS = ("deprecated",)
@@ -24,6 +24,12 @@ def load_plugin_manifest(path: Path) -> dict:
missing_fields = ", ".join(missing) missing_fields = ", ".join(missing)
raise ValueError(f"{path.relative_to(ROOT_DIR)} is missing: {missing_fields}") raise ValueError(f"{path.relative_to(ROOT_DIR)} is missing: {missing_fields}")
plugin_api = manifest["plugin_api"]
if not isinstance(plugin_api, int) or isinstance(plugin_api, bool) or plugin_api <= 0:
raise ValueError(
f"{path.relative_to(ROOT_DIR)} has invalid plugin_api; expected a positive integer"
)
if not isinstance(manifest["tags"], list) or not all( if not isinstance(manifest["tags"], list) or not all(
isinstance(tag, str) for tag in manifest["tags"] isinstance(tag, str) for tag in manifest["tags"]
): ):
@@ -109,7 +115,7 @@ def render_catalog(plugins: list[dict]) -> str:
lines.append(f"deprecated = {toml_bool(plugin['deprecated'])}") lines.append(f"deprecated = {toml_bool(plugin['deprecated'])}")
lines.extend( lines.extend(
[ [
f"min_noctalia = {toml_string(plugin['min_noctalia'])}", f"plugin_api = {plugin['plugin_api']}",
"tags = [" "tags = ["
+ ", ".join(toml_string(tag) for tag in plugin["tags"]) + ", ".join(toml_string(tag) for tag in plugin["tags"])
+ "]", + "]",
+10 -5
View File
@@ -85,7 +85,6 @@ ROOT_STRING_FIELDS = (
"id", "id",
"name", "name",
"version", "version",
"min_noctalia",
"author", "author",
"license", "license",
"icon", "icon",
@@ -112,6 +111,7 @@ PANEL_POSITIONS = {
ROOT_FIELDS = set(ROOT_STRING_FIELDS) | set(ROOT_ARRAY_FIELDS) | set(ENTRY_TYPES) | { ROOT_FIELDS = set(ROOT_STRING_FIELDS) | set(ROOT_ARRAY_FIELDS) | set(ENTRY_TYPES) | {
"setting", "setting",
"deprecated", "deprecated",
"plugin_api",
} }
BASE_ENTRY_FIELDS = {"id", "entry"} BASE_ENTRY_FIELDS = {"id", "entry"}
ENTRY_FIELDS = { ENTRY_FIELDS = {
@@ -521,6 +521,12 @@ class Validator:
if "deprecated" in manifest and not isinstance(manifest["deprecated"], bool): if "deprecated" in manifest and not isinstance(manifest["deprecated"], bool):
self.add_error(manifest_path, "root field 'deprecated' must be a bool") self.add_error(manifest_path, "root field 'deprecated' must be a bool")
plugin_api = manifest.get("plugin_api")
if "plugin_api" not in manifest:
self.add_error(manifest_path, "missing required root field 'plugin_api'")
elif not is_int(plugin_api) or plugin_api <= 0:
self.add_error(manifest_path, "root field 'plugin_api' must be a positive integer")
# A plugin id is "<author>/<plugin>", and the part after the "/" is the directory # A plugin id is "<author>/<plugin>", and the part after the "/" is the directory
# it lives in - so a folder name is taken once for the whole repo. # it lives in - so a folder name is taken once for the whole repo.
folder = manifest_path.parent.name folder = manifest_path.parent.name
@@ -539,10 +545,9 @@ class Validator:
if folder in RESERVED_NAMES: if folder in RESERVED_NAMES:
self.add_error(manifest_path, f"'{folder}' is a reserved name and cannot be a plugin directory") self.add_error(manifest_path, f"'{folder}' is a reserved name and cannot be a plugin directory")
for field in ("version", "min_noctalia"): version = manifest.get("version")
value = manifest.get(field) if is_non_empty_string(version) and not SEMVER_RE.fullmatch(version):
if is_non_empty_string(value) and not SEMVER_RE.fullmatch(value): self.add_error(manifest_path, "root field 'version' must use MAJOR.MINOR.PATCH")
self.add_error(manifest_path, f"root field '{field}' must use MAJOR.MINOR.PATCH")
def validate_entry_path(self, manifest_path: Path, context: str, plugin_dir: Path, value: Any) -> None: def validate_entry_path(self, manifest_path: Path, context: str, plugin_dir: Path, value: Any) -> None:
if not is_non_empty_string(value): if not is_non_empty_string(value):
+2 -2
View File
@@ -147,8 +147,8 @@ Open a PR against `main`. CI validates your manifest, entry scripts, required fi
- **One plugin per PR.** - **One plugin per PR.**
- The directory name matches the part of `id` after the `/` in `plugin.toml` **exactly**. - The directory name matches the part of `id` after the `/` in `plugin.toml` **exactly**.
- `version` is semver and gets bumped on every change to the plugin. - `version` is semver and gets bumped on every change to the plugin.
- `min_noctalia` is the Noctalia version you actually tested against. Users on older builds are then told the plugin - `plugin_api` is the oldest Noctalia plugin API level the plugin requires. Use the current documented level for a new
needs an upgrade instead of getting a broken install. plugin, and increase it only when the plugin adopts a capability from a newer API level.
- `description` is concise catalog copy, limited to 120 characters. Put feature details in the plugin's README. - `description` is concise catalog copy, limited to 120 characters. Put feature details in the plugin's README.
- `license` is set in `plugin.toml`. You keep the copyright on your plugin; if it is not MIT, put a `LICENSE` file in - `license` is set in `plugin.toml`. You keep the copyright on your plugin; if it is not MIT, put a `LICENSE` file in
your plugin directory. There is no repo-wide license covering contributed plugins. your plugin directory. There is no repo-wide license covering contributed plugins.
+1 -1
View File
@@ -4,7 +4,7 @@ name = "Color Picker"
description = "Pick a color from your screen with hyprpicker." description = "Pick a color from your screen with hyprpicker."
license = "MIT" license = "MIT"
version = "1.0.2" version = "1.0.2"
min_noctalia = "5.0.0" plugin_api = 3
icon = "palette" icon = "palette"
dependencies = ["hyprpicker"] dependencies = ["hyprpicker"]
tags = ["theming", "utility", "bar", "panel"] tags = ["theming", "utility", "bar", "panel"]
+1 -1
View File
@@ -1,7 +1,7 @@
id = "nzlov/daily-wallpaper" id = "nzlov/daily-wallpaper"
name = "Daily Wallpaper" name = "Daily Wallpaper"
version = "1.0.0" version = "1.0.0"
min_noctalia = "5.0.0" plugin_api = 3
author = "nzlov" author = "nzlov"
license = "MIT" license = "MIT"
dependencies = [] dependencies = []
+1 -1
View File
@@ -1,7 +1,7 @@
id = "lux/ideapad-conservation-mode" id = "lux/ideapad-conservation-mode"
name = "IdeaPad Conservation Mode" name = "IdeaPad Conservation Mode"
version = "0.3.0" version = "0.3.0"
min_noctalia = "5.0.0" plugin_api = 3
author = "lux" author = "lux"
license = "MIT" license = "MIT"
tags = ["hardware", "shortcut", "system"] tags = ["hardware", "shortcut", "system"]
+1 -1
View File
@@ -1,7 +1,7 @@
id = "nikolaj-zwergius/iio_lock" id = "nikolaj-zwergius/iio_lock"
name = "iio-lock" name = "iio-lock"
version = "1.0.0" version = "1.0.0"
min_noctalia = "5.0.0" plugin_api = 3
author = "nikolaj-zwergius" author = "nikolaj-zwergius"
license = "MIT" license = "MIT"
dependencies = ["pgrep","pkill","iio-hyprland","iio-sway","hyprctl","swaymsg"] dependencies = ["pgrep","pkill","iio-hyprland","iio-sway","hyprctl","swaymsg"]
+1 -1
View File
@@ -1,7 +1,7 @@
id = "gambled23/mangowm-keymode" id = "gambled23/mangowm-keymode"
name = "Mangowm Keymode" name = "Mangowm Keymode"
version = "1.0.1" version = "1.0.1"
min_noctalia = "5.0.0" plugin_api = 3
author = "gambled23" author = "gambled23"
license = "MIT" license = "MIT"
dependencies = ["mmsg"] dependencies = ["mmsg"]
+1 -1
View File
@@ -1,7 +1,7 @@
id = "8bury/mini-docker" id = "8bury/mini-docker"
name = "Mini Docker" name = "Mini Docker"
version = "1.0.3" version = "1.0.3"
min_noctalia = "5.0.0" plugin_api = 3
author = "8bury" author = "8bury"
license = "MIT" license = "MIT"
icon = "brand-docker" icon = "brand-docker"
+1 -1
View File
@@ -1,7 +1,7 @@
id = "avivbintangaringga/nix-monitor" id = "avivbintangaringga/nix-monitor"
name = "Nix Monitor" name = "Nix Monitor"
version = "1.1.1" version = "1.1.1"
min_noctalia = "5.0.0" plugin_api = 3
icon = "package" icon = "package"
author = "avivbintangaringga" author = "avivbintangaringga"
description = "Nixpkgs update monitor" description = "Nixpkgs update monitor"
+1 -1
View File
@@ -3,7 +3,7 @@
id = "rxtsel/portctl" id = "rxtsel/portctl"
name = "Portctl" name = "Portctl"
version = "0.2.0" version = "0.2.0"
min_noctalia = "5.0.0" plugin_api = 3
author = "Cristhian Melo" author = "Cristhian Melo"
license = "MIT" license = "MIT"
icon = "plug" icon = "plug"
+1 -1
View File
@@ -1,7 +1,7 @@
id = "radimous/prismlauncher-instances" id = "radimous/prismlauncher-instances"
name = "PrismLauncher Instances" name = "PrismLauncher Instances"
version = "1.0.0" version = "1.0.0"
min_noctalia = "5.0.0" plugin_api = 3
author = "radimous" author = "radimous"
license = "MIT" license = "MIT"
dependencies = ["prismlauncher"] dependencies = ["prismlauncher"]
+1 -1
View File
@@ -1,7 +1,7 @@
id = "lucasoe/proton-pass" id = "lucasoe/proton-pass"
name = "Proton Pass" name = "Proton Pass"
version = "0.1.0" version = "0.1.0"
min_noctalia = "5.0.0" plugin_api = 3
author = "LucasOe" author = "LucasOe"
license = "MIT" license = "MIT"
icon = "lock" icon = "lock"
+1 -1
View File
@@ -14,7 +14,7 @@
id = "whyoolw/sharednd" id = "whyoolw/sharednd"
name = "ShareDND" name = "ShareDND"
version = "1.1.1" version = "1.1.1"
min_noctalia = "5.0.0" plugin_api = 3
author = "whyoolw" author = "whyoolw"
license = "MIT" license = "MIT"
dependencies = ["niri"] dependencies = ["niri"]
+1 -1
View File
@@ -2,7 +2,7 @@ id = "joshuaslate/shelly"
name = "Shelly" name = "Shelly"
icon = "package" icon = "package"
version = "1.0.1" version = "1.0.1"
min_noctalia = "5.0.0" plugin_api = 3
license = "MIT" license = "MIT"
author = "joshuaslate" author = "joshuaslate"
dependencies = ["shelly"] dependencies = ["shelly"]
+1 -1
View File
@@ -9,7 +9,7 @@
id = "nightwatch75/todo" id = "nightwatch75/todo"
name = "To Do" name = "To Do"
version = "0.0.7" version = "0.0.7"
min_noctalia = "5.0.0" plugin_api = 3
author = "nightwatch75" author = "nightwatch75"
license = "MIT" license = "MIT"
dependencies = [] dependencies = []
+1 -1
View File
@@ -1,7 +1,7 @@
id = "thatonecalculator/um5606_fan_state" id = "thatonecalculator/um5606_fan_state"
name = "ASUS UM5606 Fan State" name = "ASUS UM5606 Fan State"
version = "1.0.6" version = "1.0.6"
min_noctalia = "5.0.0" plugin_api = 3
author = "ThatOneCalculator" author = "ThatOneCalculator"
license = "MIT" license = "MIT"
tags = ["hardware"] tags = ["hardware"]
+1 -1
View File
@@ -1,7 +1,7 @@
id = "neuro/upbeat" id = "neuro/upbeat"
name = "Upbeat" name = "Upbeat"
version = "1.0.1" version = "1.0.1"
min_noctalia = "5.0.0" plugin_api = 3
author = "neuro" author = "neuro"
license = "MIT" license = "MIT"
dependencies = [] dependencies = []