Merge branch 'feature/updater' into feature/store-i18n

Brings in feature/updater accb22da75, which carries main 5995766f58.

Conflict resolutions:
- web-app-fider: main's tenant bootstrap (01_tenant.yml) runs first on
  the stack host, OIDC setup moves to 02_setup_oidc.yml, design to
  03_design.yml; flat includes so main's gate test reads them.
- sys-front-tls test_nginx_ssl_header: the tls fake maps the domain term
  store-i18n passes back to its app.
- group_vars 04_webserver.yml, sys-svc-proxy CSP header: store-i18n's
  webserver_resource lookup plus #1256's WEBSERVER_SECURITY_HEADERS and
  the security_headers.lua include after the CSP block.
- web-app-nextcloud: updater's decoupled Talk stack (Janus, signaling,
  NATS, recording builds) replaces aio-talk; store-i18n's per-network
  Talk backends move from the wrapper script into signaling.conf.j2 urls
  (NEXTCLOUD_HPB_SIGNALING_BACKEND_URLS from network_urls, canonical
  first); IS_COMPOSE_MODE/IS_SWARM_MODE booleans kept; the integration
  OpenProject digest stays delegated to STACK_HOST.
- users lookup docs, token-store write_many, cache README, meta info
  lint: both sides combined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-30 00:40:05 +01:00
commit 5e8890d208
130 files changed
+4303 -898

No files matched your search

Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,134 @@
from __future__ import annotations
import os
import subprocess
import tempfile
import unittest
from pathlib import Path
from utils.cache.files import PROJECT_ROOT
SCRIPT = PROJECT_ROOT / "scripts" / "git" / "assert" / "main_merged.sh"
MAIN_COMMIT_ENV = "INFINITO_MAIN_COMMIT" # nocheck: git maintainer tooling knob, not a deployment env key
class MainMergedFixture:
"""A bare ``origin`` whose ``main`` moved on after ``head`` branched off it.
Args:
tmp: directory that holds the bare remote and its two clones.
"""
def __init__(self, tmp: str) -> None:
root = Path(tmp)
inherited = {
k: v
for k, v in os.environ.items()
if not k.startswith(("GIT_", "INFINITO_"))
}
self.env = {
**inherited,
"GIT_CONFIG_GLOBAL": os.devnull,
"GIT_CONFIG_NOSYSTEM": "1",
"GIT_AUTHOR_NAME": "test",
"GIT_AUTHOR_EMAIL": "test@example.invalid",
"GIT_COMMITTER_NAME": "test",
"GIT_COMMITTER_EMAIL": "test@example.invalid",
}
origin = root / "origin.git"
maintainer = root / "maintainer"
self.head = root / "head"
self.git(root, "init", "--bare", "-b", "main", str(origin))
self.git(root, "init", "-b", "main", str(maintainer))
self.git(maintainer, "remote", "add", "origin", str(origin))
self.base = self.commit(maintainer, "base")
self.git(maintainer, "push", "origin", "main")
self.git(root, "clone", str(origin), str(self.head))
self.git(self.head, "switch", "-c", "feature")
self.commit(self.head, "feature")
self.main_tip = self.commit(maintainer, "main moved on")
self.git(maintainer, "push", "origin", "main")
def git(self, cwd: Path, *args: str) -> str:
return subprocess.run(
["git", *args],
cwd=cwd,
env=self.env,
capture_output=True,
text=True,
check=True,
).stdout.strip()
def commit(self, repo: Path, message: str) -> str:
self.git(repo, "commit", "--allow-empty", "-m", message)
return self.git(repo, "rev-parse", "HEAD")
def merge_main(self) -> None:
self.git(self.head, "pull", "--no-rebase", "--no-edit", "origin", "main")
def run_script(self, main_commit: str | None) -> subprocess.CompletedProcess:
"""Run the script in ``head``.
Args:
main_commit: value of the main-commit override; ``None`` leaves it unset.
"""
env = dict(self.env)
if main_commit is not None:
env[MAIN_COMMIT_ENV] = main_commit
return subprocess.run(
["bash", str(SCRIPT)],
cwd=self.head,
env=env,
capture_output=True,
text=True,
check=False,
)
class TestMainMergedPinnedCommit(unittest.TestCase):
def test_contained_commit_passes_after_main_moved_on(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
fixture = MainMergedFixture(tmp)
result = fixture.run_script(fixture.base)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn(f"origin/main@{fixture.base} is fully merged", result.stderr)
def test_commit_missing_from_head_fails(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
fixture = MainMergedFixture(tmp)
result = fixture.run_script(fixture.main_tip)
self.assertEqual(result.returncode, 1, result.stderr)
self.assertIn(
f"origin/main@{fixture.main_tip} has 1 commit(s) not merged into HEAD",
result.stderr,
)
def test_empty_commit_fails_loudly(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
fixture = MainMergedFixture(tmp)
result = fixture.run_script("")
self.assertEqual(result.returncode, 2, result.stderr)
self.assertIn("must be a full commit SHA", result.stderr)
class TestMainMergedLiveTip(unittest.TestCase):
def test_live_tip_missing_from_head_fails(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
fixture = MainMergedFixture(tmp)
result = fixture.run_script(None)
self.assertEqual(result.returncode, 1, result.stderr)
self.assertIn(
"origin/main has 1 commit(s) not merged into HEAD", result.stderr
)
def test_merged_live_tip_passes(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
fixture = MainMergedFixture(tmp)
fixture.merge_main()
result = fixture.run_script(None)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("origin/main is fully merged into HEAD", result.stderr)
if __name__ == "__main__":
unittest.main()