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,157 @@ 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
83
-
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 )
89
- return False
104
+ return_code = False
90
105
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 )
106
+ if os .path .isfile ("mbed-os.lib" ):
107
+ # Rename command will fail on some OS's if the target file already exist,
108
+ # so ensure if it does, it is deleted first.
109
+ if os .path .isfile ("mbed-os.lib_bak" ):
110
+ os .remove ("mbed-os.lib_bak" )
111
+
112
+ os .rename ("mbed-os.lib" , "mbed-os.lib_bak" )
113
+ else :
114
+ print ("!! Error trying to backup mbed-os.lib prior to updating." )
99
115
return False
100
116
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 )
117
+ # mbed-os.lib file contains one line with the following format
118
+ # e.g. https://github.com/ARMmbed/mbed-os/#0789928ee7f2db08a419fa4a032fffd9bd477aa7
119
+ lib_re = re .compile ('https://github.com/ARMmbed/mbed-os/#[A-Za-z0-9]+' )
120
+ updated = False
121
+
122
+ # Scan through mbed-os.lib line by line
123
+ with open ('mbed-os.lib_bak' , 'r' ) as ip , open ('mbed-os.lib' , 'w' ) as op :
124
+ for line in ip :
125
+
126
+ opline = line
127
+
128
+ regexp = lib_re .match (line )
129
+ if regexp :
130
+ opline = 'https://github.com/ARMmbed/mbed-os/#' + ref
131
+ updated = True
106
132
133
+ op .write (opline )
134
+
135
+ if updated :
136
+ # Setup and run the git add command
137
+ cmd = ['git' , 'add' , 'mbed-os.lib' ]
138
+ return_code = run_cmd (cmd )
139
+
107
140
os .chdir (cwd )
108
141
return not return_code
109
142
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.
143
+ def prepare_fork (arm_example ):
144
+ """ Synchronises a cloned fork to ensure it is up to date with the original.
145
+
146
+ Args:
147
+ arm_example - Full GitHub repo path for original example
148
+ ret - True if the fork was synchronised successfully, False otherwise
149
+
150
+ """
151
+
152
+ print "In " + os .getcwd ()
153
+
154
+ for cmd in [['git' , 'remote' , 'add' , 'armmbed' , arm_example ],
155
+ ['git' , 'fetch' , 'armmbed' ],
156
+ ['git' , 'reset' , '--hard' , 'armmbed/master' ],
157
+ ['git' , 'push' , '-f' , 'origin' ]]:
158
+ if run_cmd (cmd ):
159
+ print ("preparation of the fork failed!" )
160
+ return False
161
+ return True
162
+
163
+
164
+ def upgrade_example (github , example , tag , user , ref ):
165
+ """ Clone a fork of the example specified.
166
+ Ensures the fork is up to date with the original and then and updates the associated
167
+ mbed-os.lib file on that fork to correspond to the version specified by the GitHub tag supplied.
168
+ Also deals with multiple sub-examples in the GitHub repo, updating them in the same way.
169
+ The updates are pushed to the forked repo.
170
+ Finally a PR is raised against the original example repo for the changes.
114
171
115
172
Args:
173
+ github - GitHub instance to allow internal git commands to be run
116
174
example - json example object containing the GitHub repo to update.
117
175
tag - GitHub tag corresponding to a version of mbed-os to upgrade to.
176
+ user - GitHub user name
177
+ ref - SHA corresponding to the tag
118
178
119
179
"""
120
- print ("Updating example '%s'" % example ['name' ])
180
+ ret = False
181
+ print ("\n Updating example '%s'" % example ['name' ])
121
182
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 :
183
+
184
+ full_repo_name = 'ARMmbed/' + example ['name' ]
185
+ fork = "https://github.com/" + user + '/' + example ['name' ]
186
+
187
+ # Check access to mbed-os repo
188
+ try :
189
+ repo = github .get_repo (full_repo_name , False )
190
+
191
+ except :
192
+ print ("\t \t !! Repo does not exist - skipping\n " )
128
193
return False
194
+
195
+
196
+ # Clone the forked example repo
197
+ clone_cmd = ['git' , 'clone' , fork ]
198
+ return_code = run_cmd (clone_cmd )
129
199
130
- # Find all examples
131
- example_directories = find_all_examples (example ['name' ])
132
-
133
- os .chdir (example ['name' ])
134
-
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
200
+ if not return_code :
141
201
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
202
+ # Find all examples
203
+ example_directories = find_all_examples (example ['name' ])
146
204
147
- # Setup the default commit message
148
- commit_message = 'Updating mbed-os to {{' + tag + '}}'
205
+ os .chdir (example ['name' ])
206
+
207
+ # checkout and synchronise the release-candidate branch
208
+ prepare_fork (example ['github' ])
209
+
210
+ for example_directory in example_directories :
211
+ if not upgrade_single_example (example , tag , os .path .relpath (example_directory , example ['name' ]), ref ):
212
+ os .chdir (cwd )
213
+ return False
214
+
215
+ # Setup the default commit message
216
+ commit_message = 'Updating mbed-os to ' + tag
217
+
218
+ # Setup and run the commit command
219
+ commit_cmd = ['git' , 'commit' , '-m' , commit_message ]
220
+ return_code = run_cmd (commit_cmd )
221
+ if not return_code :
222
+
223
+ # Setup and run the push command
224
+ push_cmd = ['git' , 'push' , 'origin' ]
225
+ return_code = run_cmd (push_cmd )
149
226
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." )
227
+ if not return_code :
228
+ body = "Please test/merge this PR and then tag Master with " + tag
229
+ # Raise a PR from release-candidate to master
230
+ user_fork = user + ':master'
231
+ try :
232
+ pr = repo .create_pull (title = 'Updating mbed-os to ' + tag , head = user_fork , base = 'master' , body = body )
233
+ ret = True
234
+ except GithubException as e :
235
+ # Default to False
236
+ print ("Creation of Pull Request from release-candidate to master failed with the following error!" )
237
+ print e
238
+ else :
239
+ print ("!!! Git push command failed." )
158
240
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
-
241
+ print ("!!! Git commit command failed." )
242
+ else :
243
+ print ("!!! Could not clone user fork %s\n " % fork )
244
+
245
+
180
246
os .chdir (cwd )
181
- return not return_code
247
+ return ret
182
248
183
249
def create_work_directory (path ):
184
250
""" Create a new directory specified in 'path', overwrite if the directory already
@@ -220,11 +286,27 @@ def test_compile(config, tag):
220
286
221
287
222
288
def main (arguments ):
289
+ """ Will update any mbed-os.lib files found in the example list specified by the config file.
290
+ If no config file is specified the default 'examples.json' is used.
291
+ The update is done by cloning a fork of each example (the fork must be present in the
292
+ github account specified by the github user parameter). The fork is searched for any
293
+ mbed-os.lib files and each one found is updated with the SHA corresponding to the supplied
294
+ github tag. A pull request is then made from the fork to the original example repo.
295
+
296
+ Args:
297
+ tag - tag to update the mbed-os.lib to. E.g. mbed-os-5.3.1
298
+ github_token - Pre-authorised token to allow github access
299
+ github_user - github username whose account contains the example forks
300
+ config_file - optional parameter to specify a list of examples
301
+
302
+ """
223
303
224
304
parser = argparse .ArgumentParser (description = __doc__ ,
225
305
formatter_class = argparse .RawDescriptionHelpFormatter )
226
306
parser .add_argument ('tag' , help = "mbed-os tag to which all examples will be updated" )
227
307
parser .add_argument ('-c' , '--config_file' , help = "Path to the configuration file (default is 'examples.json')" , default = 'examples.json' )
308
+ parser .add_argument ('-T' , '--github_token' , help = "GitHub token for secure access" )
309
+ parser .add_argument ('-U' , '--github_user' , help = "GitHub user for forked repos" )
228
310
229
311
args = parser .parse_args (arguments )
230
312
@@ -238,45 +320,41 @@ def main(arguments):
238
320
print ("Failed to load config file '%s'" % args .config_file )
239
321
sys .exit (1 )
240
322
241
- # Create work directories
323
+ # Create working directory
242
324
create_work_directory ('examples' )
243
-
325
+
326
+ github = Github (args .github_token )
327
+
328
+ # Get the github sha corresponding to the specified mbed-os tag
329
+ cmd = ['git' , 'rev-list' , '-1' , args .tag ]
330
+ return_code , ref = run_cmd_with_output (cmd )
331
+
332
+ if return_code :
333
+ print ("Could not obtain SHA for tag: %s\n " % args .tag )
334
+ sys .exit (1 )
335
+
244
336
# Loop through the examples
245
337
failures = []
246
338
successes = []
247
- not_compiled = []
248
339
results = {}
249
340
os .chdir ('examples' )
250
-
251
- results = test_compile (config , args .tag )
252
- lib .print_compilation_summary (results )
253
341
254
342
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' ]]
343
+ # Determine if this example should be updated and if so update any found
344
+ # mbed-os.lib files.
345
+
346
+ if upgrade_example (github , example , args .tag , args .github_user , ref ):
347
+ successes += [example ['name' ]]
264
348
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' ]]
349
+ failures += [example ['name' ]]
272
350
273
351
os .chdir ('../' )
274
352
275
353
# Finish the script and report the results
276
354
print (os .linesep + os .linesep + 'Finished updating examples!' + os .linesep )
277
355
278
356
if successes :
279
- print ('The following examples updated successfully:' )
357
+ print ('\n The following examples updated successfully:' )
280
358
for success in successes :
281
359
print (' - %s' % success )
282
360
@@ -285,11 +363,5 @@ def main(arguments):
285
363
for fail in failures :
286
364
print (' - %s' % fail )
287
365
288
- if not_compiled :
289
- print ('The following examples were skipped:' )
290
- for example in not_compiled :
291
- print (' - %s' % example )
292
-
293
-
294
366
if __name__ == '__main__' :
295
367
sys .exit (main (sys .argv [1 :]))
0 commit comments