update_changelog.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #!/usr/bin/env python3
  2. import sys
  3. import os
  4. from typing import List, Any
  5. import yaml
  6. import argparse
  7. import datetime
  8. MAX_ENTRIES = 500
  9. HEADER_RE = r"(?::cl:|🆑) *\r?\n(.+)$"
  10. ENTRY_RE = r"^ *[*-]? *(\S[^\n\r]+)\r?$"
  11. CATEGORY_MAIN = "Main"
  12. # From https://stackoverflow.com/a/37958106/4678631
  13. class NoDatesSafeLoader(yaml.SafeLoader):
  14. @classmethod
  15. def remove_implicit_resolver(cls, tag_to_remove):
  16. if not 'yaml_implicit_resolvers' in cls.__dict__:
  17. cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy()
  18. for first_letter, mappings in cls.yaml_implicit_resolvers.items():
  19. cls.yaml_implicit_resolvers[first_letter] = [(tag, regexp)
  20. for tag, regexp in mappings
  21. if tag != tag_to_remove]
  22. # Hrm yes let's make the fucking default of our serialization library to PARSE ISO-8601
  23. # but then output garbage when re-serializing.
  24. NoDatesSafeLoader.remove_implicit_resolver('tag:yaml.org,2002:timestamp')
  25. def main():
  26. parser = argparse.ArgumentParser()
  27. parser.add_argument("changelog_file")
  28. parser.add_argument("parts_dir")
  29. parser.add_argument("--category", default=CATEGORY_MAIN)
  30. args = parser.parse_args()
  31. category = args.category
  32. with open(args.changelog_file, "r", encoding="utf-8-sig") as f:
  33. current_data = yaml.load(f, Loader=NoDatesSafeLoader)
  34. entries_list: List[Any]
  35. if current_data is None:
  36. entries_list = []
  37. else:
  38. entries_list = current_data["Entries"]
  39. max_id = max(map(lambda e: e["id"], entries_list), default=0)
  40. for partname in os.listdir(args.parts_dir):
  41. if not partname.endswith(".yml"):
  42. continue
  43. partpath = os.path.join(args.parts_dir, partname)
  44. print(partpath)
  45. with open(partpath, "r", encoding="utf-8-sig") as f:
  46. partyaml = yaml.load(f, Loader=NoDatesSafeLoader)
  47. part_category = partyaml.get("category", CATEGORY_MAIN)
  48. if part_category != category:
  49. print(f"Skipping: wrong category ({part_category} vs {category})")
  50. continue
  51. author = partyaml["author"]
  52. time = partyaml.get(
  53. "time", datetime.datetime.now(datetime.timezone.utc).isoformat()
  54. )
  55. changes = partyaml["changes"]
  56. url = partyaml.get("url")
  57. if not isinstance(changes, list):
  58. changes = [changes]
  59. if len(changes):
  60. # Don't add empty changelog entries...
  61. max_id += 1
  62. new_id = max_id
  63. entries_list.append(
  64. {"author": author, "time": time, "changes": changes, "id": new_id, "url": url}
  65. )
  66. os.remove(partpath)
  67. print(f"Have {len(entries_list)} changelog entries")
  68. entries_list.sort(key=lambda e: e["id"])
  69. overflow = len(entries_list) - MAX_ENTRIES
  70. if overflow > 0:
  71. print(f"Removing {overflow} old entries.")
  72. entries_list = entries_list[overflow:]
  73. new_data = {"Entries": entries_list}
  74. for key, value in current_data.items():
  75. if key != "Entries":
  76. new_data[key] = value
  77. with open(args.changelog_file, "w", encoding="utf-8-sig") as f:
  78. yaml.safe_dump(new_data, f)
  79. main()