Skip to content

Commit 2cf96c8

Browse files
committed
Modify update command to directly edit the mbed-os.lib files for each example specified in the supplied .json file, in a user specified fork. A pull request is then made from each fork to the ARMmbed master repo.
New usage: python update.py <args> Where <args> are: [-c <file.json>] optional argument for example list, default example.json -U <github user with forked repos> -T <github authorisation token> tag
1 parent 609d6a1 commit 2cf96c8

File tree

1 file changed

+192
-110
lines changed

1 file changed

+192
-110
lines changed

tools/test/examples/update.py

Lines changed: 192 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import subprocess
99
import shutil
1010
import stat
11+
import re
12+
from github import Github, GithubException
1113

1214
ROOT = abspath(dirname(dirname(dirname(dirname(__file__)))))
1315
sys.path.insert(0, ROOT)
@@ -33,6 +35,26 @@ def run_cmd(command, print_warning_on_fail=True):
3335

3436
return return_code
3537

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
3658

3759
def rmtree_readonly(directory):
3860
""" Deletes a readonly directory tree.
@@ -63,7 +85,7 @@ def find_all_examples(path):
6385

6486
return examples
6587

66-
def upgrade_single_example(example, tag, directory):
88+
def upgrade_single_example(example, tag, directory, ref):
6789
""" Updates the mbed-os.lib file in the example specified to correspond to the
6890
version specified by the GitHub tag supplied. Also deals with
6991
multiple sub-examples in the GitHub repo, updating them in the same way.
@@ -72,113 +94,167 @@ def upgrade_single_example(example, tag, directory):
7294
example - json example object containing the GitHub repo to update.
7395
tag - GitHub tag corresponding to a version of mbed-os to upgrade to.
7496
directory - directory path for the example.
97+
ref - SHA corresponding to the supplied tag
7598
returns - True if the upgrade was successful, False otherwise.
7699
77100
"""
78-
print("Upgrading single example at path '%s'" % directory)
79101
cwd = os.getcwd()
80102
os.chdir(directory)
81103

82-
return_code = None
104+
return_code = False
83105

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.")
89110
return False
90111

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
106127

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+
107135
os.chdir(cwd)
108136
return not return_code
109137

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.
114140
115141
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
116184
example - json example object containing the GitHub repo to update.
117185
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
118188
119189
"""
120-
print("Updating example '%s'" % example['name'])
190+
ret = False
191+
print("\nUpdating example '%s'" % example['name'])
121192
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")
128203
return False
204+
205+
206+
# Clone the forked example repo
207+
clone_cmd = ['git', 'clone', fork]
208+
return_code = run_cmd(clone_cmd)
129209

130-
# Find all examples
131-
example_directories = find_all_examples(example['name'])
132-
133-
os.chdir(example['name'])
210+
if not return_code:
134211

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'])
146216

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)
149236

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.")
158250
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+
180256
os.chdir(cwd)
181-
return not return_code
257+
return ret
182258

183259
def create_work_directory(path):
184260
""" Create a new directory specified in 'path', overwrite if the directory already
@@ -220,11 +296,27 @@ def test_compile(config, tag):
220296

221297

222298
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+
"""
223313

224314
parser = argparse.ArgumentParser(description=__doc__,
225315
formatter_class=argparse.RawDescriptionHelpFormatter)
226316
parser.add_argument('tag', help="mbed-os tag to which all examples will be updated")
227317
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")
228320

229321
args = parser.parse_args(arguments)
230322

@@ -238,45 +330,41 @@ def main(arguments):
238330
print("Failed to load config file '%s'" % args.config_file)
239331
sys.exit(1)
240332

241-
# Create work directories
333+
# Create working directory
242334
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+
244346
# Loop through the examples
245347
failures = []
246348
successes = []
247-
not_compiled = []
248349
results = {}
249350
os.chdir('examples')
250-
251-
results = test_compile(config, args.tag)
252-
lib.print_compilation_summary(results)
253351

254352
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']]
264358
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']]
272360

273361
os.chdir('../')
274362

275363
# Finish the script and report the results
276364
print(os.linesep + os.linesep +'Finished updating examples!' + os.linesep)
277365

278366
if successes:
279-
print('The following examples updated successfully:')
367+
print('\nThe following examples updated successfully:')
280368
for success in successes:
281369
print(' - %s' % success)
282370

@@ -285,11 +373,5 @@ def main(arguments):
285373
for fail in failures:
286374
print(' - %s' % fail)
287375

288-
if not_compiled:
289-
print('The following examples were skipped:')
290-
for example in not_compiled:
291-
print(' - %s' % example)
292-
293-
294376
if __name__ == '__main__':
295377
sys.exit(main(sys.argv[1:]))

0 commit comments

Comments
 (0)