enforce max 120 chars description
This commit is contained in:
@@ -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))
|
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):
|
class PluginConfigAccessorTests(unittest.TestCase):
|
||||||
def test_accepts_universal_accessor(self) -> None:
|
def test_accepts_universal_accessor(self) -> None:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from typing import Any
|
|||||||
DEFAULT_ROOT = Path(__file__).resolve().parents[2]
|
DEFAULT_ROOT = Path(__file__).resolve().parents[2]
|
||||||
SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+$")
|
SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+$")
|
||||||
LAUNCHER_PREFIX_RE = re.compile(r"^[a-z]+$")
|
LAUNCHER_PREFIX_RE = re.compile(r"^[a-z]+$")
|
||||||
|
DESCRIPTION_MAX_CHARS = 120
|
||||||
ALLOWED_TAGS = {
|
ALLOWED_TAGS = {
|
||||||
"ai",
|
"ai",
|
||||||
"animation",
|
"animation",
|
||||||
@@ -475,6 +476,18 @@ class Validator:
|
|||||||
f"tags[{index}] '{tag}' is not an allowed tag",
|
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:
|
def validate_root_fields(self, manifest_path: Path, manifest: dict[str, Any]) -> None:
|
||||||
unknown = sorted(set(manifest) - ROOT_FIELDS)
|
unknown = sorted(set(manifest) - ROOT_FIELDS)
|
||||||
for field in unknown:
|
for field in unknown:
|
||||||
@@ -502,6 +515,9 @@ class Validator:
|
|||||||
if "tags" in manifest:
|
if "tags" in manifest:
|
||||||
self.validate_tags(manifest_path, manifest["tags"])
|
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):
|
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")
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
- `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
|
- `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.
|
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
|
- `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.
|
||||||
- Screenshots or a short video for anything with a visual surface.
|
- Screenshots or a short video for anything with a visual surface.
|
||||||
|
|||||||
@@ -13,14 +13,14 @@
|
|||||||
|
|
||||||
id = "whyoolw/sharednd"
|
id = "whyoolw/sharednd"
|
||||||
name = "ShareDND"
|
name = "ShareDND"
|
||||||
version = "1.1.0"
|
version = "1.1.1"
|
||||||
min_noctalia = "5.0.0"
|
min_noctalia = "5.0.0"
|
||||||
author = "whyoolw"
|
author = "whyoolw"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
dependencies = ["niri"]
|
dependencies = ["niri"]
|
||||||
tags = ["privacy", "niri"]
|
tags = ["privacy", "niri"]
|
||||||
icon = "screen-share"
|
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]]
|
[[setting]]
|
||||||
key = "only_active"
|
key = "only_active"
|
||||||
|
|||||||
Reference in New Issue
Block a user