sqlinsight-pro/deploy/forum/render.py
Søren Eggert Lundsteen Olsen ce49866d2b
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 1m0s
deploy community / deploy (push) Successful in 0s
deploy hub / deploy (push) Successful in 23s
CI / MSI-installer (push) Successful in 36s
Forum: one SEOSoft forum at forum.seosoft.dk, SQLInsight Pro as a category
The forum is shared by SEOSoft's products, so SQLInsight Pro becomes the
top-level category "sqlinsight" with the existing categories beneath it.

- render.py/install.sh: default hostname forum.seosoft.dk
- configure.py: forum-wide settings ("SEOSoft Forum") kept apart from the
  SQLInsight Pro category tree; subcategories found via /site.json; the
  welcome topic is pinned in the category, not on the shared front page
- Community site: ForumCategory option. The front page lists only that
  category's topics (c/<slug>/l/latest.json); forum links go to the
  category, topic links stay at the forum root
- Web: default ForumUrl https://forum.seosoft.dk/c/sqlinsight. The old
  forum.sqlinsight.seosoft.dk stays as a redirect for installed versions

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 10:34:29 +02:00

66 lines
2.3 KiB
Python
Executable file

#!/usr/bin/env python3
"""Fills in deploy/forum/app.yml from environment variables and writes the result (mode 600).
render.py <template> <output>
Required: FORUM_BIND KNOWN_PROXY FORUM_ADMIN_EMAIL FORUM_SMTP_ADDRESS FORUM_SMTP_USER FORUM_SMTP_PASSWORD
Optional: FORUM_HOSTNAME (forum.seosoft.dk), FORUM_PORT (8082), FORUM_SMTP_PORT (587), FORUM_NOTIFICATION_EMAIL (FORUM_SMTP_USER),
FORUM_WORKERS (4), FORUM_DB_SHARED_BUFFERS (512MB)
"""
import ipaddress
import os
import re
import sys
DEFAULTS = {
"FORUM_HOSTNAME": "forum.seosoft.dk",
"FORUM_PORT": "8082",
"FORUM_SMTP_PORT": "587",
"FORUM_WORKERS": "4",
"FORUM_DB_SHARED_BUFFERS": "512MB",
}
# Values that end up unquoted in YAML or in nginx/docker syntax must look exactly like this.
FORMATS = {
"FORUM_PORT": r"\d{1,5}",
"FORUM_SMTP_PORT": r"\d{1,5}",
"FORUM_WORKERS": r"\d{1,2}",
"FORUM_DB_SHARED_BUFFERS": r"\d+[MG]B",
"FORUM_HOSTNAME": r"[a-z0-9.-]+",
}
def value(name):
v = os.environ.get(name) or DEFAULTS.get(name)
if name == "FORUM_NOTIFICATION_EMAIL" and not v:
v = os.environ.get("FORUM_SMTP_USER")
if not v:
sys.exit(f"ERROR: {name} is not set")
if "\n" in v or "{{" in v:
sys.exit(f"ERROR: {name} contains a line break or {{{{")
if name in FORMATS and not re.fullmatch(FORMATS[name], v):
sys.exit(f"ERROR: {name} has an unexpected format")
if name in ("FORUM_BIND", "KNOWN_PROXY"):
try:
ipaddress.ip_network(v, strict=False) if name == "KNOWN_PROXY" else ipaddress.ip_address(v)
except ValueError:
sys.exit(f"ERROR: {name} is not an IP address" + (" or network" if name == "KNOWN_PROXY" else ""))
# All free-text values sit in single-quoted YAML strings.
return v.replace("'", "''")
def main():
if len(sys.argv) != 3:
sys.exit(__doc__)
template, output = sys.argv[1], sys.argv[2]
with open(template, encoding="utf-8") as f:
text = f.read()
names = sorted(set(re.findall(r"\{\{([A-Z_]+)\}\}", text)))
text = re.sub(r"\{\{([A-Z_]+)\}\}", lambda m: value(m.group(1)), text)
fd = os.open(output, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "w", encoding="utf-8") as f:
f.write(text)
print(f"{output}: {len(names)} values filled in")
if __name__ == "__main__":
main()