diff --git a/dzonegit.py b/dzonegit.py index 8ddb060..2478366 100644 --- a/dzonegit.py +++ b/dzonegit.py @@ -6,9 +6,11 @@ import subprocess import re import time import datetime +import json from collections import namedtuple from hashlib import sha256 from pathlib import Path +from string import Template class HookException(ValueError): @@ -265,6 +267,51 @@ def replace_serial(path, oldserial, newserial): path.write_text(updated) +def template_config(checkoutpath, template): + """ Recursively find all *.zone files and template config file using + a simple JSON based template like this: + + { + "header": "# Managed by dzonegit, do not edit.\n", + "footer": "", + "item": " - zone: \"$zonename\"\n file: \"$zonefile\"\n $zonevar\n", + "defaultvar": "template: default", + "zonevars": { + "example.com": "template: signed" + } + } + + Available placeholders are: + - $datetime - timestamp of file creation + - $zonename - zone name, without trailing dot + - $zonefile - full path to zone file + - $zonevar - per-zone specific variables, content of `defaultvar` if + not defined for current zone + """ + tpl = json.loads(template) + headertpl = Template(tpl.get("header", "")) + footertpl = Template(tpl.get("footer", "")) + itemtpl = Template(tpl.get("item", "")) + defaultvar = tpl.get("defaultvar", "") + zonevars = tpl.get("zonevars", dict()) + out = list() + zones = set() + mapping = {"datetime": datetime.datetime.now().strftime("%c")} + out.append(headertpl.substitute(mapping)) + for f in Path(checkoutpath).glob("**/*.zone"): + zonename = get_zone_name(f, f.read_bytes()) + if zonename in zones: + continue # Safety net in case duplicate zone file is found + zones.add(zonename) + zonevar = zonevars[zonename] if zonename in zonevars else defaultvar + out.append(itemtpl.substitute( + mapping, zonename=zonename, + zonefile=str(f), zonevar=zonevar, + )) + out.append(footertpl.substitute(mapping)) + return "\n".join(out) + + def do_commit_checks(against, revision=None, autoupdate_serial=False): try: if not get_config("dzonegit.ignorewhitespaceerrors", bool): @@ -323,16 +370,23 @@ def post_receive(stdin=sys.stdin): added or delefed. """ suffixes = list(str(n) if n else "" for n in range(10)) - for s in suffixes: - d = get_config("dzonegit.checkoutpath{}".format(s)) - if d: - print("Checking out repository into {}…".format(d)) - subprocess.run( - ["git", "checkout", "-f", "master"], - check=True, - env=dict(os.environ, GIT_WORK_TREE=d), + checkoutpath = get_config("dzonegit.checkoutpath") + if checkoutpath: + print("Checking out repository into {}…".format(checkoutpath)) + subprocess.run( + ["git", "checkout", "-f", "master"], + check=True, + env=dict(os.environ, GIT_WORK_TREE=checkoutpath), + ) + for s in suffixes: + cfpath = get_config("dzonegit.conffilepath{}".format(s)) + tplpath = get_config("dzonegit.conffiletemplate{}".format(s)) + if cfpath is None or tplpath is None: + continue + print("Templating config file {}…".format(cfpath)) + Path(cfpath).write_text( + template_config(checkoutpath, Path(tplpath).read_text()), ) - # TODO config if stdin.isatty(): raise SystemExit( diff --git a/test_dzonegit.py b/test_dzonegit.py index feeaf10..9c4673c 100644 --- a/test_dzonegit.py +++ b/test_dzonegit.py @@ -273,10 +273,23 @@ def test_post_receive(git_dir): git_dir.chdir() revisions = "{} {} ".format("0"*40, dzonegit.get_head()) stdin = StringIO(revisions + "refs/heads/master\n") - codir1 = git_dir.mkdir("co1") - codir2 = git_dir.mkdir("co2") - subprocess.call(["git", "config", "dzonegit.checkoutpath", str(codir1)]) - subprocess.call(["git", "config", "dzonegit.checkoutpath9", str(codir2)]) + codir = git_dir.mkdir("co") + subprocess.call(["git", "config", "dzonegit.checkoutpath", str(codir)]) dzonegit.post_receive(stdin) - assert codir1.join("dummy.zone").check() - assert codir2.join("dummy.zone").check() + assert codir.join("dummy.zone").check() + + +def test_template_config(git_dir): + template = r"""{ + "header": "# Managed by dzonegit on $datetime, do not edit.\n", + "footer": "# This is the end", + "item": " - zone: \"$zonename\"\n file: \"$zonefile\"\n $zonevar\n", + "defaultvar": "template: default", + "zonevars": { + "example.com": "template: signed" + } +}""" + output = dzonegit.template_config(str(git_dir), template) + assert output.startswith("# Managed by dzonegit") + assert " - zone: \"dummy\"\n file: \"" in output + assert output.endswith("# This is the end")