release.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # -*- coding: utf-8 -*-
  2. """
  3. Invoke development tasks.
  4. """
  5. import argparse
  6. from pathlib import Path
  7. from subprocess import call
  8. from subprocess import check_call
  9. from subprocess import check_output
  10. from colorama import Fore
  11. from colorama import init
  12. def announce(version):
  13. """Generates a new release announcement entry in the docs."""
  14. # Get our list of authors
  15. stdout = check_output(["git", "describe", "--abbrev=0", "--tags"])
  16. stdout = stdout.decode("utf-8")
  17. last_version = stdout.strip()
  18. stdout = check_output(
  19. ["git", "log", "{}..HEAD".format(last_version), "--format=%aN"]
  20. )
  21. stdout = stdout.decode("utf-8")
  22. contributors = set(stdout.splitlines())
  23. template_name = (
  24. "release.minor.rst" if version.endswith(".0") else "release.patch.rst"
  25. )
  26. template_text = (
  27. Path(__file__).parent.joinpath(template_name).read_text(encoding="UTF-8")
  28. )
  29. contributors_text = (
  30. "\n".join("* {}".format(name) for name in sorted(contributors)) + "\n"
  31. )
  32. text = template_text.format(version=version, contributors=contributors_text)
  33. target = Path(__file__).parent.joinpath(
  34. "../doc/en/announce/release-{}.rst".format(version)
  35. )
  36. target.write_text(text, encoding="UTF-8")
  37. print(f"{Fore.CYAN}[generate.announce] {Fore.RESET}Generated {target.name}")
  38. # Update index with the new release entry
  39. index_path = Path(__file__).parent.joinpath("../doc/en/announce/index.rst")
  40. lines = index_path.read_text(encoding="UTF-8").splitlines()
  41. indent = " "
  42. for index, line in enumerate(lines):
  43. if line.startswith("{}release-".format(indent)):
  44. new_line = indent + target.stem
  45. if line != new_line:
  46. lines.insert(index, new_line)
  47. index_path.write_text("\n".join(lines) + "\n", encoding="UTF-8")
  48. print(
  49. f"{Fore.CYAN}[generate.announce] {Fore.RESET}Updated {index_path.name}"
  50. )
  51. else:
  52. print(
  53. f"{Fore.CYAN}[generate.announce] {Fore.RESET}Skip {index_path.name} (already contains release)"
  54. )
  55. break
  56. check_call(["git", "add", str(target)])
  57. def regen():
  58. """Call regendoc tool to update examples and pytest output in the docs."""
  59. print(f"{Fore.CYAN}[generate.regen] {Fore.RESET}Updating docs")
  60. check_call(["tox", "-e", "regen"])
  61. def fix_formatting():
  62. """Runs pre-commit in all files to ensure they are formatted correctly"""
  63. print(
  64. f"{Fore.CYAN}[generate.fix linting] {Fore.RESET}Fixing formatting using pre-commit"
  65. )
  66. call(["pre-commit", "run", "--all-files"])
  67. def pre_release(version):
  68. """Generates new docs, release announcements and creates a local tag."""
  69. announce(version)
  70. regen()
  71. changelog(version, write_out=True)
  72. fix_formatting()
  73. msg = "Preparing release version {}".format(version)
  74. check_call(["git", "commit", "-a", "-m", msg])
  75. print()
  76. print(f"{Fore.CYAN}[generate.pre_release] {Fore.GREEN}All done!")
  77. print()
  78. print(f"Please push your branch and open a PR.")
  79. def changelog(version, write_out=False):
  80. if write_out:
  81. addopts = []
  82. else:
  83. addopts = ["--draft"]
  84. check_call(["towncrier", "--yes", "--version", version] + addopts)
  85. def main():
  86. init(autoreset=True)
  87. parser = argparse.ArgumentParser()
  88. parser.add_argument("version", help="Release version")
  89. options = parser.parse_args()
  90. pre_release(options.version)
  91. if __name__ == "__main__":
  92. main()