1
1
import json
2
2
import requests
3
3
import os
4
+ import sys
5
+ import argparse
4
6
import shutil
5
7
import sh
6
8
from sh .contrib import git
15
17
apply_errors = []
16
18
stats = []
17
19
20
+ """
21
+ Setup the command line argument parsing object.
22
+ """
23
+ cli_parser = argparse .ArgumentParser (description = "Apply patches to any common file(s) in"
24
+ " all Adafruit CircuitPython Libraries." )
25
+ cli_parser .add_argument ("-l" , "--list" , help = "Lists the available patches to run." ,
26
+ action = 'store_true' )
27
+ cli_parser .add_argument ("-p" , help = "Runs only the single patch referenced." ,
28
+ metavar = "<PATCH FILENAME>" , dest = "patch" )
29
+ cli_parser .add_argument ("-f" , help = "Adds the referenced FLAGS to the git.am call."
30
+ " Only available when using '-p'. Enclose flags in brackets '[]'."
31
+ " Multiple flags can be passed. NOTE: '--signoff' is already used "
32
+ " used by default, and will be ignored. EXAMPLE: -f [-C0] -f [-s]" ,
33
+ metavar = "FLAGS" , action = "append" , dest = "flags" , type = str )
34
+ cli_parser .add_argument ("--use-apply" , help = "Forces use of 'git apply' instead of 'git am'."
35
+ " This is necessary when needing to use 'apply' flags not available"
36
+ " to 'am' (e.g. '--unidiff-zero'). Only available when using '-p'." ,
37
+ action = "store_true" , dest = "use_apply" )
38
+
18
39
def get_repo_list ():
19
40
""" Uses adabot.circuitpython_libraries module to get a list of
20
41
CircuitPython repositories. Filters the list down to adafruit
@@ -36,48 +57,77 @@ def get_patches():
36
57
"""
37
58
return_list = []
38
59
contents = requests .get ("https://api.github.com/repos/adafruit/adabot/contents/patches" )
39
-
40
60
if contents .ok :
41
61
for patch in contents .json ():
42
62
patch_name = patch ["name" ]
43
63
return_list .append (patch_name )
44
64
45
65
return return_list
46
66
47
- def apply_patch (repo_directory , patch_filepath , repo , patch ):
67
+ def apply_patch (repo_directory , patch_filepath , repo , patch , flags , use_apply ):
48
68
""" Apply the `patch` in `patch_filepath` to the `repo` in
49
- `repo_directory` using git am. --signoff will sign the commit
69
+ `repo_directory` using git am or git apply. The commit
50
70
with the user running the script (adabot if credentials are set
51
71
for that).
72
+
73
+ When `use_apply` is true, the `--apply` flag is automatically added
74
+ to ensure that any passed flags that turn off apply (e.g. `--check`)
75
+ are overridden.
52
76
"""
53
77
if not os .getcwd () == repo_directory :
54
78
os .chdir (repo_directory )
55
79
56
- try :
57
- git .am ("--signoff" , patch_filepath )
58
- except sh .ErrorReturnCode as Err :
59
- apply_errors .append (dict (repo_name = repo ,
60
- patch_name = patch , error = Err .stderr ))
61
- return False
80
+ if not use_apply :
81
+ try :
82
+ git .am (flags , patch_filepath )
83
+ except sh .ErrorReturnCode as Err :
84
+ apply_errors .append (dict (repo_name = repo ,
85
+ patch_name = patch , error = Err .stderr ))
86
+ return False
87
+ else :
88
+ apply_flags = ["--apply" ]
89
+ for flag in flags :
90
+ if not flag == "--signoff" :
91
+ apply_flags .append (flag )
92
+ try :
93
+ git .apply (apply_flags , patch_filepath )
94
+ except sh .ErrorReturnCode as Err :
95
+ apply_errors .append (dict (repo_name = repo ,
96
+ patch_name = patch , error = Err .stderr ))
97
+ return False
98
+
99
+ with open (patch_filepath ) as f :
100
+ for line in f :
101
+ if "[PATCH]" in line :
102
+ message = '"' + line [(line .find ("]" ) + 2 ):] + '"'
103
+ break
104
+ try :
105
+ git .commit ("-a" , "-m" , message )
106
+ except sh .ErrorReturnCode as Err :
107
+ apply_errors .append (dict (repo_name = repo ,
108
+ patch_name = patch , error = Err .stderr ))
109
+ return False
62
110
63
111
try :
64
112
git .push ()
65
113
except sh .ErrorReturnCode as Err :
66
114
apply_errors .append (dict (repo_name = repo ,
67
115
patch_name = patch , error = Err .stderr ))
68
116
return False
69
-
70
117
return True
71
118
72
- def check_patches (repo ):
119
+ def check_patches (repo , patches , flags , use_apply ):
73
120
""" Gather a list of patches from the `adabot/patches` directory
74
121
on the adabot repo. Clone the `repo` and run git apply --check
75
122
to test wether it requires any of the gathered patches.
123
+
124
+ When `use_apply` is true, any flags except `--apply` are passed
125
+ through to the check call. This ensures that the check call is
126
+ representative of the actual apply call.
76
127
"""
77
128
applied = 0
78
129
skipped = 0
79
130
failed = 0
80
- patches = get_patches ()
81
131
82
132
repo_directory = lib_directory + repo ["name" ]
83
133
@@ -100,19 +150,31 @@ def check_patches(repo):
100
150
patch_filepath = patch_directory + patch
101
151
102
152
try :
103
- git .apply ("--check" , patch_filepath )
153
+ check_flags = ["--check" ]
154
+ if use_apply :
155
+ for flag in flags :
156
+ if not flag in ("--apply" , "--signoff" ):
157
+ check_flags .append (flag )
158
+ git .apply (check_flags , patch_filepath )
104
159
run_apply = True
105
- except sh .ErrorReturnCode_1 :
160
+ except sh .ErrorReturnCode_1 as Err :
106
161
run_apply = False
107
- skipped += 1
108
- except sh .ErrorReturnCode_128 as Err :
162
+ if not b"error" in Err .stderr :
163
+ skipped += 1
164
+ else :
165
+ failed += 1
166
+ check_errors .append (dict (repo_name = repo ["name" ],
167
+ patch_name = patch , error = Err .stderr ))
168
+
169
+ except sh .ErrorReturnCode as Err :
109
170
run_apply = False
110
171
failed += 1
111
172
check_errors .append (dict (repo_name = repo ["name" ],
112
173
patch_name = patch , error = Err .stderr ))
113
174
114
175
if run_apply :
115
- result = apply_patch (repo_directory , patch_filepath , repo ["name" ], patch )
176
+ result = apply_patch (repo_directory , patch_filepath , repo ["name" ],
177
+ patch , flags , use_apply )
116
178
if result :
117
179
applied += 1
118
180
else :
@@ -121,6 +183,31 @@ def check_patches(repo):
121
183
return [applied , skipped , failed ]
122
184
123
185
if __name__ == "__main__" :
186
+
187
+ run_patches = get_patches ()
188
+ flags = ["--signoff" ]
189
+
190
+ cli_args = cli_parser .parse_args ()
191
+ if cli_args .list :
192
+ print ("Available Patches:" , run_patches )
193
+ sys .exit ()
194
+ if cli_args .patch :
195
+ if not cli_args .patch in run_patches :
196
+ raise ValueError ("'{}' is not an available patchfile." .format (cli_args .patch ))
197
+ run_patches = [cli_args .patch ]
198
+ if not cli_args .flags == None :
199
+ if not cli_args .patch :
200
+ raise RuntimeError ("Must be used with a single patch. See help (-h) for usage." )
201
+ if "[-i]" in cli_args .flags :
202
+ raise ValueError ("Interactive Mode flag not allowed." )
203
+ for flag in cli_args .flags :
204
+ if not flag == "[--signoff]" :
205
+ flags .append (flag .strip ("[]" ))
206
+ if cli_args .use_apply :
207
+ if not cli_args .patch :
208
+ raise RuntimeError ("Must be used with a single patch. See help (-h) for usage." )
209
+ use_apply = cli_args .use_apply
210
+
124
211
print (".... Beginning Patch Updates ...." )
125
212
print (".... Working directory:" , working_directory )
126
213
print (".... Library directory:" , lib_directory )
@@ -136,23 +223,23 @@ def check_patches(repo):
136
223
for lib in libs :
137
224
shutil .rmtree (lib_directory + lib )
138
225
except FileNotFoundError :
139
- pass
226
+ pass
140
227
141
228
repos = get_repo_list ()
142
229
print (".... Running Patch Checks On" , len (repos ), "Repos ...." )
143
230
144
231
for repo in repos :
145
- results = check_patches (repo )
146
- for k in range (len ( stats ) ):
147
- stats [k ] += results [k ]
232
+ results = check_patches (repo , run_patches , flags , use_apply )
233
+ for k in range (3 ):
234
+ stats [k ] += results [k ]
148
235
149
236
print (".... Patch Updates Completed ...." )
150
237
print (".... Patches Applied:" , stats [0 ])
151
238
print (".... Patches Skipped:" , stats [1 ])
152
239
print (".... Patches Failed:" , stats [2 ], "\n " )
153
240
print (".... Patch Check Failure Report ...." )
154
241
if len (check_errors ) > 0 :
155
- for error , _ in check_errors :
242
+ for error in check_errors :
156
243
print (">>" , error )
157
244
else :
158
245
print ("No Failures" )
@@ -163,4 +250,4 @@ def check_patches(repo):
163
250
print (">>" , error )
164
251
else :
165
252
print ("No Failures" )
166
-
253
+
0 commit comments