release: v0.6.0 — remove deprecated env var aliases (#704)

* chore: bump version to 0.6.0 and remove deprecated env var aliases

BREAKING CHANGES:
- POTATOMESH_INSTANCE removed — use INSTANCE_DOMAIN
- PROVIDER removed — use PROTOCOL
- MESH_SERIAL removed — use CONNECTION
- PORT config alias removed — use CONNECTION

The _ConfigModule proxy class (which kept PROTOCOL/PROVIDER and
CONNECTION/PORT in sync) is deleted. docker-compose.yml now defaults
INSTANCE_DOMAIN to http://web:41447 so deployments without an explicit
value continue to work.

* tests: run black

* address review comments
This commit is contained in:
l5y
2026-04-05 16:49:10 +02:00
committed by GitHub
parent 0a479e4517
commit de1ccc5a2e
21 changed files with 107 additions and 205 deletions
+1 -1
View File
@@ -188,7 +188,7 @@ jobs:
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-ingestor-linux-amd64:${{ steps.version.outputs.version }}
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-ingestor-linux-amd64:${{ steps.version.outputs.version_with_v }}
docker run --rm --name ingestor-test \
-e POTATOMESH_INSTANCE=http://localhost:41447 \
-e INSTANCE_DOMAIN=http://localhost:41447 \
-e API_TOKEN=test-token \
-e CONNECTION=mock \
-e DEBUG=1 \
+60
View File
@@ -1,5 +1,65 @@
# CHANGELOG
## v0.6.0
**Breaking changes — remove deprecated environment variable aliases:**
* Ingestor: remove `POTATOMESH_INSTANCE` env var — use `INSTANCE_DOMAIN` by @l5yth
* Ingestor: remove `PROVIDER` env var — use `PROTOCOL` by @l5yth
* Ingestor: remove `MESH_SERIAL` env var — use `CONNECTION` by @l5yth
* Ingestor: remove `PORT` config alias — use `CONNECTION` by @l5yth
* Docker: give `INSTANCE_DOMAIN` a default of `http://web:41447` in compose by @l5yth
* Chore: bump version to 0.6.0 across web, matrix bridge, and mobile app by @l5yth
## v0.5.12
* Enh: surface meshcore role types (#680) by @l5yth in <https://github.com/l5yth/potato-mesh/pull/685>
* Chore: refactor codebase before meshcore release by @l5yth in <https://github.com/l5yth/potato-mesh/pull/682>
* [Meshcore] enh: short name should be 1st 4 hex digits of public key by @benallfree in <https://github.com/l5yth/potato-mesh/pull/679>
* Chore: update xcode deps by @benallfree in <https://github.com/l5yth/potato-mesh/pull/674>
* Chore: update mesh.sh to use requirements file by @benallfree in <https://github.com/l5yth/potato-mesh/pull/675>
* Data/meshcore: fix ble and enable tcp by @l5yth in <https://github.com/l5yth/potato-mesh/pull/669>
* Data: handle store_forward and router_heartbeat portnum by @l5yth in <https://github.com/l5yth/potato-mesh/pull/667>
* Feat: implement meshcore provider by @l5yth in <https://github.com/l5yth/potato-mesh/pull/663>
* Ci: update dependabot and codecov settings by @l5yth in <https://github.com/l5yth/potato-mesh/pull/666>
* Web: prepare release by @l5yth in <https://github.com/l5yth/potato-mesh/pull/665>
* App: only query meshtastic provider by @l5yth in <https://github.com/l5yth/potato-mesh/pull/664>
* Data: prepare ingestor for meshcore by @l5yth in <https://github.com/l5yth/potato-mesh/pull/658>
* Web: fix css issues by @l5yth in <https://github.com/l5yth/potato-mesh/pull/659>
* Web: prepare frontend for multi protocol by @l5yth in <https://github.com/l5yth/potato-mesh/pull/657>
* Feat: split device and power-sensor telemetry charts (#643) by @l5yth in <https://github.com/l5yth/potato-mesh/pull/656>
* Web: implement a 'protocol' field across systems by @l5yth in <https://github.com/l5yth/potato-mesh/pull/655>
* Fix upsert clearing node coordinates bug by @l5yth in <https://github.com/l5yth/potato-mesh/pull/654>
* Data: resolve circular dependency of deamon.py by @l5yth in <https://github.com/l5yth/potato-mesh/pull/653>
* Proposal: mesh provider pattern refactor by @benallfree in <https://github.com/l5yth/potato-mesh/pull/651>
* Build(deps): bump rustls-webpki from 0.103.8 to 0.103.10 in /matrix by @dependabot[bot]< in https://github.com/l5yth/potato-mesh/pull/649>
* Build(deps): bump quinn-proto from 0.11.13 to 0.11.14 in /matrix by @dependabot[bot]< in https://github.com/l5yth/potato-mesh/pull/646>
## v0.5.11
* Chore: bump version to 0.5.11 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/645>
* Web: limit horizontal size of dropdown by @l5yth in <https://github.com/l5yth/potato-mesh/pull/644>
## v0.5.10
* Web: expose node stats in distinct api by @l5yth in <https://github.com/l5yth/potato-mesh/pull/641>
* Web: do merge channels by name by @l5yth in <https://github.com/l5yth/potato-mesh/pull/640>
* Web: do not merge channels by ID in frontend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/637>
* Web: do not touch neighbor last seen on neighbor info by @l5yth in <https://github.com/l5yth/potato-mesh/pull/636>
* Ingestor: report self id per packet by @l5yth in <https://github.com/l5yth/potato-mesh/pull/635>
* Ci: fix docker compose and docs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/634>
* Web: supress encrypted text messages in frontend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/633>
* Federation: ensure requests timeout properly and can be terminated by @l5yth in <https://github.com/l5yth/potato-mesh/pull/631>
* Build(deps): bump bytes from 1.11.0 to 1.11.1 in /matrix by @dependabot[bot]< in https://github.com/l5yth/potato-mesh/pull/627>
* Matrix: config loading now merges optional TOML with CLI/env/secret inputs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/617>
* Matrix: logs only non-sensitive config fields by @l5yth in <https://github.com/l5yth/potato-mesh/pull/616>
* Web: decrypted takes precedence by @l5yth in <https://github.com/l5yth/potato-mesh/pull/614>
* Add Apache 2.0 license headers to missing sources by @l5yth in <https://github.com/l5yth/potato-mesh/pull/615>
* Web: decrypt PSK-1 unencrypted messages on arrival by @l5yth in <https://github.com/l5yth/potato-mesh/pull/611>
* Web: daemonize federation worker pool to avoid deadlocks on stuck announcments by @l5yth in <https://github.com/l5yth/potato-mesh/pull/610>
* Web: add announcement banner by @l5yth in <https://github.com/l5yth/potato-mesh/pull/609>
* L5Y chore version 0510 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/608>
## v0.5.9
* Matrix: listen for synapse on port 41448 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/607>
+9 -10
View File
@@ -13,16 +13,16 @@ will pull the latest release images for you.
## Images on GHCR
| Service | Image |
|----------|---------------------------------------------------------------------------------------------------------------|
| Web UI | `ghcr.io/l5yth/potato-mesh-web-linux-amd64:<tag>` (e.g. `latest`, `3.0`, `v3.0`, or `3.1.0-rc1`) |
| Ingestor | `ghcr.io/l5yth/potato-mesh-ingestor-linux-amd64:<tag>` (e.g. `latest`, `3.0`, `v3.0`, or `3.1.0-rc1`) |
| Service | Image |
|----------|----------------------------------------------------------------------------------------------------------------|
| Web UI | `ghcr.io/l5yth/potato-mesh-web-linux-amd64:<tag>` (e.g. `latest`, `0.6.0`, `v0.6.0`, or `0.7.0-rc1`) |
| Ingestor | `ghcr.io/l5yth/potato-mesh-ingestor-linux-amd64:<tag>` (e.g. `latest`, `0.6.0`, `v0.6.0`, or `0.7.0-rc1`) |
Images are published for every tagged release. Stable builds receive both
semantic version tags (for example `3.0`) and a matching `v`-prefixed tag (for
example `v3.0`), plus a `latest` tag that tracks the newest stable release.
semantic version tags (for example `0.6.0`) and a matching `v`-prefixed tag (for
example `v0.6.0`), plus a `latest` tag that tracks the newest stable release.
Pre-release tags (for example `-rc`, `-beta`, `-alpha`, or `-dev` suffixes) are
published only with their explicit version strings (`3.1.0-rc1` and `v3.1.0-rc1`
published only with their explicit version strings (`0.7.0-rc1` and `v0.7.0-rc1`
in this example) and do **not** advance `latest`. Pin the versioned tags when
you need a specific build.
@@ -60,9 +60,8 @@ Additional environment variables are optional:
| `CONNECTION` | `/dev/ttyACM0` | Serial device, TCP endpoint, or Bluetooth target used by the ingestor to reach the radio. |
The ingestor posts to the URL configured via `INSTANCE_DOMAIN` (defaulting to
`http://web:41447` in the provided compose file) and still accepts
`POTATOMESH_INSTANCE` as a legacy alias when the primary variable is unset. Use
`CHANNEL_INDEX` to select a LoRa channel on serial or Bluetooth connections.
`http://web:41447` in the provided compose file). Use `CHANNEL_INDEX` to select
a LoRa channel on serial or Bluetooth connections.
## Docker Compose file
+7 -2
View File
@@ -7,7 +7,10 @@
[![Contributions Welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/l5yth/potato-mesh/issues)
[![Matrix Chat](https://img.shields.io/badge/matrix-%23potatomesh:dod.ngo-blue)](https://matrix.to/#/#potatomesh:dod.ngo)
A federated, Meshtastic-powered node dashboard for your local community.
[![Meshtastic](https://img.shields.io/badge/Meshtastic-supported-67ea94)](https://meshtastic.org)
[![MeshCore](https://img.shields.io/badge/MeshCore-supported-000000)](https://meshcore.co.uk)
A federated, Meshtastic & Meshcore node dashboard for your local community.
_No MQTT clutter, just local LoRa aether._
* Web dashboard with chat window and map view showing nodes, positions, neighbors,
@@ -17,13 +20,15 @@ _No MQTT clutter, just local LoRa aether._
* Allows searching and filtering for nodes in map and table view.
* Federated: _automatically_ froms a federation with other communities running
Potato Mesh!
* Supports Meshtastic and Meshcore
* Supplemental Python ingestor to feed the POST APIs of the Web app with data remotely.
* Supports multiple ingestors per instance.
* Supports Meshtastic and Meshcore
* Matrix bridge that posts Meshtastic messages to a defined matrix channel (no
radio required).
* Mobile app to _read_ messages on your local aether (no radio required).
Live demo for Berlin #MediumFast: [potatomesh.net](https://potatomesh.net)
Live demo for Berlin: [potatomesh.net](https://potatomesh.net)
![screenshot of the fourth version](./scrot-0.4.png)
+2 -2
View File
@@ -15,11 +15,11 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.5.12</string>
<string>0.6.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.5.12</string>
<string>0.6.0</string>
<key>MinimumOSVersion</key>
<string>14.0</string>
</dict>
+1 -1
View File
@@ -1,7 +1,7 @@
name: potato_mesh_reader
description: Meshtastic Reader — read-only view for PotatoMesh messages.
publish_to: "none"
version: 0.5.12
version: 0.6.0
environment:
sdk: ">=3.4.0 <4.0.0"
-10
View File
@@ -219,16 +219,6 @@ else
sed -i.bak '/^INSTANCE_DOMAIN=.*/d' .env
fi
# Migrate legacy connection settings and ensure defaults exist
if grep -q "^MESH_SERIAL=" .env; then
legacy_connection=$(grep "^MESH_SERIAL=" .env | head -n1 | cut -d'=' -f2-)
if [ -n "$legacy_connection" ] && ! grep -q "^CONNECTION=" .env; then
echo "♻️ Migrating legacy MESH_SERIAL value to CONNECTION"
update_env "CONNECTION" "$legacy_connection"
fi
sed -i.bak '/^MESH_SERIAL=.*/d' .env
fi
if ! grep -q "^CONNECTION=" .env; then
echo "CONNECTION=/dev/ttyACM0" >> .env
fi
+1 -1
View File
@@ -18,7 +18,7 @@ The ``data.mesh`` module exposes helpers for reading Meshtastic node and
message information before forwarding it to the accompanying web application.
"""
VERSION = "0.5.12"
VERSION = "0.6.0"
"""Semantic version identifier shared with the dashboard and front-end."""
__version__ = VERSION
-3
View File
@@ -82,9 +82,6 @@ _CONFIG_ATTRS = {
"_debug_log",
}
# Legacy export maintained for backwards compatibility.
_CONFIG_ATTRS.add("PORT")
_INTERFACE_ATTRS = {"BLEInterface", "SerialInterface", "TCPInterface"}
_QUEUE_ATTRS = set(queue.__all__)
+6 -47
View File
@@ -17,9 +17,7 @@
from __future__ import annotations
import os
import sys
from datetime import datetime, timezone
from types import ModuleType
from typing import Any
DEFAULT_SNAPSHOT_SECS = 60
@@ -49,12 +47,11 @@ DEFAULT_ENERGY_SLEEP_SECS = float(6 * 60 * 60)
DEFAULT_INGESTOR_HEARTBEAT_SECS = float(60 * 60)
"""Interval between ingestor heartbeat announcements."""
CONNECTION = os.environ.get("CONNECTION") or os.environ.get("MESH_SERIAL")
CONNECTION = os.environ.get("CONNECTION")
"""Optional connection target for the mesh interface.
When unset, platform-specific defaults will be inferred by the interface
implementations. The legacy :envvar:`MESH_SERIAL` environment variable is still
accepted for backwards compatibility.
implementations.
"""
SNAPSHOT_SECS = DEFAULT_SNAPSHOT_SECS
@@ -67,13 +64,7 @@ DEBUG = os.environ.get("DEBUG") == "1"
_KNOWN_PROTOCOLS = ("meshtastic", "meshcore")
# Prefer the canonical PROTOCOL env var; fall back to legacy PROVIDER for
# backwards compatibility with existing deployments.
_raw_protocol = (
(os.environ.get("PROTOCOL") or os.environ.get("PROVIDER", "meshtastic"))
.strip()
.lower()
)
_raw_protocol = os.environ.get("PROTOCOL", "meshtastic").strip().lower()
if _raw_protocol not in _KNOWN_PROTOCOLS:
raise ValueError(
f"Unknown PROTOCOL={_raw_protocol!r}. "
@@ -84,12 +75,8 @@ PROTOCOL = _raw_protocol
"""Active ingestion protocol, selected via the :envvar:`PROTOCOL` environment variable.
Accepted values are ``meshtastic`` (default) and ``meshcore``.
The legacy :envvar:`PROVIDER` environment variable is still accepted as a fallback.
"""
PROVIDER = PROTOCOL
"""Deprecated alias for :data:`PROTOCOL`; kept for backwards compatibility."""
def _parse_channel_names(raw_value: str | None) -> tuple[str, ...]:
"""Normalise a comma-separated list of channel names.
@@ -137,16 +124,11 @@ ALLOWED_CHANNELS = _parse_channel_names(os.environ.get("ALLOWED_CHANNELS"))
def _resolve_instance_domain() -> str:
"""Resolve the configured instance domain from the environment.
The ingestor prefers the :envvar:`INSTANCE_DOMAIN` variable for clarity and
compatibility with the web application. For deployments that still
configure the legacy :envvar:`POTATOMESH_INSTANCE` variable, the resolver
falls back to that value when no primary domain is set.
Reads the :envvar:`INSTANCE_DOMAIN` variable. When the value does not
contain a scheme, ``https://`` is prepended automatically.
"""
instance_domain = os.environ.get("INSTANCE_DOMAIN", "")
legacy_instance = os.environ.get("POTATOMESH_INSTANCE", "")
configured_instance = (instance_domain or legacy_instance).rstrip("/")
configured_instance = os.environ.get("INSTANCE_DOMAIN", "").rstrip("/")
if configured_instance and "://" not in configured_instance:
return f"https://{configured_instance}"
@@ -173,9 +155,6 @@ _ENERGY_ONLINE_DURATION_SECS = DEFAULT_ENERGY_ONLINE_DURATION_SECS
_ENERGY_SLEEP_SECS = DEFAULT_ENERGY_SLEEP_SECS
_INGESTOR_HEARTBEAT_SECS = DEFAULT_INGESTOR_HEARTBEAT_SECS
# Backwards compatibility shim for legacy imports.
PORT = CONNECTION
def _debug_log(
message: str,
@@ -232,23 +211,3 @@ __all__ = [
"_INGESTOR_HEARTBEAT_SECS",
"_debug_log",
]
class _ConfigModule(ModuleType):
"""Module proxy that keeps connection aliases synchronised."""
def __setattr__(self, name: str, value: Any) -> None: # type: ignore[override]
"""Propagate CONNECTION/PORT and PROTOCOL/PROVIDER assignments to both attributes."""
if name in {"CONNECTION", "PORT"}:
super().__setattr__("CONNECTION", value)
super().__setattr__("PORT", value)
return
if name in {"PROTOCOL", "PROVIDER"}:
super().__setattr__("PROTOCOL", value)
super().__setattr__("PROVIDER", value)
return
super().__setattr__(name, value)
sys.modules[__name__].__class__ = _ConfigModule
+1 -2
View File
@@ -52,8 +52,7 @@ x-ingestor-base: &ingestor-base
ALLOWED_CHANNELS: ${ALLOWED_CHANNELS:-""}
HIDDEN_CHANNELS: ${HIDDEN_CHANNELS:-""}
API_TOKEN: ${API_TOKEN}
INSTANCE_DOMAIN: ${INSTANCE_DOMAIN}
POTATOMESH_INSTANCE: ${POTATOMESH_INSTANCE:-http://web:41447}
INSTANCE_DOMAIN: ${INSTANCE_DOMAIN:-http://web:41447}
DEBUG: ${DEBUG:-0}
FEDERATION: ${FEDERATION:-1}
PRIVATE: ${PRIVATE:-0}
+1 -1
View File
@@ -969,7 +969,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "potatomesh-matrix-bridge"
version = "0.5.12"
version = "0.6.0"
dependencies = [
"anyhow",
"axum",
+1 -1
View File
@@ -14,7 +14,7 @@
[package]
name = "potatomesh-matrix-bridge"
version = "0.5.12"
version = "0.6.0"
edition = "2021"
[dependencies]
+1 -3
View File
@@ -28,9 +28,7 @@ from meshtastic.mesh_interface import MeshInterface
from meshtastic.serial_interface import SerialInterface
from pubsub import pub
CONNECTION = os.environ.get("CONNECTION") or os.environ.get(
"MESH_SERIAL", "/dev/ttyACM0"
)
CONNECTION = os.environ.get("CONNECTION", "/dev/ttyACM0")
"""Connection target opened to capture Meshtastic traffic."""
OUT = os.environ.get("MESH_DUMP_FILE", "meshtastic-dump.ndjson")
+2 -63
View File
@@ -102,39 +102,27 @@ class TestResolveInstanceDomain:
def test_returns_instance_domain_when_set(self, monkeypatch):
"""Uses INSTANCE_DOMAIN when set."""
monkeypatch.setenv("INSTANCE_DOMAIN", "mesh.example.com")
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
result = config._resolve_instance_domain()
assert result == "https://mesh.example.com"
def test_adds_https_when_no_scheme(self, monkeypatch):
"""Adds https:// prefix when no scheme is present."""
monkeypatch.setenv("INSTANCE_DOMAIN", "example.com")
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
assert config._resolve_instance_domain() == "https://example.com"
def test_preserves_existing_scheme(self, monkeypatch):
"""Leaves existing http:// scheme intact."""
monkeypatch.setenv("INSTANCE_DOMAIN", "http://example.com")
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
assert config._resolve_instance_domain() == "http://example.com"
def test_strips_trailing_slash(self, monkeypatch):
"""Strips trailing slash from instance domain."""
monkeypatch.setenv("INSTANCE_DOMAIN", "https://example.com/")
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
assert config._resolve_instance_domain() == "https://example.com"
def test_falls_back_to_legacy_env(self, monkeypatch):
"""Falls back to POTATOMESH_INSTANCE when INSTANCE_DOMAIN is absent."""
def test_returns_empty_when_not_set(self, monkeypatch):
"""Returns empty string when INSTANCE_DOMAIN is unset."""
monkeypatch.delenv("INSTANCE_DOMAIN", raising=False)
monkeypatch.setenv("POTATOMESH_INSTANCE", "legacy.example.com")
result = config._resolve_instance_domain()
assert result == "https://legacy.example.com"
def test_returns_empty_when_neither_set(self, monkeypatch):
"""Returns empty string when neither env var is set."""
monkeypatch.delenv("INSTANCE_DOMAIN", raising=False)
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
assert config._resolve_instance_domain() == ""
@@ -221,52 +209,3 @@ class TestProtocolValidation:
# Restore to valid value so subsequent tests work
monkeypatch.setenv("PROTOCOL", "meshtastic")
importlib.reload(config)
def test_protocol_env_var_takes_priority_over_provider(self, monkeypatch):
"""PROTOCOL env var must take precedence over legacy PROVIDER."""
import importlib
monkeypatch.setenv("PROTOCOL", "meshcore")
monkeypatch.setenv("PROVIDER", "meshtastic")
cfg = importlib.reload(config)
assert cfg.PROTOCOL == "meshcore"
assert cfg.PROVIDER == "meshcore" # alias stays in sync
# Restore
monkeypatch.delenv("PROTOCOL")
monkeypatch.delenv("PROVIDER")
importlib.reload(config)
def test_provider_env_var_fallback(self, monkeypatch):
"""Legacy PROVIDER env var must still be accepted when PROTOCOL is absent."""
import importlib
monkeypatch.delenv("PROTOCOL", raising=False)
monkeypatch.setenv("PROVIDER", "meshcore")
cfg = importlib.reload(config)
assert cfg.PROTOCOL == "meshcore"
assert cfg.PROVIDER == "meshcore"
# Restore
monkeypatch.delenv("PROVIDER")
importlib.reload(config)
# ---------------------------------------------------------------------------
# _ConfigModule proxy
# ---------------------------------------------------------------------------
class TestConfigModuleProxy:
"""Tests for the :class:`config._ConfigModule` proxy behaviour."""
def test_connection_and_port_stay_in_sync(self):
"""Setting CONNECTION also updates PORT and vice versa."""
original_connection = config.CONNECTION
original_port = config.PORT
try:
config.CONNECTION = "tcp://testhost"
assert config.PORT == "tcp://testhost"
config.PORT = "serial:/dev/ttyUSB0"
assert config.CONNECTION == "serial:/dev/ttyUSB0"
finally:
config.CONNECTION = original_connection
config.PORT = original_port
+4 -16
View File
@@ -891,13 +891,12 @@ def _reload_config() -> types.ModuleType:
@pytest.fixture()
def reset_provider_config():
"""Reload config after the test so PROTOCOL/PROVIDER changes don't leak across tests."""
def reset_protocol_config():
"""Reload config after the test so PROTOCOL changes don't leak across tests."""
yield
import os
os.environ.pop("PROTOCOL", None)
os.environ.pop("PROVIDER", None)
_reload_config()
@@ -908,28 +907,17 @@ def reset_provider_config():
("meshcore", "meshcore"),
],
)
def test_config_protocol_env(monkeypatch, reset_provider_config, env_value, expected):
def test_config_protocol_env(monkeypatch, reset_protocol_config, env_value, expected):
"""PROTOCOL env var selects the protocol; absent defaults to 'meshtastic'."""
if env_value is None:
monkeypatch.delenv("PROTOCOL", raising=False)
monkeypatch.delenv("PROVIDER", raising=False)
else:
monkeypatch.setenv("PROTOCOL", env_value)
cfg = _reload_config()
assert cfg.PROTOCOL == expected
assert cfg.PROVIDER == expected # deprecated alias stays in sync
def test_config_provider_env_fallback(monkeypatch, reset_provider_config):
"""Legacy PROVIDER env var must still work when PROTOCOL is absent."""
monkeypatch.delenv("PROTOCOL", raising=False)
monkeypatch.setenv("PROVIDER", "meshcore")
cfg = _reload_config()
assert cfg.PROTOCOL == "meshcore"
assert cfg.PROVIDER == "meshcore"
def test_config_protocol_unknown_raises(monkeypatch, reset_provider_config):
def test_config_protocol_unknown_raises(monkeypatch, reset_protocol_config):
"""An unrecognised PROTOCOL value must raise ValueError at import time."""
monkeypatch.setenv("PROTOCOL", "reticulum")
with pytest.raises(ValueError, match="PROTOCOL"):
-10
View File
@@ -41,16 +41,6 @@ def reset_state(monkeypatch):
importlib.reload(config)
def test_config_module_port_aliases(monkeypatch):
"""Ensure the config module keeps CONNECTION and PORT in sync."""
reloaded = importlib.reload(config)
monkeypatch.setattr(reloaded, "CONNECTION", "dev-tty", raising=False)
reloaded.PORT = "new-port"
assert reloaded.CONNECTION == "new-port"
assert reloaded.PORT == "new-port"
def test_queue_stringification_and_ordering():
"""Exercise queue payload formatting and priority ordering."""
+6 -28
View File
@@ -228,10 +228,9 @@ def mesh_module(monkeypatch):
def test_instance_domain_prefers_primary_env(mesh_module, monkeypatch):
"""Ensure the ingestor prefers ``INSTANCE_DOMAIN`` over the legacy variable."""
"""Ensure the ingestor reads ``INSTANCE_DOMAIN``."""
monkeypatch.setenv("INSTANCE_DOMAIN", "https://new.example")
monkeypatch.setenv("POTATOMESH_INSTANCE", "https://legacy.example")
try:
refreshed_instance = mesh_module.config._resolve_instance_domain()
@@ -242,26 +241,6 @@ def test_instance_domain_prefers_primary_env(mesh_module, monkeypatch):
assert mesh_module.INSTANCE == "https://new.example"
finally:
monkeypatch.delenv("INSTANCE_DOMAIN", raising=False)
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
mesh_module.config.INSTANCE = mesh_module.config._resolve_instance_domain()
mesh_module.INSTANCE = mesh_module.config.INSTANCE
def test_instance_domain_falls_back_to_legacy(mesh_module, monkeypatch):
"""Verify ``POTATOMESH_INSTANCE`` is used when ``INSTANCE_DOMAIN`` is unset."""
monkeypatch.delenv("INSTANCE_DOMAIN", raising=False)
monkeypatch.setenv("POTATOMESH_INSTANCE", "https://legacy-only.example")
try:
refreshed_instance = mesh_module.config._resolve_instance_domain()
mesh_module.config.INSTANCE = refreshed_instance
mesh_module.INSTANCE = refreshed_instance
assert refreshed_instance == "https://legacy-only.example"
assert mesh_module.INSTANCE == "https://legacy-only.example"
finally:
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
mesh_module.config.INSTANCE = mesh_module.config._resolve_instance_domain()
mesh_module.INSTANCE = mesh_module.config.INSTANCE
@@ -270,7 +249,6 @@ def test_instance_domain_infers_scheme_for_hostnames(mesh_module, monkeypatch):
"""Ensure bare hostnames are promoted to HTTPS URLs for ingestion."""
monkeypatch.setenv("INSTANCE_DOMAIN", "mesh.example.org")
monkeypatch.delenv("POTATOMESH_INSTANCE", raising=False)
try:
refreshed_instance = mesh_module.config._resolve_instance_domain()
@@ -1637,7 +1615,7 @@ def test_main_retries_interface_creation(mesh_module, monkeypatch):
raise RuntimeError("boom")
return iface, port
monkeypatch.setattr(mesh, "PORT", "/dev/ttyTEST")
monkeypatch.setattr(mesh, "CONNECTION", "/dev/ttyTEST")
monkeypatch.setattr(mesh, "_create_serial_interface", fake_create)
monkeypatch.setattr(mesh.threading, "Event", DummyEvent)
monkeypatch.setattr(mesh.signal, "signal", lambda *_, **__: None)
@@ -1709,7 +1687,7 @@ def test_main_reconnects_when_connection_event_clears(mesh_module, monkeypatch):
self._flag = True
return True
monkeypatch.setattr(mesh, "PORT", "/dev/ttyTEST")
monkeypatch.setattr(mesh, "CONNECTION", "/dev/ttyTEST")
monkeypatch.setattr(mesh, "_create_serial_interface", fake_create)
monkeypatch.setattr(mesh.threading, "Event", DummyStopEvent)
monkeypatch.setattr(mesh.signal, "signal", lambda *_, **__: None)
@@ -1773,7 +1751,7 @@ def test_main_recreates_interface_after_snapshot_error(mesh_module, monkeypatch)
def record_upsert(node_id, node):
upsert_calls.append(node_id)
monkeypatch.setattr(mesh, "PORT", "/dev/ttyTEST")
monkeypatch.setattr(mesh, "CONNECTION", "/dev/ttyTEST")
monkeypatch.setattr(mesh, "_create_serial_interface", fake_create)
monkeypatch.setattr(mesh, "upsert_node", record_upsert)
monkeypatch.setattr(mesh.threading, "Event", DummyEvent)
@@ -1795,7 +1773,7 @@ def test_main_exits_when_defaults_unavailable(mesh_module, monkeypatch):
def fail_default():
raise mesh.NoAvailableMeshInterface("no interface available")
monkeypatch.setattr(mesh, "PORT", None)
monkeypatch.setattr(mesh, "CONNECTION", None)
monkeypatch.setattr(mesh, "_create_default_interface", fail_default)
monkeypatch.setattr(mesh.signal, "signal", lambda *_, **__: None)
@@ -3203,7 +3181,7 @@ def test_queue_ingestor_heartbeat_enqueues_and_throttles(mesh_module, monkeypatc
def test_queue_ingestor_heartbeat_protocol_meshcore(mesh_module, monkeypatch):
"""Heartbeat payload must carry the configured PROVIDER as its protocol."""
"""Heartbeat payload must carry the configured PROTOCOL as its protocol."""
mesh = mesh_module
captured = []
+1 -1
View File
@@ -187,7 +187,7 @@ module PotatoMesh
#
# @return [String] semantic version identifier.
def version_fallback
"0.5.12"
"0.6.0"
end
# Default refresh interval for frontend polling routines.
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "potato-mesh",
"version": "0.5.12",
"version": "0.6.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "potato-mesh",
"version": "0.5.12",
"version": "0.6.0",
"devDependencies": {
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "potato-mesh",
"version": "0.5.12",
"version": "0.6.0",
"type": "module",
"private": true,
"scripts": {