def main(): import argparse parser = argparse.ArgumentParser( description=wraptext(DESCRIPTION), epilog=wraptext(EPILOG), formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "-c", "--config", metavar="FILE", type=argparse.FileType("r"), help="Configuration file", ) parser.add_argument( "-y", dest="ask_confirmation", default=True, action="store_false", help="'I trust this script to do the right thing and send emails" "without asking for confirmation.'" ) parser.add_argument( "--no-proto", dest="include_protoxep", default=True, action="store_false", help="Do not announce ProtoXEPs", ) parser.add_argument( "-n", "--dry-run", dest="dry_run", action="store_true", default=False, help="Instead of sending emails, print them to stdout (implies -y)", ) parser.add_argument( "--no-editorial", action="store_false", default=True, dest="include_editorial", help="Do not announce editorial changes." ) parser.add_argument( "--no-non-editorial", action="store_false", default=True, dest="include_non_editorial", help="Do not announce non-editorial changes." ) parser.add_argument( "old", type=argparse.FileType("rb"), help="Old xep-infos XML file", ) parser.add_argument( "new", type=argparse.FileType("rb"), help="New xep-infos XML file", ) parser.add_argument( "to", nargs="+", help="The mail addresses to send the update mails to." ) args = parser.parse_args() can_be_interactive = ( os.isatty(sys.stdin.fileno()) and os.isatty(sys.stdout.fileno()) ) if args.dry_run: args.ask_confirmation = False if args.ask_confirmation and not can_be_interactive: print("Cannot ask for confirmation (stdio is not a TTY), but -y is", "not given either. Aborting.", sep="\n", file=sys.stderr) sys.exit(2) config = configparser.ConfigParser() if args.config is not None: config.read_file(args.config) with args.old as f: tree = etree.parse(f) old_accepted, old_proto = load_xepinfos(tree) with args.new as f: tree = etree.parse(f) new_accepted, new_proto = load_xepinfos(tree) old_xeps = set(old_accepted.keys()) new_xeps = set(new_accepted.keys()) common_xeps = old_xeps & new_xeps added_xeps = new_xeps - old_xeps added_protos = set(new_proto.keys()) - set(old_proto.keys()) updates = [] for common_xep in common_xeps: old_info = old_accepted[common_xep] new_info = new_accepted[common_xep] action = diff_infos(old_info, new_info) if action == Action.UPDATE and not filter_bump_level( extract_version(old_info), extract_version(new_info), args.include_editorial, args.include_non_editorial): continue if action is not None: updates.append((common_xep, action, new_info)) for added_xep in added_xeps: old_info = dummy_info(added_xep) new_info = new_accepted[added_xep] action = diff_infos(old_info, new_info) if action is not None: updates.append((added_xep, action, new_info)) if args.include_protoxep: for added_proto in added_protos: old_info = dummy_info('xxxx') new_info = new_proto[added_proto] action = diff_infos(old_info, new_info) if action is not None: updates.append((added_proto, action, new_info)) if args.dry_run: smtpconn = make_fake_smtpconn() else: if can_be_interactive: interactively_extend_smtp_config(config) try: smtpconn = make_smtpconn(config) except (configparser.NoSectionError, configparser.NoOptionError) as exc: print("Missing configuration: {}".format(exc), file=sys.stderr) print("(cannot ask for configuration on stdio because it is " "not a TTY)", file=sys.stderr) sys.exit(3) try: for id_, action, info in updates: if action == Action.PROTO: mail = make_proto_mail(info) else: mail = make_nonproto_mail(action, info) mail["Date"] = datetime.utcnow() mail["From"] = config.get("smtp", "from") mail["To"] = args.to if args.ask_confirmation: print() print("---8<---") print(mail.as_string()) print("--->8---") print() choice = choose( "Send this email? [y]es, [n]o, [a]bort: ", "yna", eof="a", ) if choice == "n": continue elif choice == "a": print("Exiting on user request.", file=sys.stderr) sys.exit(4) smtpconn.send_message(mail) finally: smtpconn.close()
def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument( "-c", "--config", metavar="FILE", type=argparse.FileType("r"), help="Configuration file", ) parser.add_argument( "-y", dest="ask_confirmation", default=True, action="store_false", help="'I trust this script to do the right thing and send emails" "without asking for confirmation.'" ) parser.add_argument( "-n", "--dry-run", dest="dry_run", action="store_true", default=False, help="Instead of sending emails, print them to stdout (implies -y)", ) parser.add_argument( "--duration", "-d", metavar="DAYS", default=14, help="Duration of the CFE in days (default and at least: 14)", type=int, ) parser.add_argument( "--xeplist", default=None, type=argparse.FileType("r") ) parser.add_argument( "-x", "--xep", type=int, dest="xeps", action="append", default=[], help="XEP(s) to issue a CFE for" ) parser.add_argument( "to", nargs="+", help="The mail addresses to send the update mails to." ) args = parser.parse_args() can_be_interactive = ( os.isatty(sys.stdin.fileno()) and os.isatty(sys.stdout.fileno()) ) if not args.xeps: print("nothing to do (use -x/--xep)", file=sys.stderr) sys.exit(1) if args.duration < 14: print("duration must be at least 14", file=sys.stderr) sys.exit(1) enddate = (datetime.utcnow() + timedelta(days=args.duration)).date() if args.dry_run: args.ask_confirmation = False if args.ask_confirmation and not can_be_interactive: print("Cannot ask for confirmation (stdio is not a TTY), but -y is", "not given either. Aborting.", sep="\n", file=sys.stderr) sys.exit(2) config = configparser.ConfigParser() if args.config is not None: config.read_file(args.config) if args.xeplist is None: args.xeplist = open("build/xeplist.xml", "rb") with args.xeplist as f: tree = etree.parse(f) accepted, _ = load_xepinfos(tree) matched_xeps = [] has_error = False for num in args.xeps: try: info = accepted[num] except KeyError: print("no such xep: {}".format(num), file=sys.stderr) has_error = True continue if info["status"] != Status.DRAFT: print("XEP-{:04d} is in {}, but must be Draft".format( num, info["status"].value, )) has_error = True continue matched_xeps.append(info) if has_error: sys.exit(1) if args.dry_run: smtpconn = make_fake_smtpconn() else: if can_be_interactive: interactively_extend_smtp_config(config) try: smtpconn = make_smtpconn(config) except (configparser.NoSectionError, configparser.NoOptionError) as exc: print("Missing configuration: {}".format(exc), file=sys.stderr) print("(cannot ask for configuration on stdio because it is " "not a TTY)", file=sys.stderr) sys.exit(3) try: for info in matched_xeps: mail = make_mail(info, enddate) mail["Date"] = datetime.utcnow() mail["From"] = config.get("smtp", "from") mail["To"] = args.to if args.ask_confirmation: print() print("---8<---") print(mail.as_string()) print("--->8---") print() choice = choose( "Send this email? [y]es, [n]o, [a]bort: ", "yna", eof="a", ) if choice == "n": continue elif choice == "a": print("Exiting on user request.", file=sys.stderr) sys.exit(4) smtpconn.send_message(mail) finally: smtpconn.close()
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], ]) if args.ask: flags = ["-ve"] else: flags = [] subprocess.check_call( [ "git", "commit", "-m", "Accept {} as XEP-{:04}".format( inbox_path, new_number, ) ] + flags )
def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument( "-c", "--config", metavar="FILE", type=argparse.FileType("r"), help="Configuration file", ) parser.add_argument( "-y", dest="ask_confirmation", default=True, action="store_false", help="'I trust this script to do the right thing and send emails" "without asking for confirmation.'") parser.add_argument( "-n", "--dry-run", dest="dry_run", action="store_true", default=False, help="Instead of sending emails, print them to stdout (implies -y)", ) parser.add_argument( "--duration", "-d", metavar="DAYS", default=14, help="Duration of the CFE in days (default and at least: 14)", type=int, ) parser.add_argument("--xeplist", default=None, type=argparse.FileType("r")) parser.add_argument("-x", "--xep", type=int, dest="xeps", action="append", default=[], help="XEP(s) to issue a CFE for") parser.add_argument("to", nargs="+", help="The mail addresses to send the update mails to.") args = parser.parse_args() can_be_interactive = (os.isatty(sys.stdin.fileno()) and os.isatty(sys.stdout.fileno())) if not args.xeps: print("nothing to do (use -x/--xep)", file=sys.stderr) sys.exit(1) if args.duration < 14: print("duration must be at least 14", file=sys.stderr) sys.exit(1) enddate = (datetime.utcnow() + timedelta(days=args.duration)).date() if args.dry_run: args.ask_confirmation = False if args.ask_confirmation and not can_be_interactive: print("Cannot ask for confirmation (stdio is not a TTY), but -y is", "not given either. Aborting.", sep="\n", file=sys.stderr) sys.exit(2) config = configparser.ConfigParser() if args.config is not None: config.read_file(args.config) if args.xeplist is None: args.xeplist = open("build/xeplist.xml", "rb") with args.xeplist as f: tree = etree.parse(f) accepted, _ = load_xepinfos(tree) matched_xeps = [] has_error = False for num in args.xeps: try: info = accepted[num] except KeyError: print("no such xep: {}".format(num), file=sys.stderr) has_error = True continue if info["status"] != Status.DRAFT: print("XEP-{:04d} is in {}, but must be Draft".format( num, info["status"].value, )) has_error = True continue matched_xeps.append(info) if has_error: sys.exit(1) if args.dry_run: smtpconn = make_fake_smtpconn() else: if can_be_interactive: interactively_extend_smtp_config(config) try: smtpconn = make_smtpconn(config) except (configparser.NoSectionError, configparser.NoOptionError) as exc: print("Missing configuration: {}".format(exc), file=sys.stderr) print( "(cannot ask for configuration on stdio because it is " "not a TTY)", file=sys.stderr) sys.exit(3) try: for info in matched_xeps: mail = make_mail(info, enddate) mail["Date"] = datetime.utcnow() mail["From"] = config.get("smtp", "from") mail["To"] = args.to if args.ask_confirmation: print() print("---8<---") print(mail.as_string()) print("--->8---") print() choice = choose( "Send this email? [y]es, [n]o, [a]bort: ", "yna", eof="a", ) if choice == "n": continue elif choice == "a": print("Exiting on user request.", file=sys.stderr) sys.exit(4) smtpconn.send_message(mail) finally: smtpconn.close()
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], ]) if args.ask: flags = ["-ve"] else: flags = [] subprocess.check_call([ "git", "commit", "-m", "Accept {} as XEP-{:04}".format( inbox_path, new_number, ) ] + flags)
def main(): import argparse parser = argparse.ArgumentParser( description=wraptext(DESCRIPTION), epilog=wraptext(EPILOG), formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "-c", "--config", metavar="FILE", type=argparse.FileType("r"), help="Configuration file", ) parser.add_argument( "-y", dest="ask_confirmation", default=True, action="store_false", help="'I trust this script to do the right thing and send emails" "without asking for confirmation.'" ) parser.add_argument( "--no-proto", dest="process_proto", default=True, action="store_false", help="Disable processing of ProtoXEPs.", ) parser.add_argument( "-n", "--dry-run", dest="dry_run", action="store_true", default=False, help="Instead of sending emails, print them to stdout (implies -y)", ) parser.add_argument( "old", type=argparse.FileType("rb"), help="Old xep-infos XML file", ) parser.add_argument( "new", type=argparse.FileType("rb"), help="New xep-infos XML file", ) parser.add_argument( "to", nargs="+", help="The mail addresses to send the update mails to." ) args = parser.parse_args() can_be_interactive = ( os.isatty(sys.stdin.fileno()) and os.isatty(sys.stdout.fileno()) ) if args.dry_run: args.ask_confirmation = False if args.ask_confirmation and not can_be_interactive: print("Cannot ask for confirmation (stdio is not a TTY), but -y is", "not given either. Aborting.", sep="\n", file=sys.stderr) sys.exit(2) config = configparser.ConfigParser() if args.config is not None: config.read_file(args.config) with args.old as f: tree = etree.parse(f) old_accepted, old_proto = load_xepinfos(tree) with args.new as f: tree = etree.parse(f) new_accepted, new_proto = load_xepinfos(tree) old_xeps = set(old_accepted.keys()) new_xeps = set(new_accepted.keys()) common_xeps = old_xeps & new_xeps added_xeps = new_xeps - old_xeps added_protos = set(new_proto.keys()) - set(old_proto.keys()) updates = [] for common_xep in common_xeps: old_info = old_accepted[common_xep] new_info = new_accepted[common_xep] action = diff_infos(old_info, new_info) if action is not None: updates.append((common_xep, action, new_info)) for added_xep in added_xeps: old_info = dummy_info(added_xep) new_info = new_accepted[added_xep] action = diff_infos(old_info, new_info) if action is not None: updates.append((added_xep, action, new_info)) if args.process_proto: for added_proto in added_protos: old_info = dummy_info('xxxx') new_info = new_proto[added_proto] action = diff_infos(old_info, new_info) if action is not None: updates.append((added_proto, action, new_info)) if args.dry_run: smtpconn = make_fake_smtpconn() else: if can_be_interactive: interactively_extend_smtp_config(config) try: smtpconn = make_smtpconn(config) except (configparser.NoSectionError, configparser.NoOptionError) as exc: print("Missing configuration: {}".format(exc), file=sys.stderr) print("(cannot ask for configuration on stdio because it is " "not a TTY)", file=sys.stderr) sys.exit(3) try: for id_, action, info in updates: if action == Action.PROTO: mail = make_proto_mail(info) else: mail = make_nonproto_mail(action, info) mail["Date"] = datetime.utcnow() mail["From"] = config.get("smtp", "from") mail["To"] = args.to if args.ask_confirmation: print() print("---8<---") print(mail.as_string()) print("--->8---") print() choice = choose( "Send this email? [y]es, [n]o, [a]bort: ", "yna", eof="a", ) if choice == "n": continue elif choice == "a": print("Exiting on user request.", file=sys.stderr) sys.exit(4) smtpconn.send_message(mail) finally: smtpconn.close()