8
8
import subprocess
9
9
import shutil
10
10
import stat
11
+ import re
12
+ from github import Github , GithubException
11
13
12
14
ROOT = abspath (dirname (dirname (dirname (dirname (__file__ )))))
13
15
sys .path .insert (0 , ROOT )
@@ -33,6 +35,26 @@ def run_cmd(command, print_warning_on_fail=True):
33
35
34
36
return return_code
35
37
38
+ def run_cmd_with_output (command , print_warning_on_fail = True ):
39
+ """ Takes the command specified and runs it in a sub-process, obtaining the return code
40
+ and the returned output.
41
+
42
+ Args:
43
+ command - command to run, provided as a list of individual fields which are combined into a
44
+ single command before passing to the sub-process call.
45
+ return_code - result of the command.
46
+ output - the output of the command
47
+
48
+ """
49
+ print ('[Exec] %s' % ' ' .join (command ))
50
+ returncode = 0
51
+ output = None
52
+ try :
53
+ output = subprocess .check_output (command )
54
+ except subprocess .CalledProcessError as e :
55
+ print ("The command '%s' failed with return code: %s" % (' ' .join (command ), e .returncode ))
56
+ returncode = e .returncode
57
+ return returncode , output
36
58
37
59
def rmtree_readonly (directory ):
38
60
""" Deletes a readonly directory tree.
@@ -63,7 +85,7 @@ def find_all_examples(path):
63
85
64
86
return examples
65
87
66
- def upgrade_single_example (example , tag , directory ):
88
+ def upgrade_single_example (example , tag , directory , ref ):
67
89
""" Updates the mbed-os.lib file in the example specified to correspond to the
68
90
version specified by the GitHub tag supplied. Also deals with
69
91
multiple sub-examples in the GitHub repo, updating them in the same way.
@@ -72,113 +94,167 @@ def upgrade_single_example(example, tag, directory):
72
94
example - json example object containing the GitHub repo to update.
73
95
tag - GitHub tag corresponding to a version of mbed-os to upgrade to.
74
96
directory - directory path for the example.
97
+ ref - SHA corresponding to the supplied tag
75
98
returns - True if the upgrade was successful, False otherwise.
76
99
77
100
"""
78
- print ("Upgrading single example at path '%s'" % directory )
79
101
cwd = os .getcwd ()
80
102
os .chdir (directory )
81
103
82
- return_code = None
104
+ return_code = False
83
105
84
- # Change directories to the mbed-os library
85
- if not os .path .exists ('mbed-os' ):
86
- print ("'mbed-os' directory not found in the root of '%s'" % directory )
87
- print ("Ignoring and moving on to the next example" )
88
- os .chdir (cwd )
106
+ if os .path .isfile ("mbed-os.lib" ):
107
+ os .system ("mv mbed-os.lib mbed-os.lib_bak" )
108
+ else :
109
+ print ("!! Error trying to backup mbed-os.lib prior to updating." )
89
110
return False
90
111
91
- os . chdir ( ' mbed-os' )
92
-
93
- # Setup and run the update command
94
- update_cmd = [ 'mbed' , 'update' , tag ]
95
- return_code = run_cmd ( update_cmd )
96
-
97
- if return_code :
98
- os . chdir ( cwd )
99
- return False
100
-
101
- os . chdir ( '../' )
102
-
103
- # Setup and run the add command
104
- add_cmd = [ 'git' , 'add' , ' mbed-os.lib' ]
105
- return_code = run_cmd ( add_cmd )
112
+ # mbed-os.lib file contains one line with the following format
113
+ # e.g. https://github.com/ARMmbed/mbed-os/#0789928ee7f2db08a419fa4a032fffd9bd477aa7
114
+ lib_re = re . compile ( 'https://github.com/ARMmbed/mbed-os/#[A-Za-z0-9]+' )
115
+ updated = False
116
+
117
+ # Scan through mbed-os.lib line by line
118
+ with open ( 'mbed-os.lib_bak' , 'r' ) as ip , open ( 'mbed-os.lib' , 'w' ) as op :
119
+ for line in ip :
120
+
121
+ opline = line
122
+
123
+ regexp = lib_re . match ( line )
124
+ if regexp :
125
+ opline = 'https://github.com/ARMmbed/ mbed-os/#' + ref
126
+ updated = True
106
127
128
+ op .write (opline )
129
+
130
+ if updated :
131
+ # Setup and run the git add command
132
+ cmd = ['git' , 'add' , 'mbed-os.lib' ]
133
+ return_code = run_cmd (cmd )
134
+
107
135
os .chdir (cwd )
108
136
return not return_code
109
137
110
- def upgrade_example (example , tag ):
111
- """ Clones the example specified from GitHub and updates the associated mbed-os.lib file
112
- to correspond to the version specified by the GitHub tag supplied. Also deals with
113
- multiple sub-examples in the GitHub repo, updating them in the same way.
138
+ def prepare_fork (arm_example ):
139
+ """ Synchronises a cloned fork to ensure it is up to date with the original.
114
140
115
141
Args:
142
+ arm_example - Full GitHub repo path for original example
143
+ ret - True if the fork was synchronised successfully, False otherwise
144
+
145
+ """
146
+
147
+ ret = False
148
+
149
+ print "In " + os .getcwd ()
150
+
151
+ cmd = ['git' , 'remote' , 'add' , 'armmbed' , arm_example ]
152
+ return_code = run_cmd (cmd )
153
+
154
+ if not return_code :
155
+
156
+ cmd = ['git' , 'fetch' , 'armmbed' ]
157
+ return_code = run_cmd (cmd )
158
+ if not return_code :
159
+
160
+ cmd = ['git' , 'reset' , '--hard' , 'armmbed/master' ]
161
+ return_code = run_cmd (cmd )
162
+ if not return_code :
163
+
164
+ cmd = ['git' , 'push' , '-f' , 'origin' ]
165
+ return_code = run_cmd (cmd )
166
+ if not return_code :
167
+ ret = True
168
+
169
+ if not ret :
170
+ print ("Preparation of the fork failed!" )
171
+
172
+ return ret
173
+
174
+ def upgrade_example (github , example , tag , user , ref ):
175
+ """ Clone a fork of the example specified.
176
+ Ensures the fork is up to date with the original and then and updates the associated
177
+ mbed-os.lib file on that fork to correspond to the version specified by the GitHub tag supplied.
178
+ Also deals with multiple sub-examples in the GitHub repo, updating them in the same way.
179
+ The updates are pushed to the forked repo.
180
+ Finally a PR is raised against the original example repo for the changes.
181
+
182
+ Args:
183
+ github - GitHub instance to allow internal git commands to be run
116
184
example - json example object containing the GitHub repo to update.
117
185
tag - GitHub tag corresponding to a version of mbed-os to upgrade to.
186
+ user - GitHub user name
187
+ ref - SHA corresponding to the tag
118
188
119
189
"""
120
- print ("Updating example '%s'" % example ['name' ])
190
+ ret = False
191
+ print ("\n Updating example '%s'" % example ['name' ])
121
192
cwd = os .getcwd ()
122
-
123
- # Setup and run the import command
124
- clone_cmd = ['git' , 'clone' , example ['github' ]]
125
- return_code = run_cmd (clone_cmd )
126
-
127
- if return_code :
193
+
194
+ full_repo_name = 'ARMmbed/' + example ['name' ]
195
+ fork = "https://github.com/" + user + '/' + example ['name' ]
196
+
197
+ # Check access to mbed-os repo
198
+ try :
199
+ repo = github .get_repo (full_repo_name , False )
200
+
201
+ except :
202
+ print ("\t \t !! Repo does not exist - skipping\n " )
128
203
return False
204
+
205
+
206
+ # Clone the forked example repo
207
+ clone_cmd = ['git' , 'clone' , fork ]
208
+ return_code = run_cmd (clone_cmd )
129
209
130
- # Find all examples
131
- example_directories = find_all_examples (example ['name' ])
132
-
133
- os .chdir (example ['name' ])
210
+ if not return_code :
134
211
135
- # Setup and run the update command
136
- import_cmd = ['mbed' , 'update' ]
137
- return_code = run_cmd (import_cmd )
138
- if return_code :
139
- os .chdir (cwd )
140
- return False
141
-
142
- for example_directory in example_directories :
143
- if not upgrade_single_example (example , tag , os .path .relpath (example_directory , example ['name' ])):
144
- os .chdir (cwd )
145
- return False
212
+ # Find all examples
213
+ example_directories = find_all_examples (example ['name' ])
214
+
215
+ os .chdir (example ['name' ])
146
216
147
- # Setup the default commit message
148
- commit_message = 'Updating mbed-os to {{' + tag + '}}'
217
+ # checkout and synchronise the release-candidate branch
218
+ prepare_fork (example ['github' ])
219
+
220
+ for example_directory in example_directories :
221
+ if not upgrade_single_example (example , tag , os .path .relpath (example_directory , example ['name' ]), ref ):
222
+ os .chdir (cwd )
223
+ return False
224
+
225
+ # Setup the default commit message
226
+ commit_message = 'Updating mbed-os to ' + tag
227
+
228
+ # Setup and run the commit command
229
+ commit_cmd = ['git' , 'commit' , '-m' , commit_message ]
230
+ return_code = run_cmd (commit_cmd )
231
+ if not return_code :
232
+
233
+ # Setup and run the push command
234
+ push_cmd = ['git' , 'push' , 'origin' ]
235
+ return_code = run_cmd (push_cmd )
149
236
150
- # Setup and run the commit command
151
- commit_cmd = ['git' , 'commit' , '-m' , commit_message ]
152
- return_code = run_cmd (commit_cmd )
153
- if return_code :
154
- if return_code == 1 :
155
- print ("[WARNING] 'git commit' exited with a return code of 1. " + \
156
- "This usually inidicates that no update was made. Still " + \
157
- "attempting to create a tag." )
237
+ if not return_code :
238
+ body = "Please test/merge this PR and then tag Master with " + tag
239
+ # Raise a PR from release-candidate to master
240
+ user_fork = user + ':master'
241
+ try :
242
+ pr = repo .create_pull (title = 'Updating mbed-os to ' + tag , head = user_fork , base = 'master' , body = body )
243
+ ret = True
244
+ except GithubException as e :
245
+ # Default to False
246
+ print ("Creation of Pull Request from release-candidate to master failed with the following error!" )
247
+ print e
248
+ else :
249
+ print ("!!! Git push command failed." )
158
250
else :
159
- os .chdir (cwd )
160
- return False
161
-
162
- # Setup and run the tag command
163
- tag_cmd = ['git' , 'tag' , '-a' , tag , '-m' , tag ]
164
- return_code = run_cmd (tag_cmd )
165
- if return_code :
166
- os .chdir (cwd )
167
- return False
168
-
169
- # Setup and run the push command
170
- push_cmd = ['git' , 'push' , 'origin' , 'master' ]
171
- return_code = run_cmd (push_cmd )
172
-
173
- if return_code :
174
- os .chdir (cwd )
175
- return False
176
-
177
- push_cmd = ['git' , 'push' , 'origin' , tag ]
178
- return_code = run_cmd (push_cmd )
179
-
251
+ print ("!!! Git commit command failed." )
252
+ else :
253
+ print ("!!! Could not clone user fork %s\n " % fork )
254
+
255
+
180
256
os .chdir (cwd )
181
- return not return_code
257
+ return ret
182
258
183
259
def create_work_directory (path ):
184
260
""" Create a new directory specified in 'path', overwrite if the directory already
@@ -220,11 +296,27 @@ def test_compile(config, tag):
220
296
221
297
222
298
def main (arguments ):
299
+ """ Will update any mbed-os.lib files found in the example list specified by the config file.
300
+ If no config file is specified the default 'examples.json' is used.
301
+ The update is done by cloning a fork of each example (the fork must be present in the
302
+ github account specified by the github user parameter). The fork is searched for any
303
+ mbed-os.lib files and each one found is updated with the SHA corresponding to the supplied
304
+ github tag. A pull request is then made from the fork to the original example repo.
305
+
306
+ Args:
307
+ tag - tag to update the mbed-os.lib to. E.g. mbed-os-5.3.1
308
+ github_token - Pre-authorised token to allow github access
309
+ github_user - github username whose account contains the example forks
310
+ config_file - optional parameter to specify a list of examples
311
+
312
+ """
223
313
224
314
parser = argparse .ArgumentParser (description = __doc__ ,
225
315
formatter_class = argparse .RawDescriptionHelpFormatter )
226
316
parser .add_argument ('tag' , help = "mbed-os tag to which all examples will be updated" )
227
317
parser .add_argument ('-c' , '--config_file' , help = "Path to the configuration file (default is 'examples.json')" , default = 'examples.json' )
318
+ parser .add_argument ('-T' , '--github_token' , help = "GitHub token for secure access" )
319
+ parser .add_argument ('-U' , '--github_user' , help = "GitHub user for forked repos" )
228
320
229
321
args = parser .parse_args (arguments )
230
322
@@ -238,45 +330,41 @@ def main(arguments):
238
330
print ("Failed to load config file '%s'" % args .config_file )
239
331
sys .exit (1 )
240
332
241
- # Create work directories
333
+ # Create working directory
242
334
create_work_directory ('examples' )
243
-
335
+
336
+ github = Github (args .github_token )
337
+
338
+ # Get the github sha corresponding to the specified mbed-os tag
339
+ cmd = ['git' , 'rev-list' , '-1' , args .tag ]
340
+ return_code , ref = run_cmd_with_output (cmd )
341
+
342
+ if return_code :
343
+ print ("Could not obtain SHA for tag: %s\n " % args .tag )
344
+ sys .exit (1 )
345
+
244
346
# Loop through the examples
245
347
failures = []
246
348
successes = []
247
- not_compiled = []
248
349
results = {}
249
350
os .chdir ('examples' )
250
-
251
- results = test_compile (config , args .tag )
252
- lib .print_compilation_summary (results )
253
351
254
352
for example in config ['examples' ]:
255
- # Determine if this example should be updated
256
-
257
- # Attempt to update if:
258
- # group of examples passed compilation and
259
- # auto update is set to True
260
- # Note: results fields are [compiled flag, pass flag, successes list, failures list]
261
- if not results [example ['name' ]][0 ]:
262
- # Example was not compiled
263
- not_compiled += [example ['name' ]]
353
+ # Determine if this example should be updated and if so update any found
354
+ # mbed-os.lib files.
355
+
356
+ if upgrade_example (github , example , args .tag , args .github_user , ref ):
357
+ successes += [example ['name' ]]
264
358
else :
265
- if results [example ['name' ]][1 ] and example ['auto-update' ]:
266
- if upgrade_example (example , args .tag ):
267
- successes += [example ['name' ]]
268
- else :
269
- failures += [example ['name' ]]
270
- else :
271
- failures += [example ['name' ]]
359
+ failures += [example ['name' ]]
272
360
273
361
os .chdir ('../' )
274
362
275
363
# Finish the script and report the results
276
364
print (os .linesep + os .linesep + 'Finished updating examples!' + os .linesep )
277
365
278
366
if successes :
279
- print ('The following examples updated successfully:' )
367
+ print ('\n The following examples updated successfully:' )
280
368
for success in successes :
281
369
print (' - %s' % success )
282
370
@@ -285,11 +373,5 @@ def main(arguments):
285
373
for fail in failures :
286
374
print (' - %s' % fail )
287
375
288
- if not_compiled :
289
- print ('The following examples were skipped:' )
290
- for example in not_compiled :
291
- print (' - %s' % example )
292
-
293
-
294
376
if __name__ == '__main__' :
295
377
sys .exit (main (sys .argv [1 :]))
0 commit comments