Compare commits

..

3 Commits

Author SHA1 Message Date
missytake
2521698093 XXX add merge conflict, don't merge this 2025-11-06 10:19:16 +01:00
missytake
8c85dddfa3 XXX create merge conflict 2025-11-06 10:19:16 +01:00
Alexander Dietrich
acf2bdefdf Skip www_folder if merge conflict marker found 2025-11-06 10:19:16 +01:00
5 changed files with 48 additions and 52 deletions

View File

@@ -681,7 +681,7 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
check_config(config) check_config(config)
mail_domain = config.mail_domain mail_domain = config.mail_domain
from .www import build_webpages, get_paths from .www import build_webpages, find_merge_conflict, get_paths
server.group(name="Create vmail group", group="vmail", system=True) server.group(name="Create vmail group", group="vmail", system=True)
server.user(name="Create vmail user", user="vmail", group="vmail", system=True) server.user(name="Create vmail user", user="vmail", group="vmail", system=True)
@@ -823,6 +823,8 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
# if www_folder was set to a non-existing folder, skip upload # if www_folder was set to a non-existing folder, skip upload
if not www_path.is_dir(): if not www_path.is_dir():
logger.warning("Building web pages is disabled in chatmail.ini, skipping") logger.warning("Building web pages is disabled in chatmail.ini, skipping")
elif (path := find_merge_conflict(src_dir)) is not None:
logger.warning(f"Merge conflict found in {path}, skipping")
else: else:
# if www_folder is a hugo page, build it # if www_folder is a hugo page, build it
if build_dir: if build_dir:

View File

@@ -73,7 +73,9 @@ def query_dns(typ, domain):
# Query authoritative nameserver directly to bypass DNS cache. # Query authoritative nameserver directly to bypass DNS cache.
res = shell(f"dig @{ns} -r -q {domain} -t {typ} +short", print=log_progress) res = shell(f"dig @{ns} -r -q {domain} -t {typ} +short", print=log_progress)
return next((line for line in res.split("\n") if not line.startswith(';')), '') if res:
return res.split("\n")[0]
return ""
def check_zonefile(zonefile, verbose=True): def check_zonefile(zonefile, verbose=True):

View File

