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