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