prepare_release_update_documentation.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # Licensed to Elasticsearch under one or more contributor
  2. # license agreements. See the NOTICE file distributed with
  3. # this work for additional information regarding copyright
  4. # ownership. Elasticsearch licenses this file to you under
  5. # the Apache License, Version 2.0 (the "License"); you may
  6. # not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing,
  12. # software distributed under the License is distributed on
  13. # an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  14. # either express or implied. See the License for the specific
  15. # language governing permissions and limitations under the License.
  16. # Prepare a release: Update the documentation and commit
  17. #
  18. # USAGE:
  19. #
  20. # python3 ./dev-tools/prepare_release_update_documentation.py
  21. #
  22. # Note: Ensure the script is run from the root directory
  23. # This script needs to be run and then pushed,
  24. # before proceeding with prepare_release_create-release-version.py
  25. # on your build VM
  26. #
  27. import fnmatch
  28. import subprocess
  29. import tempfile
  30. import re
  31. import os
  32. import shutil
  33. def run(command):
  34. if os.system('%s' % (command)):
  35. raise RuntimeError(' FAILED: %s' % (command))
  36. def ensure_checkout_is_clean():
  37. # Make sure no local mods:
  38. s = subprocess.check_output('git diff --shortstat', shell=True)
  39. if len(s) > 0:
  40. raise RuntimeError('git diff --shortstat is non-empty: got:\n%s' % s)
  41. # Make sure no untracked files:
  42. s = subprocess.check_output('git status', shell=True).decode('utf-8', errors='replace')
  43. if 'Untracked files:' in s:
  44. raise RuntimeError('git status shows untracked files: got:\n%s' % s)
  45. # Make sure we have all changes from origin:
  46. if 'is behind' in s:
  47. raise RuntimeError('git status shows not all changes pulled from origin; try running "git pull origin" in this branch: got:\n%s' % (s))
  48. # Make sure we no local unpushed changes (this is supposed to be a clean area):
  49. if 'is ahead' in s:
  50. raise RuntimeError('git status shows local commits; try running "git fetch origin", "git checkout ", "git reset --hard origin/" in this branch: got:\n%s' % (s))
  51. # Reads the given file and applies the
  52. # callback to it. If the callback changed
  53. # a line the given file is replaced with
  54. # the modified input.
  55. def process_file(file_path, line_callback):
  56. fh, abs_path = tempfile.mkstemp()
  57. modified = False
  58. with open(abs_path,'w', encoding='utf-8') as new_file:
  59. with open(file_path, encoding='utf-8') as old_file:
  60. for line in old_file:
  61. new_line = line_callback(line)
  62. modified = modified or (new_line != line)
  63. new_file.write(new_line)
  64. os.close(fh)
  65. if modified:
  66. #Remove original file
  67. os.remove(file_path)
  68. #Move new file
  69. shutil.move(abs_path, file_path)
  70. return True
  71. else:
  72. # nothing to do - just remove the tmp file
  73. os.remove(abs_path)
  74. return False
  75. # Checks the pom.xml for the release version.
  76. # This method fails if the pom file has no SNAPSHOT version set ie.
  77. # if the version is already on a release version we fail.
  78. # Returns the next version string ie. 0.90.7
  79. def find_release_version():
  80. with open('pom.xml', encoding='utf-8') as file:
  81. for line in file:
  82. match = re.search(r'<version>(.+)-SNAPSHOT</version>', line)
  83. if match:
  84. return match.group(1)
  85. raise RuntimeError('Could not find release version in branch')
  86. # Stages the given files for the next git commit
  87. def add_pending_files(*files):
  88. for file in files:
  89. if file:
  90. # print("Adding file: %s" % (file))
  91. run('git add %s' % (file))
  92. # Updates documentation feature flags
  93. def commit_feature_flags(release):
  94. run('git commit -m "Update Documentation Feature Flags [%s]"' % release)
  95. # Walks the given directory path (defaults to 'docs')
  96. # and replaces all 'coming[$version]' tags with
  97. # 'added[$version]'. This method only accesses asciidoc files.
  98. def update_reference_docs(release_version, path='docs'):
  99. pattern = 'coming[%s' % (release_version)
  100. replacement = 'added[%s' % (release_version)
  101. pending_files = []
  102. def callback(line):
  103. return line.replace(pattern, replacement)
  104. for root, _, file_names in os.walk(path):
  105. for file_name in fnmatch.filter(file_names, '*.asciidoc'):
  106. full_path = os.path.join(root, file_name)
  107. if process_file(full_path, callback):
  108. pending_files.append(os.path.join(root, file_name))
  109. return pending_files
  110. if __name__ == "__main__":
  111. release_version = find_release_version()
  112. print('*** Preparing release version documentation: [%s]' % release_version)
  113. ensure_checkout_is_clean()
  114. pending_files = update_reference_docs(release_version)
  115. if pending_files:
  116. add_pending_files(*pending_files) # expects var args use * to expand
  117. commit_feature_flags(release_version)
  118. else:
  119. print('WARNING: no documentation references updates for release %s' % (release_version))
  120. print('*** Done.')