@@ -1,5 +1,3 @@
from copy import deepcopy
import pytest import pytest
from cmdeploy import remote from cmdeploy import remote
@@ -10,63 +8,38 @@ from cmdeploy.dns import check_full_zone, check_initial_remote_data
def mockdns_base(monkeypatch): def mockdns_base(monkeypatch):
qdict = {} qdict = {}
def shell(command, fail_ok=False, print=print): def query_dns(typ, domain):
if command.startswith("dig"): try:
if command == "dig": return qdict[typ][domain]
return "." except KeyError:
if "SOA" in command: return ""
return (
"delta.chat. 21600 IN SOA ns1.first-ns.de. dns.hetzner.com."
" 2025102800 14400 1800 604800 3600"
)
command_chunks = command.split()
domain, typ = command_chunks[4], command_chunks[6]
try:
return qdict[typ][domain]
except KeyError:
return ""
return remote.rshell.shell(command=command, fail_ok=fail_ok, print=print)
monkeypatch.setattr(remote.rdns, shell.__name__, shell) monkeypatch.setattr(remote.rdns, query_dns.__name__, query_dns)
return qdict return qdict
@pytest.fixture @pytest.fixture
def mockdns_expected(): def mockdns(mockdns_base):
return { mockdns_base.update(
"A": {"some.domain": "1.1.1.1"}, {
"AAAA": {"some.domain": "fde5:cd7a:9e1c:3240:5a99:936f:cdac:53ae"}, "A": {"some.domain": "1.1.1.1"},
"CNAME": { "AAAA": {"some.domain": "fde5:cd7a:9e1c:3240:5a99:936f:cdac:53ae"},
"mta-sts.some.domain": "some.domain.", "CNAME": {
"www.some.domain": "some.domain.", "mta-sts.some.domain": "some.domain.",
}, "www.some.domain": "some.domain.",
} },
}
)
@pytest.fixture(params=["plain", "with-dns-comments"])
def mockdns(request, mockdns_base, mockdns_expected):
mockdns_base.update(deepcopy(mockdns_expected))
match request.param:
case "plain":
pass
case "with-dns-comments":
for typ, data in mockdns_base.items():
for host, result in data.items():
mockdns_base[typ][host] = (
";; some unsuccessful attempt result\n"
"; and another with a single semicolon\n"
f"{result}"
)
return mockdns_base return mockdns_base
class TestPerformInitialChecks: class TestPerformInitialChecks:
def test_perform_initial_checks_ok1(self, mockdns, mockdns_expected): def test_perform_initial_checks_ok1(self, mockdns):
remote_data = remote.rdns.perform_initial_checks("some.domain") remote_data = remote.rdns.perform_initial_checks("some.domain")
assert remote_data["A"] == mockdns_expected["A"]["some.domain"] assert remote_data["A"] == mockdns["A"]["some.domain"]
assert remote_data["AAAA"] == mockdns_expected["AAAA"]["some.domain"] assert remote_data["AAAA"] == mockdns["AAAA"]["some.domain"]
assert remote_data["MTA_STS"] == mockdns_expected["CNAME"]["mta-sts.some.domain"] assert remote_data["MTA_STS"] == mockdns["CNAME"]["mta-sts.some.domain"]
assert remote_data["WWW"] == mockdns_expected["CNAME"]["www.some.domain"] assert remote_data["WWW"] == mockdns["CNAME"]["www.some.domain"]
@pytest.mark.parametrize("drop", ["A", "AAAA"]) @pytest.mark.parametrize("drop", ["A", "AAAA"])
def test_perform_initial_checks_with_one_of_A_AAAA(self, mockdns, drop): def test_perform_initial_checks_with_one_of_A_AAAA(self, mockdns, drop):

View File

@@ -4,6 +4,7 @@ import time
import traceback import traceback
import webbrowser import webbrowser
from pathlib import Path from pathlib import Path
import re
import markdown import markdown
from chatmaild.config import read_config from chatmaild.config import read_config
@@ -12,6 +13,9 @@ from jinja2 import Template
from .genqr import gen_qr_png_data from .genqr import gen_qr_png_data
_MERGE_CONFLICT_RE = re.compile(r"^<<<<<<<.+^=======.+^>>>>>>>", re.DOTALL | re.MULTILINE)
def snapshot_dir_stats(somedir): def snapshot_dir_stats(somedir):
d = {} d = {}
for path in somedir.iterdir(): for path in somedir.iterdir():
@@ -116,6 +120,17 @@ def _build_webpages(src_dir, build_dir, config):
return build_dir return build_dir
def find_merge_conflict(src_dir) -> Path:
assert src_dir.exists(), src_dir
result = None
for path in src_dir.iterdir():
if path.suffix in [".css", ".html", ".md"]:
if _MERGE_CONFLICT_RE.search(path.read_text()):
result = path
break
return result
def main(): def main():
path = importlib.resources.files(__package__) path = importlib.resources.files(__package__)
reporoot = path.joinpath("../../../").resolve() reporoot = path.joinpath("../../../").resolve()

View File

@@ -9,7 +9,11 @@
<title>{{ config.mail_domain }} {{ pagename }}</title> <title>{{ config.mail_domain }} {{ pagename }}</title>
<link rel="stylesheet" href="./main.css"> <link rel="stylesheet" href="./main.css">
<link rel="icon" href="/logo.svg"> <link rel="icon" href="/logo.svg">
<link rel=”mask-icon” href=”/logo.svg” color=”#000000"> <<<<<<< HEAD
<link rel="mask-icon" href="/logo.svg" color="#000000">
=======
<link rel=”mask-icon” href=”/logo.svg” color=”#000001">
>>>>>>> 2da7de8 (make logo slightly brighter)
</head> </head>
<body> <body>