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