p≡p engine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
7.3 KiB

  1. # Requires GitPython: https://gitpython.readthedocs.io/en/stable/intro.html
  2. import subprocess
  3. import re
  4. import sys
  5. import argparse
  6. import os
  7. from git import Repo, TagReference
  8. from enum import Enum
  9. def replace_src_versions(src_lines, new_major, new_minor, new_patch, new_rc, with_rc, comment, src_repo, src_path):
  10. header_file_path = src_path + "/pEpEngine.h"
  11. if not src_lines:
  12. exit("Information: Not writing version/RC info to " + header_file_path)
  13. filedata = None
  14. # If successful, then bump the RC
  15. with open(header_file_path, 'r') as file:
  16. filedata = file.read()
  17. file.close()
  18. cmd = ["grep", "-E", "#define PEP_ENGINE_VERSION[ \t]+\"[0-9]+.[0-9]+.[0-9]+\"", header_file_path]
  19. result = subprocess.run(cmd, capture_output=True)
  20. grep_output = result.stdout.decode("utf-8")
  21. major_str = str(new_major)
  22. minor_str = str(new_minor)
  23. patch_str = str(new_patch)
  24. rc_str = str(new_rc)
  25. # define PEP_ENGINE_VERSION "2.1.0"
  26. version_str = str(new_major) + "." + str(new_minor) + "." + str(new_patch)
  27. filedata = filedata.replace(grep_output, "#define PEP_ENGINE_VERSION \"" + version_str + "\"\n")
  28. filedata = filedata.replace(src_lines[0], "#define PEP_ENGINE_VERSION_MAJOR " + major_str)
  29. filedata = filedata.replace(src_lines[1], "#define PEP_ENGINE_VERSION_MINOR " + minor_str)
  30. filedata = filedata.replace(src_lines[2], "#define PEP_ENGINE_VERSION_PATCH " + patch_str)
  31. filedata = filedata.replace(src_lines[3], "#define PEP_ENGINE_VERSION_RC " + rc_str)
  32. # Write the file out again
  33. with open(header_file_path, 'w') as file:
  34. file.write(filedata)
  35. file.close()
  36. print("About to run with this comment:")
  37. print(comment)
  38. if not comment:
  39. comment = "Default commit message: rewrote src to contain eventual release info for FUTURE Release_" + \
  40. version_str
  41. if with_rc:
  42. comment += "-RC" + rc_str
  43. src_repo.git.commit('-am', comment)
  44. return version_str
  45. def bool_prompt(prompt):
  46. reply = str(input(prompt)).lower().strip()
  47. if reply != "y":
  48. exit("Aborting at user request.")
  49. class ReleaseType(Enum):
  50. UNKNOWN = 0
  51. RC = 1
  52. PATCH = 2
  53. # Init some things to be used later
  54. rel_type = ReleaseType.UNKNOWN
  55. repo_path = None
  56. bumped_comment = "Bumped header patch number for NEXT release"
  57. # Parse args
  58. parser = argparse.ArgumentParser(description='Automate the RC release process as sanely as possible.')
  59. parser.add_argument('-r', '--repo', help="Repository root - [default: current working directory]")
  60. parser.add_argument('REASON',
  61. help="Tag annotation for showing reason for release - short description of what this release is")
  62. args = parser.parse_args()
  63. annotation = args.REASON
  64. # Set up repo object
  65. repo_path = args.repo
  66. if not repo_path:
  67. repo_path = os.getenv('ENGINE_REPO_PATH')
  68. # API docs say the above is a string, but I'm getting a list from 3.9.6, so just in case...
  69. if not repo_path:
  70. repo_path = os.getcwd()
  71. if not repo_path:
  72. exit("Can't get repository path.")
  73. repo = Repo(repo_path)
  74. if not repo or repo.bare:
  75. exit("No extant repository at " + repo_path)
  76. # DO THE THING!
  77. #
  78. # 1. get current branch
  79. #
  80. branch = repo.active_branch
  81. if not branch:
  82. exit("Can't get current branch.")
  83. start_branch = branch.name
  84. if not start_branch:
  85. exit("Can't get current branch name.")
  86. #
  87. # 2. Figure out what kind of a release we're doing
  88. #
  89. # These are the TARGET numbers
  90. major = 0
  91. minor = 0
  92. patch = 0
  93. rc = 0
  94. if start_branch == 'master':
  95. rel_type = ReleaseType.RC
  96. else:
  97. release_re = re.compile('^Release_([0-9])+[.]([0-9])+')
  98. release_match = release_re.match(start_branch)
  99. if not release_match:
  100. exit("Not in a release branch. Aborting. (Release branches are of the form 'Release_<major>.<minor>')")
  101. else:
  102. rel_type = ReleaseType.PATCH
  103. major = int(release_match.groups()[0])
  104. minor = int(release_match.groups()[1])
  105. print("\nRELEASE SCRIPT: Preparing for release in branch '" + start_branch + "' in repository at '" + repo_path + "'")
  106. #
  107. # 3. See what the header files have to say about what version we're working with
  108. #
  109. src_major = 0
  110. src_minor = 0
  111. src_patch = 0
  112. src_rc = 0
  113. # Amateur hour. Biteme.
  114. engine_header = repo_path + "/src/pEpEngine.h"
  115. cmd = ["grep", "-E", "#define PEP_ENGINE_VERSION_[A-Z]+[ \t]+[0-9]+", engine_header]
  116. result = subprocess.run(cmd, capture_output=True)
  117. grep_output = result.stdout.decode("utf-8")
  118. grep_lines = grep_output.splitlines();
  119. version_re = re.compile('#define PEP_ENGINE_VERSION_([A-Z]+)[ \t]+([0-9]+)')
  120. for line in grep_lines:
  121. m = version_re.search(line)
  122. if not m:
  123. exit("Can't find matching version information in header file to determine source version info.")
  124. key = m.group(1)
  125. value = int(m.group(2))
  126. if key == "MAJOR":
  127. src_major = value
  128. elif key == "MINOR":
  129. src_minor = value
  130. elif key == "PATCH":
  131. src_patch = value
  132. elif key == "RC":
  133. src_rc = value
  134. else:
  135. exit("Additional information has been added matching '#define PEP_ENGINE_VERSION_.*' - please fix this script.")
  136. # This is tentative based on tag checks:
  137. if rel_type == ReleaseType.RC:
  138. major = src_major
  139. minor = src_minor
  140. patch = 0 # Should be anyway, but...
  141. rc = src_rc # we still have checks to run
  142. elif rel_type == ReleaseType.PATCH:
  143. patch = src_patch
  144. else:
  145. exit("Internal script error. Probably bad cleanup.")
  146. # Unsolicited commentary: I hate that Python doesn't have a switch construct
  147. new_tag = None
  148. #
  149. # 3. Get last release tag for this branch to check we aren't messing things up
  150. # Then tag and release
  151. #
  152. tag_maj = 0
  153. tag_min = 0
  154. tag_patch = 0
  155. tag_rc = 0
  156. compile_string = ''
  157. if rel_type == ReleaseType.RC:
  158. major = src_major
  159. minor = src_minor
  160. patch = 0 # Should be anyway, but...
  161. compile_string = '^Release_' + str(src_major) + '[.]' + str(src_minor) + '[.]0-RC([0-9])+'
  162. elif rel_type == ReleaseType.PATCH:
  163. compile_string = '^Release_' + str(major) + '[.]' + str(minor) + '[.]([0-9]+)$'
  164. else:
  165. exit("Internal script error. Probably bad cleanup.")
  166. tag_re = re.compile(compile_string)
  167. tag_refs = TagReference.list_items(repo)
  168. candidates = [int(m.group(1)) for tag in tag_refs for m in [tag_re.search(tag.name)] if m]
  169. if candidates:
  170. candidates.sort(reverse=True)
  171. src_val = 0
  172. tag_val = candidates[0]
  173. if rel_type == ReleaseType.RC:
  174. src_val = src_rc
  175. elif rel_type == ReleaseType.PATCH:
  176. src_val = src_patch
  177. if src_val <= tag_val:
  178. # Somebody done messed up. Sorry, folks, we're not tagging today. (Do better here)
  179. exit("Mismatch between tag values and values in the source. Please release manually.")
  180. new_tag = "Release_" + str(major) + "." + str(minor) + "." + str(patch)
  181. if rel_type == ReleaseType.RC:
  182. new_tag += "-RC" + str(rc)
  183. print("\nRELEASE SCRIPT: About to tag release " + new_tag + " on branch '" + start_branch + "'.")
  184. print("RELEASE SCRIPT: This tag will be annotated as follows:")
  185. print("RELEASE_SCRIPT:\nRELEASE_SCRIPT: \"" + annotation + "\"")
  186. bool_prompt("RELEASE_SCRIPT:\nRELEASE SCRIPT: Continue? Y/[N]")
  187. repo.git.tag(new_tag, '-a', '-m', annotation)
  188. if rel_type == ReleaseType.PATCH:
  189. patch = patch + 1
  190. else:
  191. rc = rc + 1
  192. replace_src_versions(grep_lines, major, minor, patch, rc, rel_type == ReleaseType.RC, bumped_comment, repo,
  193. repo_path + "/src")