SetupGit.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. ## @file
  2. # Set up the git configuration for contributing to TianoCore projects
  3. #
  4. # Copyright (c) 2019, Linaro Ltd. All rights reserved.<BR>
  5. # Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
  6. #
  7. # SPDX-License-Identifier: BSD-2-Clause-Patent
  8. #
  9. from __future__ import print_function
  10. import argparse
  11. import os.path
  12. import re
  13. import sys
  14. try:
  15. import git
  16. except ImportError:
  17. print('Unable to load gitpython module - please install and try again.')
  18. sys.exit(1)
  19. try:
  20. # Try Python 2 'ConfigParser' module first since helpful lib2to3 will
  21. # otherwise automagically load it with the name 'configparser'
  22. import ConfigParser
  23. except ImportError:
  24. # Otherwise, try loading the Python 3 'configparser' under an alias
  25. try:
  26. import configparser as ConfigParser
  27. except ImportError:
  28. print("Unable to load configparser/ConfigParser module - please install and try again!")
  29. sys.exit(1)
  30. # Assumptions: Script is in edk2/BaseTools/Scripts,
  31. # templates in edk2/BaseTools/Conf
  32. CONFDIR = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
  33. 'Conf')
  34. UPSTREAMS = [
  35. {'name': 'edk2',
  36. 'repo': 'https://github.com/tianocore/edk2.git',
  37. 'list': 'devel@edk2.groups.io'},
  38. {'name': 'edk2-platforms',
  39. 'repo': 'https://github.com/tianocore/edk2-platforms.git',
  40. 'list': 'devel@edk2.groups.io', 'prefix': 'edk2-platforms'},
  41. {'name': 'edk2-non-osi',
  42. 'repo': 'https://github.com/tianocore/edk2-non-osi.git',
  43. 'list': 'devel@edk2.groups.io', 'prefix': 'edk2-non-osi'},
  44. {'name': 'edk2-test',
  45. 'repo': 'https://github.com/tianocore/edk2-test.git',
  46. 'list': 'devel@edk2.groups.io', 'prefix': 'edk2-test'}
  47. ]
  48. # The minimum version required for all of the below options to work
  49. MIN_GIT_VERSION = (1, 9, 0)
  50. # Set of options to be set identically for all repositories
  51. OPTIONS = [
  52. {'section': 'am', 'option': 'keepcr', 'value': True},
  53. {'section': 'am', 'option': 'signoff', 'value': True},
  54. {'section': 'cherry-pick', 'option': 'signoff', 'value': True},
  55. {'section': 'color', 'option': 'diff', 'value': True},
  56. {'section': 'color', 'option': 'grep', 'value': 'auto'},
  57. {'section': 'commit', 'option': 'signoff', 'value': True},
  58. {'section': 'core', 'option': 'abbrev', 'value': 12},
  59. {'section': 'core', 'option': 'attributesFile',
  60. 'value': os.path.join(CONFDIR, 'gitattributes').replace('\\', '/')},
  61. {'section': 'core', 'option': 'whitespace', 'value': 'cr-at-eol'},
  62. {'section': 'diff', 'option': 'algorithm', 'value': 'patience'},
  63. {'section': 'diff', 'option': 'orderFile',
  64. 'value': os.path.join(CONFDIR, 'diff.order').replace('\\', '/')},
  65. {'section': 'diff', 'option': 'renames', 'value': 'copies'},
  66. {'section': 'diff', 'option': 'statGraphWidth', 'value': '20'},
  67. {'section': 'diff "ini"', 'option': 'xfuncname',
  68. 'value': '^\\\\[[A-Za-z0-9_., ]+]'},
  69. {'section': 'format', 'option': 'coverLetter', 'value': True},
  70. {'section': 'format', 'option': 'numbered', 'value': True},
  71. {'section': 'format', 'option': 'signoff', 'value': False},
  72. {'section': 'log', 'option': 'mailmap', 'value': True},
  73. {'section': 'notes', 'option': 'rewriteRef', 'value': 'refs/notes/commits'},
  74. {'section': 'sendemail', 'option': 'chainreplyto', 'value': False},
  75. {'section': 'sendemail', 'option': 'thread', 'value': True},
  76. {'section': 'sendemail', 'option': 'transferEncoding', 'value': '8bit'},
  77. ]
  78. def locate_repo():
  79. """Opens a Repo object for the current tree, searching upwards in the directory hierarchy."""
  80. try:
  81. repo = git.Repo(path='.', search_parent_directories=True)
  82. except (git.InvalidGitRepositoryError, git.NoSuchPathError):
  83. print("It doesn't look like we're inside a git repository - aborting.")
  84. sys.exit(2)
  85. return repo
  86. def fuzzy_match_repo_url(one, other):
  87. """Compares two repository URLs, ignoring protocol and optional trailing '.git'."""
  88. oneresult = re.match(r'.*://(?P<oneresult>.*?)(\.git)*$', one)
  89. otherresult = re.match(r'.*://(?P<otherresult>.*?)(\.git)*$', other)
  90. if oneresult and otherresult:
  91. onestring = oneresult.group('oneresult')
  92. otherstring = otherresult.group('otherresult')
  93. if onestring == otherstring:
  94. return True
  95. return False
  96. def get_upstream(url, name):
  97. """Extracts the dict for the current repo origin."""
  98. for upstream in UPSTREAMS:
  99. if (fuzzy_match_repo_url(upstream['repo'], url) or
  100. upstream['name'] == name):
  101. return upstream
  102. print("Unknown upstream '%s' - aborting!" % url)
  103. sys.exit(3)
  104. def check_versions():
  105. """Checks versions of dependencies."""
  106. version = git.cmd.Git().version_info
  107. if version < MIN_GIT_VERSION:
  108. print('Need git version %d.%d or later!' % (version[0], version[1]))
  109. sys.exit(4)
  110. def write_config_value(repo, section, option, data):
  111. """."""
  112. with repo.config_writer(config_level='repository') as configwriter:
  113. configwriter.set_value(section, option, data)
  114. if __name__ == '__main__':
  115. check_versions()
  116. PARSER = argparse.ArgumentParser(
  117. description='Sets up a git repository according to TianoCore rules.')
  118. PARSER.add_argument('-c', '--check',
  119. help='check current config only, printing what would be changed',
  120. action='store_true',
  121. required=False)
  122. PARSER.add_argument('-f', '--force',
  123. help='overwrite existing settings conflicting with program defaults',
  124. action='store_true',
  125. required=False)
  126. PARSER.add_argument('-n', '--name', type=str, metavar='repo',
  127. choices=['edk2', 'edk2-platforms', 'edk2-non-osi'],
  128. help='set the repo name to configure for, if not '
  129. 'detected automatically',
  130. required=False)
  131. PARSER.add_argument('-v', '--verbose',
  132. help='enable more detailed output',
  133. action='store_true',
  134. required=False)
  135. ARGS = PARSER.parse_args()
  136. REPO = locate_repo()
  137. if REPO.bare:
  138. print('Bare repo - please check out an upstream one!')
  139. sys.exit(6)
  140. URL = REPO.remotes.origin.url
  141. UPSTREAM = get_upstream(URL, ARGS.name)
  142. if not UPSTREAM:
  143. print("Upstream '%s' unknown, aborting!" % URL)
  144. sys.exit(7)
  145. # Set a list email address if our upstream wants it
  146. if 'list' in UPSTREAM:
  147. OPTIONS.append({'section': 'sendemail', 'option': 'to',
  148. 'value': UPSTREAM['list']})
  149. # Append a subject prefix entry to OPTIONS if our upstream wants it
  150. if 'prefix' in UPSTREAM:
  151. OPTIONS.append({'section': 'format', 'option': 'subjectPrefix',
  152. 'value': "PATCH " + UPSTREAM['prefix']})
  153. CONFIG = REPO.config_reader(config_level='repository')
  154. for entry in OPTIONS:
  155. exists = False
  156. try:
  157. # Make sure to read boolean/int settings as real type rather than strings
  158. if isinstance(entry['value'], bool):
  159. value = CONFIG.getboolean(entry['section'], entry['option'])
  160. elif isinstance(entry['value'], int):
  161. value = CONFIG.getint(entry['section'], entry['option'])
  162. else:
  163. value = CONFIG.get(entry['section'], entry['option'])
  164. exists = True
  165. # Don't bail out from options not already being set
  166. except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
  167. pass
  168. if exists:
  169. if value == entry['value']:
  170. if ARGS.verbose:
  171. print("%s.%s already set (to '%s')" % (entry['section'],
  172. entry['option'], value))
  173. else:
  174. if ARGS.force:
  175. write_config_value(REPO, entry['section'], entry['option'], entry['value'])
  176. else:
  177. print("Not overwriting existing %s.%s value:" % (entry['section'],
  178. entry['option']))
  179. print(" '%s' != '%s'" % (value, entry['value']))
  180. print(" add '-f' to command line to force overwriting existing settings")
  181. else:
  182. print("%s.%s => '%s'" % (entry['section'], entry['option'], entry['value']))
  183. if not ARGS.check:
  184. write_config_value(REPO, entry['section'], entry['option'], entry['value'])