diff --git a/agents.md b/agents.md index 0a0b540..d6182d5 100644 --- a/agents.md +++ b/agents.md @@ -298,7 +298,7 @@ Use helpers from `update/update_lib.py`: `latest_github_release`, `latest_docker If an image has no resolvable tag pattern, the script may raise `NotImplementedError`; the orchestrator reports it as `error` and the run continues. -For apps whose updates must be done manually — self-built images (e.g. `mosquitto`), or upstreams whose Docker tags are unreliable for auto-detection (e.g. `joplin-server`, where Docker Hub publishes prerelease tags without flagging them) — raise `update.update_lib.OptOut("")` instead. The orchestrator reports them as `opt_out` (distinct from `error`) with the reason string preserved, so the choice stays discoverable in every check run. +For apps whose updates must be done manually — self-built images, or upstreams whose Docker tags are unreliable for auto-detection (e.g. `joplin-server`, where Docker Hub publishes prerelease tags without flagging them) — raise `update.update_lib.OptOut("")` instead. The orchestrator reports them as `opt_out` (distinct from `error`) with the reason string preserved, so the choice stays discoverable in every check run. ## Checklist for Adding a New App diff --git a/apps/mosquitto/app_meta.json b/apps/mosquitto/app_meta.json index 018bd21..baf8362 100644 --- a/apps/mosquitto/app_meta.json +++ b/apps/mosquitto/app_meta.json @@ -1,8 +1,11 @@ { - "v": "1.0", - "app_version": "0.1.0", + "v": "1.2", + "app_version": "2.0.22", "name": "mosquitto", + "pretty_name": "Mosquitto", "icon": "icon.png", + "homepage": "https://mosquitto.org/", + "upstream_repo": "https://github.com/eclipse-mosquitto/mosquitto", "entrypoints": [ { "container_name": "mosquitto", @@ -37,5 +40,4 @@ ], "is_featured": true } - } diff --git a/apps/mosquitto/docker-compose.yml.template b/apps/mosquitto/docker-compose.yml.template index bf16ed3..215cc4f 100644 --- a/apps/mosquitto/docker-compose.yml.template +++ b/apps/mosquitto/docker-compose.yml.template @@ -5,9 +5,116 @@ networks: services: mosquitto: restart: always - image: portalapps.azurecr.io/ptl-apps/mosquitto:main + image: eclipse-mosquitto:2.0.22 container_name: mosquitto volumes: - "{{ fs.app_data }}/mosquitto/config:/mosquitto/config" networks: - portal + command: + - /bin/sh + - -c + - | + set -e + if [ -z "$$(ls -A /mosquitto/config)" ]; then + cat > /mosquitto/config/mosquitto.conf <<'CONF' + listener 1883 + protocol mqtt + + listener 9001 + protocol websockets + + require_certificate false + + per_listener_settings false + + plugin /usr/lib/mosquitto_dynamic_security.so + plugin_opt_config_file /mosquitto/config/dynamic_security.json + CONF + cat > /mosquitto/config/dynamic_security.json <<'DYNSEC' + { + "defaultACLAccess": { + "publishClientSend": false, + "publishClientReceive": true, + "subscribe": false, + "unsubscribe": true + }, + "clients": [ + { + "username": "admin", + "textname": "Dynsec admin user", + "roles": [ + { + "rolename": "admin" + } + ], + "password": "xHrePRFxvhHRGCWpXzg3TWAnPUc9MoDFTah68IWPgOPVf2bzX1Sl8OjqMnGp8SAg6KtaHWIG5l8HocL0Qii4pw==", + "salt": "4ad7/uINpqWDaFQA", + "iterations": 101 + } + ], + "groups": [], + "roles": [ + { + "rolename": "admin", + "acls": [ + { + "acltype": "publishClientSend", + "topic": "$$CONTROL/dynamic-security/#", + "priority": 0, + "allow": true + }, + { + "acltype": "publishClientReceive", + "topic": "$$CONTROL/dynamic-security/#", + "priority": 0, + "allow": true + }, + { + "acltype": "publishClientReceive", + "topic": "$$SYS/#", + "priority": 0, + "allow": true + }, + { + "acltype": "publishClientReceive", + "topic": "#", + "priority": 0, + "allow": true + }, + { + "acltype": "subscribePattern", + "topic": "$$CONTROL/dynamic-security/#", + "priority": 0, + "allow": true + }, + { + "acltype": "subscribePattern", + "topic": "$$SYS/#", + "priority": 0, + "allow": true + }, + { + "acltype": "subscribePattern", + "topic": "#", + "priority": 0, + "allow": true + }, + { + "acltype": "unsubscribePattern", + "topic": "#", + "priority": 0, + "allow": true + } + ] + } + ] + } + DYNSEC + chown mosquitto:mosquitto /mosquitto/config/dynamic_security.json + chmod 0700 /mosquitto/config/dynamic_security.json + echo "seeded initial mosquitto config" + else + echo "existing mosquitto config found, leaving it untouched" + fi + exec /usr/sbin/mosquitto -c /mosquitto/config/mosquitto.conf diff --git a/apps/mosquitto/update_check.py b/apps/mosquitto/update_check.py index e4b25ff..e15191c 100644 --- a/apps/mosquitto/update_check.py +++ b/apps/mosquitto/update_check.py @@ -1,7 +1,30 @@ -from update.update_lib import OptOut +import re + +from update.update_lib import _http_get_text, latest_dockerhub_tag + +# Upstream publishes git tags and Docker images but no GitHub releases, so +# Docker Hub is the authoritative tag source and ChangeLog.txt the release notes. +# Plain `X.Y.Z` tags are the 2.0 line; the 2.1 line is published as +# `2.1.x-alpine` only and is deliberately not matched here. +TAG_RE = r"^\d+\.\d+\.\d+$" +CHANGELOG_RAW = "https://raw.githubusercontent.com/eclipse-mosquitto/mosquitto/v{version}/ChangeLog.txt" +CHANGELOG_HTML = "https://github.com/eclipse-mosquitto/mosquitto/blob/v{version}/ChangeLog.txt" + + +def _changelog_section(version: str) -> str: + text = _http_get_text(CHANGELOG_RAW.format(version=version)) + match = re.search( + rf"^{re.escape(version)} - .*?(?=^\d+\.\d+\.\d+ - )", + text, + re.MULTILINE | re.DOTALL, + ) + return match.group(0).strip() if match else "" def check(current_version: str) -> dict: - # Image is a private build at portalapps.azurecr.io; not auto-updatable. - # Bumps must be done manually after rebuilding the image. - raise OptOut("self-built image, manual updates only") + latest = latest_dockerhub_tag("eclipse-mosquitto", filter_regex=TAG_RE) + return { + "latest_version": latest, + "release_notes_url": CHANGELOG_HTML.format(version=latest), + "release_body": _changelog_section(latest), + }