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
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>
66 lines
2.3 KiB
Python
Executable file
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()
|