diff --git a/README.md b/README.md index 468b3e9..d0e5654 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ Das Plugin unter `netbox_plugin/` ist auf NetBox 4.6.5–4.6.8 begrenzt. Zuerst ```text cd netbox_plugin python -m build -/opt/netbox/venv/bin/pip install dist/netbox_plugin_store-0.1.1-py3-none-any.whl +/opt/netbox/venv/bin/pip install dist/netbox_plugin_store-0.1.2-py3-none-any.whl ``` Das Paket muss außerdem in `/opt/netbox/local_requirements.txt` festgehalten werden. In `configuration.py` wird es zunächst sicher im Dry-run-Modus eingerichtet: diff --git a/netbox_plugin/README.md b/netbox_plugin/README.md index a5d76e5..970cab6 100644 --- a/netbox_plugin/README.md +++ b/netbox_plugin/README.md @@ -22,7 +22,7 @@ The safe default is intentionally non-mutating. Installing the UI alone never gr Install the wheel into NetBox's virtual environment and persist it in `/opt/netbox/local_requirements.txt`: ```text -netbox-plugin-store==0.1.1 +netbox-plugin-store==0.1.2 ``` Add the plugin to `configuration.py`: diff --git a/netbox_plugin/netbox_plugin_store/client.py b/netbox_plugin/netbox_plugin_store/client.py index f06d9ed..989f330 100644 --- a/netbox_plugin/netbox_plugin_store/client.py +++ b/netbox_plugin/netbox_plugin_store/client.py @@ -166,7 +166,7 @@ class CatalogPlugin: ) def select_release(self, netbox_version: str, requested_version: str = "") -> Release: - compatible = [release for release in self.releases if release.supports(netbox_version, self)] + compatible = list(self.installable_releases(netbox_version)) if requested_version: compatible = [release for release in compatible if release.version == requested_version] elif self.latest_version: @@ -177,6 +177,26 @@ class CatalogPlugin: raise StoreClientError("No compatible release is available for this NetBox version.") return max(compatible, key=lambda release: Version(release.version)) + def installable_releases( + self, + netbox_version: str, + *, + require_approval_marker: bool = False, + ) -> tuple[Release, ...]: + return tuple( + release + for release in self.releases + if ( + release.supports(netbox_version, self) + and release.approved + and release.immutable + and bool(release.download_url) + and bool(release.sha256) + and release.artifact_kind in {"wheel", "source_archive"} + and (not require_approval_marker or bool(release.approved_payload_sha256)) + ) + ) + class StoreClient: API_LIMIT = 5 * 1024 * 1024 diff --git a/netbox_plugin/netbox_plugin_store/forms.py b/netbox_plugin/netbox_plugin_store/forms.py index 88494d7..8893967 100644 --- a/netbox_plugin/netbox_plugin_store/forms.py +++ b/netbox_plugin/netbox_plugin_store/forms.py @@ -34,21 +34,20 @@ class LifecycleConfirmForm(forms.Form): self.runtime = runtime self.fields["confirmation"].widget.attrs.update({"autocomplete": "off", "placeholder": plugin.slug}) self.fields["dry_run"].initial = runtime.default_dry_run + self.has_installable_release = True if action in {"install", "update"}: choices = [ (release.version, release.version) - for release in plugin.releases - if ( - release.supports(runtime.netbox_version, plugin) - and release.approved - and release.immutable - and bool(release.sha256) - and (runtime.execution_mode != "agent" or bool(release.approved_payload_sha256)) + for release in plugin.installable_releases( + runtime.netbox_version, + require_approval_marker=runtime.execution_mode == "agent", ) ] choices.sort(reverse=True) self.fields["version"].choices = choices - self.fields["version"].required = True + self.has_installable_release = bool(choices) + self.fields["version"].required = self.has_installable_release + self.fields["version"].disabled = not self.has_installable_release self.fields["version"].initial = plugin.latest_version else: self.fields.pop("version") @@ -64,6 +63,11 @@ class LifecycleConfirmForm(forms.Form): def clean(self): cleaned = super().clean() + if self.action in {"install", "update"} and not self.has_installable_release: + raise forms.ValidationError( + "Keine freigegebene, kompatible Plugin-Version ist verfügbar. " + "Das Source-Artefakt muss zunächst im Store synchronisiert und freigegeben werden." + ) if self.runtime.execution_mode == "dry_run" and not cleaned.get("dry_run", False): self.add_error("dry_run", "Reale Aktionen sind in execution_mode=dry_run deaktiviert.") return cleaned diff --git a/netbox_plugin/netbox_plugin_store/templates/netbox_plugin_store/catalog.html b/netbox_plugin/netbox_plugin_store/templates/netbox_plugin_store/catalog.html index 14fe7da..cae1582 100644 --- a/netbox_plugin/netbox_plugin_store/templates/netbox_plugin_store/catalog.html +++ b/netbox_plugin/netbox_plugin_store/templates/netbox_plugin_store/catalog.html @@ -9,7 +9,7 @@
Freigegebene Plugins für diese NetBox-Instanz.
{% if runtime %} - + Modus: {{ runtime.execution_mode }} {% endif %} @@ -29,14 +29,14 @@{{ card.plugin.summary|default:"Keine Zusammenfassung vorhanden." }}
| Plugin / Version | Artefakt | Integrität | Status | Aktionen |
|---|---|---|---|---|
| Keine installierbaren Artefakte gefunden. Prüfe Version und Build-Metadaten des Repositorys. | ||||
| Keine installierbaren Artefakte gefunden. Bei Repositorys ohne Release erzeugt eine erneute Synchronisierung automatisch ein commitgebundenes Source-Artefakt, sofern das Python-Paket eine Version definiert. | ||||