xeps/tools/accept.py

225 lines
6.0 KiB
Python

#!/usr/bin/env python3
import pathlib
import re
import shutil
import subprocess
import sys
import xml.etree.ElementTree as etree
from datetime import datetime, timedelta
from xeplib import load_xepinfos, Status, choose
DEFAULT_XEPLIST_PATH = "./build/xeplist.xml"
XEP_FILENAME_RE = re.compile(r"xep-(\d+)\.xml")
BLANK_NUMBER = re.compile("<number>[xX]{4}</number>")
PROTOXEP_STATUS = "<status>ProtoXEP</status>"
EXPERIMENTAL_STATUS = "<status>Experimental</status>"
REVISION_RE = re.compile(r"\s+<revision>")
REVISION_TEMPLATE = """
<revision>
<version>{version}</version>
<date>{now:%Y-%m-%d}</date>
<initials>XEP Editor ({initials})</initials>
<remark>Accepted by vote of {approving_body} on {date}.</remark>
</revision>"""
def accept_xep(number, last_version,
initials,
approving_body,
votedate):
filename = "xep-{:04d}.xml".format(number)
with open(filename, "r") as f:
xep_text = f.read()
if PROTOXEP_STATUS not in xep_text:
raise ValueError("cannot find experimental status in XEP text")
# this is so incredibly evil ...
xep_text = xep_text.replace(PROTOXEP_STATUS, EXPERIMENTAL_STATUS, 1)
xep_text, n = BLANK_NUMBER.subn("<number>{:04d}</number>".format(number),
xep_text,
1)
if n == 0:
raise ValueError("cannot find number placeholder in XEP text")
revision_match = REVISION_RE.search(xep_text)
version = last_version.split(".")
if len(version) == 1:
version.append("1")
else:
version[1] = str(int(version[1]) + 1)
del version[2:]
version.append("0")
xep_text = (
xep_text[:revision_match.start()] +
REVISION_TEMPLATE.format(
now=datetime.utcnow(),
version=".".join(version),
initials=initials,
date=votedate,
approving_body=approving_body,
) + xep_text[revision_match.start():]
)
with open(filename, "w") as f:
f.write(xep_text)
f.flush()
subprocess.check_call(["make", "build/xeplist.xml"])
with open("xep.ent", "ab") as f:
f.write(subprocess.check_output(["python3", "tools/makeent.py", str(number)]))
def isodate(s):
return datetime.strptime(s, "%Y-%m-%d")
def get_next_xep_number(accepted):
return max(accepted.keys()) + 1
def main():
import argparse
parser = argparse.ArgumentParser(
description="Accept an inbox XEP."
)
parser.add_argument(
"-l", "--xeplist",
type=argparse.FileType("rb"),
default=None,
help="XEP list to use (defaults to {})".format(DEFAULT_XEPLIST_PATH)
)
parser.add_argument(
"-y", "--yes",
dest="ask",
action="store_false",
help="Assume default answer to all questions.",
default=True,
)
parser.add_argument(
"-f", "--force",
dest="force",
action="store_true",
default=False,
help="Force acceptance even if suspicious.",
)
parser.add_argument(
"-c", "--commit",
default=False,
action="store_true",
help="Make a git commit",
)
parser.add_argument(
"item",
help="Inbox name"
)
parser.add_argument(
"votedate",
type=isodate,
help="The date of the vote, in ISO format (%%Y-%%m-%%d)."
)
parser.add_argument(
"initials",
help="Your editor initials"
)
args = parser.parse_args()
if args.item.endswith(".xml"):
# strip the path
p = pathlib.Path(args.item)
args.item = p.parts[-1].rsplit(".")[0]
if args.xeplist is None:
args.xeplist = open(DEFAULT_XEPLIST_PATH, "rb")
if args.xeplist is not None:
with args.xeplist as f:
tree = etree.parse(f)
accepted, inbox = load_xepinfos(tree)
try:
xepinfo = inbox[args.item]
except KeyError:
print("no such inbox xep: {!r}".format(args.item), file=sys.stderr)
print("maybe run make build/xeplist.xml first?", file=sys.stderr)
sys.exit(1)
new_number = get_next_xep_number(accepted)
new_filename = pathlib.Path(".") / "xep-{:04d}.xml".format(new_number)
inbox_path = pathlib.Path("inbox") / "{}.xml".format(args.item)
if new_filename.exists():
raise FileExistsError(
"Internal error: XEP file does already exist! ({})".format(
new_filename
)
)
if not inbox_path.exists():
print("inbox file does not exist or is not readable: {}".format(
inbox_path
))
if args.ask:
print("I am going to accept:")
print()
print(" Title: {!r}".format(xepinfo["title"]))
print(" Abstract: {!r}".format(xepinfo["abstract"]))
print(" Last Revision: {} ({})".format(
xepinfo["last_revision"]["date"].date(),
xepinfo["last_revision"]["version"],
))
print()
print("as new XEP-{:04d}.".format(new_number))
print()
choice = choose("Is this correct? [y]es, [n]o: ", "yn", eof="n")
if choice != "y":
print("aborted at user request")
sys.exit(2)
shutil.copy(str(inbox_path), str(new_filename))
accept_xep(new_number,
xepinfo["last_revision"]["version"],
args.initials,
xepinfo["approver"],
args.votedate.date())
if args.commit:
subprocess.check_call([
"git", "reset", "HEAD", ".",
])
subprocess.check_call([
"git", "add", new_filename.parts[-1], "xep.ent",
])
if args.ask:
flags = ["-ve"]
else:
flags = []
subprocess.check_call(
[
"git", "commit", "-m", "Accept {} as XEP-{:04}".format(
inbox_path,
new_number,
)
] + flags
)
if __name__ == "__main__":
main()