mirror of
https://github.com/l5yth/potato-mesh.git
synced 2026-08-11 03:12:57 +02:00
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:
@@ -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 \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,7 +7,10 @@
|
||||
[](https://github.com/l5yth/potato-mesh/issues)
|
||||
[](https://matrix.to/#/#potatomesh:dod.ngo)
|
||||
|
||||
A federated, Meshtastic-powered node dashboard for your local community.
|
||||
[](https://meshtastic.org)
|
||||
[](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)
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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
@@ -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}
|
||||
|
||||
Generated
+1
-1
@@ -969,7 +969,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "potatomesh-matrix-bridge"
|
||||
version = "0.5.12"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
|
||||
[package]
|
||||
name = "potatomesh-matrix-bridge"
|
||||
version = "0.5.12"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
+1
-3
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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
@@ -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 = []
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Generated
+2
-2
@@ -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
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "potato-mesh",
|
||||
"version": "0.5.12",
|
||||
"version": "0.6.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user