From 0cd40d6715ca42ab708532c91d52ea4622431aa7 Mon Sep 17 00:00:00 2001 From: Lemmy Date: Wed, 15 Jul 2026 23:04:53 -0400 Subject: [PATCH 1/3] enforce max 120 chars description --- .github/workflows/test_validate_plugins.py | 21 +++++++++++++++++++++ .github/workflows/validate-plugins.py | 16 ++++++++++++++++ README.md | 1 + sharednd/plugin.toml | 4 ++-- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_validate_plugins.py b/.github/workflows/test_validate_plugins.py index 6887fc3..5400fd2 100644 --- a/.github/workflows/test_validate_plugins.py +++ b/.github/workflows/test_validate_plugins.py @@ -66,6 +66,27 @@ class AllowedTagsTests(unittest.TestCase): self.assertTrue(any("tags[2] must be a non-empty string" in error for error in errors)) +class DescriptionTests(unittest.TestCase): + def validate_description(self, description: object) -> list[str]: + validator = validate_plugins.Validator(Path("/repo")) + validator.validate_description(Path("/repo/example/plugin.toml"), description) + return validator.errors + + def test_accepts_description_at_limit(self) -> None: + self.assertEqual( + self.validate_description("x" * validate_plugins.DESCRIPTION_MAX_CHARS), + [], + ) + + def test_rejects_description_over_limit(self) -> None: + errors = self.validate_description( + "x" * (validate_plugins.DESCRIPTION_MAX_CHARS + 1) + ) + self.assertEqual(len(errors), 1) + self.assertIn("is 121 characters", errors[0]) + self.assertIn("at or below 120", errors[0]) + + class PluginConfigAccessorTests(unittest.TestCase): def test_accepts_universal_accessor(self) -> None: self.assertEqual( diff --git a/.github/workflows/validate-plugins.py b/.github/workflows/validate-plugins.py index a5046db..c1cf05c 100644 --- a/.github/workflows/validate-plugins.py +++ b/.github/workflows/validate-plugins.py @@ -14,6 +14,7 @@ from typing import Any DEFAULT_ROOT = Path(__file__).resolve().parents[2] SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+$") LAUNCHER_PREFIX_RE = re.compile(r"^[a-z]+$") +DESCRIPTION_MAX_CHARS = 120 ALLOWED_TAGS = { "ai", "animation", @@ -475,6 +476,18 @@ class Validator: f"tags[{index}] '{tag}' is not an allowed tag", ) + def validate_description(self, manifest_path: Path, value: Any) -> None: + if not is_non_empty_string(value): + return + + length = len(value) + if length > DESCRIPTION_MAX_CHARS: + self.add_error( + manifest_path, + f"root field 'description' is {length} characters; " + f"keep catalog descriptions at or below {DESCRIPTION_MAX_CHARS}", + ) + def validate_root_fields(self, manifest_path: Path, manifest: dict[str, Any]) -> None: unknown = sorted(set(manifest) - ROOT_FIELDS) for field in unknown: @@ -502,6 +515,9 @@ class Validator: if "tags" in manifest: self.validate_tags(manifest_path, manifest["tags"]) + if "description" in manifest: + self.validate_description(manifest_path, manifest["description"]) + if "deprecated" in manifest and not isinstance(manifest["deprecated"], bool): self.add_error(manifest_path, "root field 'deprecated' must be a bool") diff --git a/README.md b/README.md index 5be7ad9..28dd7ff 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ Open a PR against `main`. CI validates your manifest, entry scripts, required fi - `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 needs an upgrade instead of getting a broken install. +- `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 your plugin directory. There is no repo-wide license covering contributed plugins. - Screenshots or a short video for anything with a visual surface. diff --git a/sharednd/plugin.toml b/sharednd/plugin.toml index d57ddd1..9d8f09d 100644 --- a/sharednd/plugin.toml +++ b/sharednd/plugin.toml @@ -13,14 +13,14 @@ id = "whyoolw/sharednd" name = "ShareDND" -version = "1.1.0" +version = "1.1.1" min_noctalia = "5.0.0" author = "whyoolw" license = "MIT" dependencies = ["niri"] tags = ["privacy", "niri"] icon = "screen-share" -description = "Enables notification Do Not Disturb while the screen is shared (niri screencast) and turns notifications back on when sharing stops. Requires niri." +description = "Automatically enables notification Do Not Disturb while a niri screencast is active." [[setting]] key = "only_active" From 264f65a66039b2ab9d7d1aaebe9c69e8506e1cdc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 16 Jul 2026 03:05:02 +0000 Subject: [PATCH 2/3] chore: update plugin catalog [skip ci] --- catalog.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalog.toml b/catalog.toml index 4475fe0..d271750 100644 --- a/catalog.toml +++ b/catalog.toml @@ -20,11 +20,11 @@ tags = ["fun", "clock", "utility", "time"] [[plugin]] id = "whyoolw/sharednd" name = "ShareDND" -version = "1.1.0" +version = "1.1.1" author = "whyoolw" license = "MIT" icon = "screen-share" -description = "Enables notification Do Not Disturb while the screen is shared (niri screencast) and turns notifications back on when sharing stops. Requires niri." +description = "Automatically enables notification Do Not Disturb while a niri screencast is active." min_noctalia = "5.0.0" tags = ["privacy", "niri"] From f459f4f4f8037008910bd158bf9087300f3bf4b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 16 Jul 2026 11:04:36 +0000 Subject: [PATCH 3/3] chore: update plugin catalog [skip ci] --- catalog.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog.toml b/catalog.toml index d271750..4da030b 100644 --- a/catalog.toml +++ b/catalog.toml @@ -111,7 +111,7 @@ tags = ["gaming", "launcher"] [[plugin]] id = "avivbintangaringga/nix-monitor" name = "Nix Monitor" -version = "1.0.0" +version = "1.1.0" author = "avivbintangaringga" license = "MIT" icon = "package"