feat: serve an APPVERSIONS.json index file to clients via IMAP metadata

This is designed to help implement self-updating APKs (and later other clients),
see counterpart https://github.com/chatmail/core/pull/8557
This commit is contained in:
holger krekel
2026-08-12 10:21:46 +02:00
parent dc8e0a34a2
commit 381a1e30e0
11 changed files with 118 additions and 2 deletions
+6 -1
View File
@@ -35,7 +35,7 @@ from .nginx.deployer import NginxDeployer
from .opendkim.deployer import OpendkimDeployer
from .postfix.deployer import PostfixDeployer
from .selfsigned.deployer import SelfSignedTlsDeployer
from .www import build_webpages, find_merge_conflict, get_paths
from .www import build_webpages, find_merge_conflict, get_paths, get_reporoot
class Port(FactBase):
@@ -126,6 +126,11 @@ def _configure_remote_venv_with_chatmaild(deployer, config) -> None:
dest=remote_chatmail_inipath,
)
deployer.put_file(
src=get_reporoot().joinpath("APPVERSIONS.json").open("rb"),
dest=str(config.appversions_path),
)
deployer.remove_file("/etc/cron.d/chatmail-metrics")
deployer.remove_file("/var/www/html/metrics")
@@ -51,6 +51,16 @@ class TestMetadataTokens:
assert res == b"1111 2222"
assert b"Getmetadata completed" in client.readline()
def test_get_appversions(self, imap_mailbox):
"get app version information shipped with the relay"
client = imap_mailbox.client
client.send(b'a01 GETMETADATA "" /shared/vendor/deltachat/appversions\n')
res = client.readline()
assert res[:1] == b"*"
res = client.readline().strip().rstrip(b")")
assert b'"clients":' in res
assert b"Getmetadata completed" in client.readline()
class TestEndToEndDeltaChat:
"Tests that use Delta Chat accounts on the chat mail instance."
@@ -0,0 +1,20 @@
import json
from cmdeploy.www import get_reporoot
ALLOWED_URL_PREFIXES = (
"https://github.com/deltachat/",
"https://download.delta.chat/",
)
def test_appversions_schema():
data = json.loads(get_reporoot().joinpath("APPVERSIONS.json").read_text())
assert data["clients"]
for client in data["clients"]:
assert isinstance(client["clientId"], str)
for source in client["sources"]:
assert isinstance(source["sourceId"], str)
assert isinstance(source["versionInteger"], int)
assert isinstance(source["versionString"], str)
assert source["downloadUrl"].startswith(ALLOWED_URL_PREFIXES)
+5 -1
View File
@@ -35,8 +35,12 @@ def prepare_template(source):
return render_vars, page_layout
def get_reporoot() -> Path:
return (Path(__file__).resolve() / "../../../../").resolve()
def get_paths(config) -> (Path, Path, Path):
reporoot = (Path(__file__).resolve() / "../../../../").resolve()
reporoot = get_reporoot()
www_path = Path(config.www_folder)
# if www_folder was not set, use default directory
if config.www_folder == "":