]>
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 | |
3a3713e6 | 109 | def get_upstream(url, name):\r |
4eb0acb1 LL |
110 | """Extracts the dict for the current repo origin."""\r |
111 | for upstream in UPSTREAMS:\r | |
3a3713e6 RC |
112 | if (fuzzy_match_repo_url(upstream['repo'], url) or\r |
113 | upstream['name'] == name):\r | |
4eb0acb1 LL |
114 | return upstream\r |
115 | print("Unknown upstream '%s' - aborting!" % url)\r | |
116 | sys.exit(3)\r | |
117 | \r | |
118 | \r | |
119 | def check_versions():\r | |
120 | """Checks versions of dependencies."""\r | |
121 | version = git.cmd.Git().version_info\r | |
122 | \r | |
123 | if version < MIN_GIT_VERSION:\r | |
124 | print('Need git version %d.%d or later!' % (version[0], version[1]))\r | |
125 | sys.exit(4)\r | |
126 | \r | |
127 | \r | |
128 | def write_config_value(repo, section, option, data):\r | |
129 | """."""\r | |
130 | with repo.config_writer(config_level='repository') as configwriter:\r | |
131 | configwriter.set_value(section, option, data)\r | |
132 | \r | |
133 | \r | |
134 | if __name__ == '__main__':\r | |
135 | check_versions()\r | |
136 | \r | |
137 | PARSER = argparse.ArgumentParser(\r | |
138 | description='Sets up a git repository according to TianoCore rules.')\r | |
139 | PARSER.add_argument('-c', '--check',\r | |
140 | help='check current config only, printing what would be changed',\r | |
141 | action='store_true',\r | |
142 | required=False)\r | |
143 | PARSER.add_argument('-f', '--force',\r | |
144 | help='overwrite existing settings conflicting with program defaults',\r | |
145 | action='store_true',\r | |
146 | required=False)\r | |
3a3713e6 RC |
147 | PARSER.add_argument('-n', '--name', type=str, metavar='repo',\r |
148 | choices=['edk2', 'edk2-platforms', 'edk2-non-osi'],\r | |
149 | help='set the repo name to configure for, if not '\r | |
150 | 'detected automatically',\r | |
151 | required=False)\r | |
4eb0acb1 LL |
152 | PARSER.add_argument('-v', '--verbose',\r |
153 | help='enable more detailed output',\r | |
154 | action='store_true',\r | |
155 | required=False)\r | |
156 | ARGS = PARSER.parse_args()\r | |
157 | \r | |
158 | REPO = locate_repo()\r | |
159 | if REPO.bare:\r | |
160 | print('Bare repo - please check out an upstream one!')\r | |
161 | sys.exit(6)\r | |
162 | \r | |
163 | URL = REPO.remotes.origin.url\r | |
164 | \r | |
3a3713e6 | 165 | UPSTREAM = get_upstream(URL, ARGS.name)\r |
4eb0acb1 LL |
166 | if not UPSTREAM:\r |
167 | print("Upstream '%s' unknown, aborting!" % URL)\r | |
168 | sys.exit(7)\r | |
169 | \r | |
170 | # Set a list email address if our upstream wants it\r | |
171 | if 'list' in UPSTREAM:\r | |
172 | OPTIONS.append({'section': 'sendemail', 'option': 'to',\r | |
173 | 'value': UPSTREAM['list']})\r | |
174 | # Append a subject prefix entry to OPTIONS if our upstream wants it\r | |
175 | if 'prefix' in UPSTREAM:\r | |
176 | OPTIONS.append({'section': 'format', 'option': 'subjectPrefix',\r | |
177 | 'value': "PATCH " + UPSTREAM['prefix']})\r | |
178 | \r | |
179 | CONFIG = REPO.config_reader(config_level='repository')\r | |
180 | \r | |
181 | for entry in OPTIONS:\r | |
182 | exists = False\r | |
183 | try:\r | |
184 | # Make sure to read boolean/int settings as real type rather than strings\r | |
185 | if isinstance(entry['value'], bool):\r | |
186 | value = CONFIG.getboolean(entry['section'], entry['option'])\r | |
187 | elif isinstance(entry['value'], int):\r | |
188 | value = CONFIG.getint(entry['section'], entry['option'])\r | |
189 | else:\r | |
190 | value = CONFIG.get(entry['section'], entry['option'])\r | |
191 | \r | |
192 | exists = True\r | |
193 | # Don't bail out from options not already being set\r | |
194 | except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):\r | |
195 | pass\r | |
196 | \r | |
197 | if exists:\r | |
198 | if value == entry['value']:\r | |
199 | if ARGS.verbose:\r | |
200 | print("%s.%s already set (to '%s')" % (entry['section'],\r | |
201 | entry['option'], value))\r | |
202 | else:\r | |
203 | if ARGS.force:\r | |
204 | write_config_value(REPO, entry['section'], entry['option'], entry['value'])\r | |
205 | else:\r | |
206 | print("Not overwriting existing %s.%s value:" % (entry['section'],\r | |
207 | entry['option']))\r | |
208 | print(" '%s' != '%s'" % (value, entry['value']))\r | |
209 | print(" add '-f' to command line to force overwriting existing settings")\r | |
210 | else:\r | |
211 | print("%s.%s => '%s'" % (entry['section'], entry['option'], entry['value']))\r | |
212 | if not ARGS.check:\r | |
213 | write_config_value(REPO, entry['section'], entry['option'], entry['value'])\r |