mbed-os

Dependents:   cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more

Committer:
be_bryan
Date:
Mon Dec 11 17:54:04 2017 +0000
Revision:
0:b74591d5ab33
motor ++

Who changed what in which revision?

UserRevisionLine numberNew contents of line
be_bryan 0:b74591d5ab33 1 #!/usr/bin/env python
be_bryan 0:b74591d5ab33 2
be_bryan 0:b74591d5ab33 3 # This script is used to update the version of mbed-os used within a specified set of example
be_bryan 0:b74591d5ab33 4 # applications. The list of examples to be updated lives in the examples.json file and is
be_bryan 0:b74591d5ab33 5 # shared with the examples.py script. Logging is used to provide varying levels of output
be_bryan 0:b74591d5ab33 6 # during execution.
be_bryan 0:b74591d5ab33 7 #
be_bryan 0:b74591d5ab33 8 # There are two modes that can be used:
be_bryan 0:b74591d5ab33 9 # 1) Update the ARMmbed/master branch of the specified example
be_bryan 0:b74591d5ab33 10 #
be_bryan 0:b74591d5ab33 11 # This is done by updating a user fork of the example and then raising a pull request
be_bryan 0:b74591d5ab33 12 # against ARMmbed/master.
be_bryan 0:b74591d5ab33 13 #
be_bryan 0:b74591d5ab33 14 # 2) Update a different ARMmbed branch of the specified example
be_bryan 0:b74591d5ab33 15 #
be_bryan 0:b74591d5ab33 16 # A branch to update is specified. If it doesn't already exist then it is first created.
be_bryan 0:b74591d5ab33 17 # This branch will be updated and the change automatically pushed. The new branch will
be_bryan 0:b74591d5ab33 18 # be created from the specified source branch.
be_bryan 0:b74591d5ab33 19 #
be_bryan 0:b74591d5ab33 20 # The modes are controlled via configuration data in the json file.
be_bryan 0:b74591d5ab33 21 # E.g.
be_bryan 0:b74591d5ab33 22 #
be_bryan 0:b74591d5ab33 23 # "update-config" : {
be_bryan 0:b74591d5ab33 24 # "help" : "Update each example repo with a version of mbed-os identified by the tag",
be_bryan 0:b74591d5ab33 25 # "via-fork" : {
be_bryan 0:b74591d5ab33 26 # "help" : "-f cmd line option. Update a fork",
be_bryan 0:b74591d5ab33 27 # "github-user" : "adbridge"
be_bryan 0:b74591d5ab33 28 # },
be_bryan 0:b74591d5ab33 29 # "via-branch" : {
be_bryan 0:b74591d5ab33 30 # "help" : "-b cmd line option. Update dst branch, created from src branch",
be_bryan 0:b74591d5ab33 31 # "src-branch" : "mbed-os-5.5.0-rc1-oob",
be_bryan 0:b74591d5ab33 32 # "dst-branch" : "mbed-os-5.5.0-rc2-oob"
be_bryan 0:b74591d5ab33 33 # },
be_bryan 0:b74591d5ab33 34 # "tag" : "mbed-os-5.5.0-rc2"
be_bryan 0:b74591d5ab33 35 #
be_bryan 0:b74591d5ab33 36 #
be_bryan 0:b74591d5ab33 37 # Command usage:
be_bryan 0:b74591d5ab33 38 #
be_bryan 0:b74591d5ab33 39 # update.py -c <config file> - T <github_token> -f -b -s
be_bryan 0:b74591d5ab33 40 #
be_bryan 0:b74591d5ab33 41 # Where:
be_bryan 0:b74591d5ab33 42 # -c <config file> - Optional path to an examples file.
be_bryan 0:b74591d5ab33 43 # If not proved the default is 'examples.json'
be_bryan 0:b74591d5ab33 44 # -T <github_token> - GitHub token for secure access (required)
be_bryan 0:b74591d5ab33 45 # -f - Update forked repos. This will use the 'github-user' parameter in
be_bryan 0:b74591d5ab33 46 # the 'via-fork' section.
be_bryan 0:b74591d5ab33 47 # -b - Update branched repos. This will use the "src-branch" and
be_bryan 0:b74591d5ab33 48 # "dst-branch" parameters in the 'via-branch' section. The destination
be_bryan 0:b74591d5ab33 49 # branch is created from the source branch (if it doesn't already exist).
be_bryan 0:b74591d5ab33 50 # -s - Show the status of any pull requests with a tag matching that in the
be_bryan 0:b74591d5ab33 51 # json config file
be_bryan 0:b74591d5ab33 52 #
be_bryan 0:b74591d5ab33 53 # The options -f, -b and -s are mutually exlusive. Only one can be specified.
be_bryan 0:b74591d5ab33 54 #
be_bryan 0:b74591d5ab33 55 #
be_bryan 0:b74591d5ab33 56
be_bryan 0:b74591d5ab33 57 import os
be_bryan 0:b74591d5ab33 58 from os.path import dirname, abspath, basename, join
be_bryan 0:b74591d5ab33 59 import sys
be_bryan 0:b74591d5ab33 60 import logging
be_bryan 0:b74591d5ab33 61 import argparse
be_bryan 0:b74591d5ab33 62 import json
be_bryan 0:b74591d5ab33 63 import subprocess
be_bryan 0:b74591d5ab33 64 import shutil
be_bryan 0:b74591d5ab33 65 import stat
be_bryan 0:b74591d5ab33 66 import re
be_bryan 0:b74591d5ab33 67 from github import Github, GithubException
be_bryan 0:b74591d5ab33 68 from jinja2 import FileSystemLoader, StrictUndefined
be_bryan 0:b74591d5ab33 69 from jinja2.environment import Environment
be_bryan 0:b74591d5ab33 70
be_bryan 0:b74591d5ab33 71 ROOT = abspath(dirname(dirname(dirname(dirname(__file__)))))
be_bryan 0:b74591d5ab33 72 sys.path.insert(0, ROOT)
be_bryan 0:b74591d5ab33 73
be_bryan 0:b74591d5ab33 74 import examples_lib as lib
be_bryan 0:b74591d5ab33 75 from examples_lib import SUPPORTED_TOOLCHAINS
be_bryan 0:b74591d5ab33 76
be_bryan 0:b74591d5ab33 77 userlog = logging.getLogger("Update")
be_bryan 0:b74591d5ab33 78
be_bryan 0:b74591d5ab33 79 # Set logging level
be_bryan 0:b74591d5ab33 80 userlog.setLevel(logging.DEBUG)
be_bryan 0:b74591d5ab33 81
be_bryan 0:b74591d5ab33 82 # Everything is output to the log file
be_bryan 0:b74591d5ab33 83 logfile = os.path.join(os.getcwd(), 'update.log')
be_bryan 0:b74591d5ab33 84 fh = logging.FileHandler(logfile)
be_bryan 0:b74591d5ab33 85 fh.setLevel(logging.DEBUG)
be_bryan 0:b74591d5ab33 86
be_bryan 0:b74591d5ab33 87 # create console handler with a higher log level
be_bryan 0:b74591d5ab33 88 ch = logging.StreamHandler()
be_bryan 0:b74591d5ab33 89 ch.setLevel(logging.INFO)
be_bryan 0:b74591d5ab33 90
be_bryan 0:b74591d5ab33 91 formatter = logging.Formatter('%(name)s: %(levelname)s - %(message)s')
be_bryan 0:b74591d5ab33 92 ch.setFormatter(formatter)
be_bryan 0:b74591d5ab33 93 fh.setFormatter(formatter)
be_bryan 0:b74591d5ab33 94
be_bryan 0:b74591d5ab33 95 # add the handlers to the logger
be_bryan 0:b74591d5ab33 96 userlog.addHandler(fh)
be_bryan 0:b74591d5ab33 97 userlog.addHandler(ch)
be_bryan 0:b74591d5ab33 98
be_bryan 0:b74591d5ab33 99 def run_cmd(command, exit_on_failure=False):
be_bryan 0:b74591d5ab33 100 """ Run a system command returning a status result
be_bryan 0:b74591d5ab33 101
be_bryan 0:b74591d5ab33 102 This is just a wrapper for the run_cmd_with_output() function, but
be_bryan 0:b74591d5ab33 103 only returns the status of the call.
be_bryan 0:b74591d5ab33 104
be_bryan 0:b74591d5ab33 105 Args:
be_bryan 0:b74591d5ab33 106 command - system command as a list of tokens
be_bryan 0:b74591d5ab33 107 exit_on_failure - If True exit the program on failure (default = False)
be_bryan 0:b74591d5ab33 108
be_bryan 0:b74591d5ab33 109 Returns:
be_bryan 0:b74591d5ab33 110 return_code - True/False indicating the success/failure of the command
be_bryan 0:b74591d5ab33 111 """
be_bryan 0:b74591d5ab33 112 return_code, _ = run_cmd_with_output(command, exit_on_failure)
be_bryan 0:b74591d5ab33 113 return return_code
be_bryan 0:b74591d5ab33 114
be_bryan 0:b74591d5ab33 115 def run_cmd_with_output(command, exit_on_failure=False):
be_bryan 0:b74591d5ab33 116 """ Run a system command returning a status result and any command output
be_bryan 0:b74591d5ab33 117
be_bryan 0:b74591d5ab33 118 Passes a command to the system and returns a True/False result once the
be_bryan 0:b74591d5ab33 119 command has been executed, indicating success/failure. If the command was
be_bryan 0:b74591d5ab33 120 successful then the output from the command is returned to the caller.
be_bryan 0:b74591d5ab33 121 Commands are passed as a string.
be_bryan 0:b74591d5ab33 122 E.g. The command 'git remote -v' would be passed in as "git remote -v"
be_bryan 0:b74591d5ab33 123
be_bryan 0:b74591d5ab33 124 Args:
be_bryan 0:b74591d5ab33 125 command - system command as a string
be_bryan 0:b74591d5ab33 126 exit_on_failure - If True exit the program on failure (default = False)
be_bryan 0:b74591d5ab33 127
be_bryan 0:b74591d5ab33 128 Returns:
be_bryan 0:b74591d5ab33 129 return_code - True/False indicating the success/failure of the command
be_bryan 0:b74591d5ab33 130 output - The output of the command if it was successful, else empty string
be_bryan 0:b74591d5ab33 131 """
be_bryan 0:b74591d5ab33 132 text = '[Exec] ' + command
be_bryan 0:b74591d5ab33 133 userlog.debug(text)
be_bryan 0:b74591d5ab33 134 returncode = 0
be_bryan 0:b74591d5ab33 135 output = ""
be_bryan 0:b74591d5ab33 136 try:
be_bryan 0:b74591d5ab33 137 output = subprocess.check_output(command, shell=True)
be_bryan 0:b74591d5ab33 138 except subprocess.CalledProcessError as e:
be_bryan 0:b74591d5ab33 139 text = "The command " + str(command) + "failed with return code: " + str(e.returncode)
be_bryan 0:b74591d5ab33 140 userlog.warning(text)
be_bryan 0:b74591d5ab33 141 returncode = e.returncode
be_bryan 0:b74591d5ab33 142 if exit_on_failure:
be_bryan 0:b74591d5ab33 143 sys.exit(1)
be_bryan 0:b74591d5ab33 144 return returncode, output
be_bryan 0:b74591d5ab33 145
be_bryan 0:b74591d5ab33 146
be_bryan 0:b74591d5ab33 147 def rmtree_readonly(directory):
be_bryan 0:b74591d5ab33 148 """ Deletes a readonly directory tree.
be_bryan 0:b74591d5ab33 149
be_bryan 0:b74591d5ab33 150 Args:
be_bryan 0:b74591d5ab33 151 directory - tree to delete
be_bryan 0:b74591d5ab33 152 """
be_bryan 0:b74591d5ab33 153 def remove_readonly(func, path, _):
be_bryan 0:b74591d5ab33 154 os.chmod(path, stat.S_IWRITE)
be_bryan 0:b74591d5ab33 155 func(path)
be_bryan 0:b74591d5ab33 156
be_bryan 0:b74591d5ab33 157 shutil.rmtree(directory, onerror=remove_readonly)
be_bryan 0:b74591d5ab33 158
be_bryan 0:b74591d5ab33 159 def find_all_examples(path):
be_bryan 0:b74591d5ab33 160 """ Search the path for examples
be_bryan 0:b74591d5ab33 161
be_bryan 0:b74591d5ab33 162 Description:
be_bryan 0:b74591d5ab33 163
be_bryan 0:b74591d5ab33 164 Searches the path specified for sub-example folders, ie those containing an
be_bryan 0:b74591d5ab33 165 mbed-os.lib file. If found adds the path to the sub-example to a list which is
be_bryan 0:b74591d5ab33 166 then returned.
be_bryan 0:b74591d5ab33 167
be_bryan 0:b74591d5ab33 168 Args:
be_bryan 0:b74591d5ab33 169 path - path to search.
be_bryan 0:b74591d5ab33 170 examples - (returned) list of paths to example directories.
be_bryan 0:b74591d5ab33 171
be_bryan 0:b74591d5ab33 172 """
be_bryan 0:b74591d5ab33 173 examples = []
be_bryan 0:b74591d5ab33 174 for root, dirs, files in os.walk(path):
be_bryan 0:b74591d5ab33 175 if 'mbed-os.lib' in files:
be_bryan 0:b74591d5ab33 176 examples += [root]
be_bryan 0:b74591d5ab33 177
be_bryan 0:b74591d5ab33 178 return examples
be_bryan 0:b74591d5ab33 179
be_bryan 0:b74591d5ab33 180 def upgrade_single_example(example, tag, directory, ref):
be_bryan 0:b74591d5ab33 181 """ Update the mbed-os version for a single example
be_bryan 0:b74591d5ab33 182
be_bryan 0:b74591d5ab33 183 Description:
be_bryan 0:b74591d5ab33 184
be_bryan 0:b74591d5ab33 185 Updates the mbed-os.lib file in the example specified to correspond to the
be_bryan 0:b74591d5ab33 186 version specified by the GitHub tag supplied. Also deals with
be_bryan 0:b74591d5ab33 187 multiple sub-examples in the GitHub repo, updating them in the same way.
be_bryan 0:b74591d5ab33 188
be_bryan 0:b74591d5ab33 189 Args:
be_bryan 0:b74591d5ab33 190 example - json example object containing the GitHub repo to update.
be_bryan 0:b74591d5ab33 191 tag - GitHub tag corresponding to a version of mbed-os to upgrade to.
be_bryan 0:b74591d5ab33 192 directory - directory path for the example.
be_bryan 0:b74591d5ab33 193 ref - SHA corresponding to the supplied tag
be_bryan 0:b74591d5ab33 194 returns - True if the upgrade was successful, False otherwise.
be_bryan 0:b74591d5ab33 195
be_bryan 0:b74591d5ab33 196 """
be_bryan 0:b74591d5ab33 197 cwd = os.getcwd()
be_bryan 0:b74591d5ab33 198 os.chdir(directory)
be_bryan 0:b74591d5ab33 199
be_bryan 0:b74591d5ab33 200 return_code = False
be_bryan 0:b74591d5ab33 201
be_bryan 0:b74591d5ab33 202 if os.path.isfile("mbed-os.lib"):
be_bryan 0:b74591d5ab33 203 # Rename command will fail on some OS's if the target file already exist,
be_bryan 0:b74591d5ab33 204 # so ensure if it does, it is deleted first.
be_bryan 0:b74591d5ab33 205 if os.path.isfile("mbed-os.lib_bak"):
be_bryan 0:b74591d5ab33 206 os.remove("mbed-os.lib_bak")
be_bryan 0:b74591d5ab33 207
be_bryan 0:b74591d5ab33 208 os.rename("mbed-os.lib", "mbed-os.lib_bak")
be_bryan 0:b74591d5ab33 209 else:
be_bryan 0:b74591d5ab33 210 userlog.error("Failed to backup mbed-os.lib prior to updating.")
be_bryan 0:b74591d5ab33 211 return False
be_bryan 0:b74591d5ab33 212
be_bryan 0:b74591d5ab33 213 # mbed-os.lib file contains one line with the following format
be_bryan 0:b74591d5ab33 214 # e.g. https://github.com/ARMmbed/mbed-os/#0789928ee7f2db08a419fa4a032fffd9bd477aa7
be_bryan 0:b74591d5ab33 215 lib_re = re.compile('https://github.com/ARMmbed/mbed-os/#[A-Za-z0-9]+')
be_bryan 0:b74591d5ab33 216 updated = False
be_bryan 0:b74591d5ab33 217
be_bryan 0:b74591d5ab33 218 # Scan through mbed-os.lib line by line
be_bryan 0:b74591d5ab33 219 with open('mbed-os.lib_bak', 'r') as ip, open('mbed-os.lib', 'w') as op:
be_bryan 0:b74591d5ab33 220 for line in ip:
be_bryan 0:b74591d5ab33 221
be_bryan 0:b74591d5ab33 222 opline = line
be_bryan 0:b74591d5ab33 223
be_bryan 0:b74591d5ab33 224 regexp = lib_re.match(line)
be_bryan 0:b74591d5ab33 225 if regexp:
be_bryan 0:b74591d5ab33 226 opline = 'https://github.com/ARMmbed/mbed-os/#' + ref
be_bryan 0:b74591d5ab33 227 updated = True
be_bryan 0:b74591d5ab33 228
be_bryan 0:b74591d5ab33 229 op.write(opline)
be_bryan 0:b74591d5ab33 230
be_bryan 0:b74591d5ab33 231 if updated:
be_bryan 0:b74591d5ab33 232 # Setup and run the git add command
be_bryan 0:b74591d5ab33 233 cmd = "git add mbed-os.lib"
be_bryan 0:b74591d5ab33 234 return_code = run_cmd(cmd)
be_bryan 0:b74591d5ab33 235
be_bryan 0:b74591d5ab33 236 os.chdir(cwd)
be_bryan 0:b74591d5ab33 237 return not return_code
be_bryan 0:b74591d5ab33 238
be_bryan 0:b74591d5ab33 239 def prepare_fork(arm_example):
be_bryan 0:b74591d5ab33 240 """ Synchronises a cloned fork to ensure it is up to date with the original.
be_bryan 0:b74591d5ab33 241
be_bryan 0:b74591d5ab33 242 Description:
be_bryan 0:b74591d5ab33 243
be_bryan 0:b74591d5ab33 244 This function sets a fork of an ARMmbed repo to be up to date with the
be_bryan 0:b74591d5ab33 245 repo it was forked from. It does this by hard resetting to the ARMmbed
be_bryan 0:b74591d5ab33 246 master branch.
be_bryan 0:b74591d5ab33 247
be_bryan 0:b74591d5ab33 248 Args:
be_bryan 0:b74591d5ab33 249 arm_example - Full GitHub repo path for original example
be_bryan 0:b74591d5ab33 250
be_bryan 0:b74591d5ab33 251 """
be_bryan 0:b74591d5ab33 252
be_bryan 0:b74591d5ab33 253 logstr = "In: " + os.getcwd()
be_bryan 0:b74591d5ab33 254 userlog.debug(logstr)
be_bryan 0:b74591d5ab33 255
be_bryan 0:b74591d5ab33 256 for cmd in ["git remote add armmbed " + str(arm_example),
be_bryan 0:b74591d5ab33 257 "git fetch armmbed",
be_bryan 0:b74591d5ab33 258 "git reset --hard armmbed/master",
be_bryan 0:b74591d5ab33 259 "git push -f origin"]:
be_bryan 0:b74591d5ab33 260 run_cmd(cmd, exit_on_failure=True)
be_bryan 0:b74591d5ab33 261
be_bryan 0:b74591d5ab33 262 def prepare_branch(src, dst):
be_bryan 0:b74591d5ab33 263 """ Set up at branch ready for use in updating examples
be_bryan 0:b74591d5ab33 264
be_bryan 0:b74591d5ab33 265 Description:
be_bryan 0:b74591d5ab33 266
be_bryan 0:b74591d5ab33 267 This function checks whether or not the supplied dst branch exists.
be_bryan 0:b74591d5ab33 268 If it does not, the branch is created from the src and pushed to the origin.
be_bryan 0:b74591d5ab33 269 The branch is then switched to.
be_bryan 0:b74591d5ab33 270
be_bryan 0:b74591d5ab33 271 Args:
be_bryan 0:b74591d5ab33 272 src - branch to create the dst branch from
be_bryan 0:b74591d5ab33 273 dst - branch to update
be_bryan 0:b74591d5ab33 274
be_bryan 0:b74591d5ab33 275 """
be_bryan 0:b74591d5ab33 276
be_bryan 0:b74591d5ab33 277 userlog.debug("Preparing branch: %s", dst)
be_bryan 0:b74591d5ab33 278
be_bryan 0:b74591d5ab33 279 # Check if branch already exists or not.
be_bryan 0:b74591d5ab33 280 # We can use the 'git branch -r' command. This returns all the remote branches for
be_bryan 0:b74591d5ab33 281 # the current repo.
be_bryan 0:b74591d5ab33 282 # The output consists of a list of lines of the form:
be_bryan 0:b74591d5ab33 283 # origin/<branch>
be_bryan 0:b74591d5ab33 284 # From these we need to extract just the branch names to a list and then check if
be_bryan 0:b74591d5ab33 285 # the specified dst exists in that list
be_bryan 0:b74591d5ab33 286 branches = []
be_bryan 0:b74591d5ab33 287 cmd = "git branch -r"
be_bryan 0:b74591d5ab33 288 _, output = run_cmd_with_output(cmd, exit_on_failure=True)
be_bryan 0:b74591d5ab33 289
be_bryan 0:b74591d5ab33 290 branches = [line.split('/')[1] for line in output.split('\n') if 'origin' in line and not '->' in line]
be_bryan 0:b74591d5ab33 291
be_bryan 0:b74591d5ab33 292 if not dst in branches:
be_bryan 0:b74591d5ab33 293
be_bryan 0:b74591d5ab33 294 # OOB branch does not exist thus create it, first ensuring we are on
be_bryan 0:b74591d5ab33 295 # the src branch and then check it out
be_bryan 0:b74591d5ab33 296
be_bryan 0:b74591d5ab33 297 for cmd in ["git checkout " + str(src),
be_bryan 0:b74591d5ab33 298 "git checkout -b " + str(dst),
be_bryan 0:b74591d5ab33 299 "git push -u origin " + str(dst)]:
be_bryan 0:b74591d5ab33 300
be_bryan 0:b74591d5ab33 301 run_cmd(cmd, exit_on_failure=True)
be_bryan 0:b74591d5ab33 302
be_bryan 0:b74591d5ab33 303 else:
be_bryan 0:b74591d5ab33 304 cmd = "git checkout " + str(dst)
be_bryan 0:b74591d5ab33 305 run_cmd(cmd, exit_on_failure=True)
be_bryan 0:b74591d5ab33 306
be_bryan 0:b74591d5ab33 307 def upgrade_example(github, example, tag, ref, user, src, dst, template):
be_bryan 0:b74591d5ab33 308 """ Upgrade all versions of mbed-os.lib found in the specified example repo
be_bryan 0:b74591d5ab33 309
be_bryan 0:b74591d5ab33 310 Description:
be_bryan 0:b74591d5ab33 311
be_bryan 0:b74591d5ab33 312 Clone a version of the example specified and upgrade all versions of
be_bryan 0:b74591d5ab33 313 mbed-os.lib found within its tree. The version cloned and how it
be_bryan 0:b74591d5ab33 314 is upgraded depends on the user, src and dst settings.
be_bryan 0:b74591d5ab33 315 1) user == None
be_bryan 0:b74591d5ab33 316 The destination branch will be updated with the version of mbed-os
be_bryan 0:b74591d5ab33 317 idenfied by the tag. If the destination branch does not exist then it
be_bryan 0:b74591d5ab33 318 will be created from the source branch.
be_bryan 0:b74591d5ab33 319
be_bryan 0:b74591d5ab33 320 2) user != None
be_bryan 0:b74591d5ab33 321 The master branch of a fork of the example will be updated with the
be_bryan 0:b74591d5ab33 322 version of mbed-os identified by the tag.
be_bryan 0:b74591d5ab33 323
be_bryan 0:b74591d5ab33 324 Args:
be_bryan 0:b74591d5ab33 325 github - GitHub instance to allow internal git commands to be run
be_bryan 0:b74591d5ab33 326 example - json example object containing the GitHub repo to update.
be_bryan 0:b74591d5ab33 327 tag - GitHub tag corresponding to a version of mbed-os to upgrade to.
be_bryan 0:b74591d5ab33 328 ref - SHA corresponding to the tag
be_bryan 0:b74591d5ab33 329 user - GitHub user name
be_bryan 0:b74591d5ab33 330 src - branch to create the dst branch from
be_bryan 0:b74591d5ab33 331 dst - branch to update
be_bryan 0:b74591d5ab33 332
be_bryan 0:b74591d5ab33 333 returns True if the upgrade was successful, False otherwise
be_bryan 0:b74591d5ab33 334 """
be_bryan 0:b74591d5ab33 335
be_bryan 0:b74591d5ab33 336 # If a user has not been specified then branch update will be used and thus
be_bryan 0:b74591d5ab33 337 # the git user will be ARMmbed.
be_bryan 0:b74591d5ab33 338 if not user:
be_bryan 0:b74591d5ab33 339 user = 'ARMmbed'
be_bryan 0:b74591d5ab33 340
be_bryan 0:b74591d5ab33 341 ret = False
be_bryan 0:b74591d5ab33 342 userlog.info("Updating example '%s'", example['name'])
be_bryan 0:b74591d5ab33 343 userlog.debug("User: %s", user)
be_bryan 0:b74591d5ab33 344 userlog.debug("Src branch: %s", (src or "None"))
be_bryan 0:b74591d5ab33 345 userlog.debug("Dst branch: %s", (dst or "None"))
be_bryan 0:b74591d5ab33 346
be_bryan 0:b74591d5ab33 347 cwd = os.getcwd()
be_bryan 0:b74591d5ab33 348
be_bryan 0:b74591d5ab33 349 update_repo = "https://github.com/" + user + '/' + example['name']
be_bryan 0:b74591d5ab33 350 userlog.debug("Update repository: %s", update_repo)
be_bryan 0:b74591d5ab33 351
be_bryan 0:b74591d5ab33 352 # Clone the example repo
be_bryan 0:b74591d5ab33 353 clone_cmd = "git clone " + str(update_repo)
be_bryan 0:b74591d5ab33 354 return_code = run_cmd(clone_cmd)
be_bryan 0:b74591d5ab33 355
be_bryan 0:b74591d5ab33 356 if not return_code:
be_bryan 0:b74591d5ab33 357
be_bryan 0:b74591d5ab33 358 # Find all examples
be_bryan 0:b74591d5ab33 359 example_directories = find_all_examples(example['name'])
be_bryan 0:b74591d5ab33 360
be_bryan 0:b74591d5ab33 361 os.chdir(example['name'])
be_bryan 0:b74591d5ab33 362
be_bryan 0:b74591d5ab33 363 # If the user is ARMmbed then a branch is used.
be_bryan 0:b74591d5ab33 364 if user == 'ARMmbed':
be_bryan 0:b74591d5ab33 365 prepare_branch(src, dst)
be_bryan 0:b74591d5ab33 366 else:
be_bryan 0:b74591d5ab33 367 prepare_fork(example['github'])
be_bryan 0:b74591d5ab33 368
be_bryan 0:b74591d5ab33 369 for example_directory in example_directories:
be_bryan 0:b74591d5ab33 370 if not upgrade_single_example(example, tag, os.path.relpath(example_directory, example['name']), ref):
be_bryan 0:b74591d5ab33 371 os.chdir(cwd)
be_bryan 0:b74591d5ab33 372 return False
be_bryan 0:b74591d5ab33 373
be_bryan 0:b74591d5ab33 374 # Setup and run the commit command
be_bryan 0:b74591d5ab33 375 commit_cmd = "git commit -m \"Updating mbed-os to " + tag + "\""
be_bryan 0:b74591d5ab33 376 return_code = run_cmd(commit_cmd)
be_bryan 0:b74591d5ab33 377 if not return_code:
be_bryan 0:b74591d5ab33 378
be_bryan 0:b74591d5ab33 379 # Setup and run the push command
be_bryan 0:b74591d5ab33 380 push_cmd = "git push origin"
be_bryan 0:b74591d5ab33 381 return_code = run_cmd(push_cmd)
be_bryan 0:b74591d5ab33 382
be_bryan 0:b74591d5ab33 383 if not return_code:
be_bryan 0:b74591d5ab33 384 # If the user is not ARMmbed then a fork is being used
be_bryan 0:b74591d5ab33 385 if user != 'ARMmbed':
be_bryan 0:b74591d5ab33 386
be_bryan 0:b74591d5ab33 387 upstream_repo = 'ARMmbed/'+ example['name']
be_bryan 0:b74591d5ab33 388 userlog.debug("Upstream repository: %s", upstream_repo)
be_bryan 0:b74591d5ab33 389 # Check access to mbed-os repo
be_bryan 0:b74591d5ab33 390 try:
be_bryan 0:b74591d5ab33 391 repo = github.get_repo(upstream_repo, False)
be_bryan 0:b74591d5ab33 392
be_bryan 0:b74591d5ab33 393 except:
be_bryan 0:b74591d5ab33 394 userlog.error("Upstream repo: %s, does not exist - skipping", upstream_repo)
be_bryan 0:b74591d5ab33 395 return False
be_bryan 0:b74591d5ab33 396
be_bryan 0:b74591d5ab33 397 jinja_loader = FileSystemLoader(template)
be_bryan 0:b74591d5ab33 398 jinja_environment = Environment(loader=jinja_loader,
be_bryan 0:b74591d5ab33 399 undefined=StrictUndefined)
be_bryan 0:b74591d5ab33 400 pr_body = jinja_environment.get_template("pr.tmpl").render(tag=tag)
be_bryan 0:b74591d5ab33 401
be_bryan 0:b74591d5ab33 402 # Raise a PR from release-candidate to master
be_bryan 0:b74591d5ab33 403 user_fork = user + ':master'
be_bryan 0:b74591d5ab33 404 try:
be_bryan 0:b74591d5ab33 405 pr = repo.create_pull(title='Updating mbed-os to ' + tag, head=user_fork, base='master', body=pr_body)
be_bryan 0:b74591d5ab33 406 ret = True
be_bryan 0:b74591d5ab33 407 except GithubException as e:
be_bryan 0:b74591d5ab33 408 # Default to False
be_bryan 0:b74591d5ab33 409 userlog.error("Pull request creation failed with error: %s", e)
be_bryan 0:b74591d5ab33 410 else:
be_bryan 0:b74591d5ab33 411 ret = True
be_bryan 0:b74591d5ab33 412 else:
be_bryan 0:b74591d5ab33 413 userlog.error("Git push command failed.")
be_bryan 0:b74591d5ab33 414 else:
be_bryan 0:b74591d5ab33 415 userlog.error("Git commit command failed.")
be_bryan 0:b74591d5ab33 416 else:
be_bryan 0:b74591d5ab33 417 userlog.error("Git clone %s failed", update_repo)
be_bryan 0:b74591d5ab33 418
be_bryan 0:b74591d5ab33 419 os.chdir(cwd)
be_bryan 0:b74591d5ab33 420 return ret
be_bryan 0:b74591d5ab33 421
be_bryan 0:b74591d5ab33 422 def create_work_directory(path):
be_bryan 0:b74591d5ab33 423 """ Create a new directory specified in 'path', overwrite if the directory already
be_bryan 0:b74591d5ab33 424 exists.
be_bryan 0:b74591d5ab33 425
be_bryan 0:b74591d5ab33 426 Args:
be_bryan 0:b74591d5ab33 427 path - directory path to be created.
be_bryan 0:b74591d5ab33 428
be_bryan 0:b74591d5ab33 429 """
be_bryan 0:b74591d5ab33 430 if os.path.exists(path):
be_bryan 0:b74591d5ab33 431 userlog.info("'%s' directory already exists. Deleting...", path)
be_bryan 0:b74591d5ab33 432 rmtree_readonly(path)
be_bryan 0:b74591d5ab33 433
be_bryan 0:b74591d5ab33 434 os.makedirs(path)
be_bryan 0:b74591d5ab33 435
be_bryan 0:b74591d5ab33 436 def check_update_status(examples, github, tag):
be_bryan 0:b74591d5ab33 437 """ Check the status of previously raised update pull requests
be_bryan 0:b74591d5ab33 438
be_bryan 0:b74591d5ab33 439 Args:
be_bryan 0:b74591d5ab33 440 examples - list of examples which should have had PRs raised against them.
be_bryan 0:b74591d5ab33 441 github - github rest API instance
be_bryan 0:b74591d5ab33 442 tag - release tag used for the update
be_bryan 0:b74591d5ab33 443
be_bryan 0:b74591d5ab33 444 """
be_bryan 0:b74591d5ab33 445
be_bryan 0:b74591d5ab33 446 for example in examples:
be_bryan 0:b74591d5ab33 447
be_bryan 0:b74591d5ab33 448 repo_name = ''.join(['ARMmbed/', example['name']])
be_bryan 0:b74591d5ab33 449 try:
be_bryan 0:b74591d5ab33 450 repo = github.get_repo(repo_name, False)
be_bryan 0:b74591d5ab33 451
be_bryan 0:b74591d5ab33 452 except Exception as exc:
be_bryan 0:b74591d5ab33 453 text = "Cannot access: " + str(repo_name)
be_bryan 0:b74591d5ab33 454 userlog.error(text)
be_bryan 0:b74591d5ab33 455 userlog.exception(exc)
be_bryan 0:b74591d5ab33 456 sys.exit(1)
be_bryan 0:b74591d5ab33 457
be_bryan 0:b74591d5ab33 458 # Create the full repository filter component
be_bryan 0:b74591d5ab33 459 org_str = ''.join(['repo:ARMmbed/', example['name']])
be_bryan 0:b74591d5ab33 460 filt = ' '.join([org_str, 'is:pr', tag])
be_bryan 0:b74591d5ab33 461 merged = False
be_bryan 0:b74591d5ab33 462
be_bryan 0:b74591d5ab33 463 issues = github.search_issues(query=(filt))
be_bryan 0:b74591d5ab33 464 pr_list = [repo.get_pull(issue.number) for issue in issues]
be_bryan 0:b74591d5ab33 465
be_bryan 0:b74591d5ab33 466 # Should only be one matching PR but just in case, go through paginated list
be_bryan 0:b74591d5ab33 467 for pr in pr_list:
be_bryan 0:b74591d5ab33 468 if pr.merged:
be_bryan 0:b74591d5ab33 469 userlog.info("%s - '%s': MERGED", example['name'], pr.title)
be_bryan 0:b74591d5ab33 470 elif pr.state == 'open':
be_bryan 0:b74591d5ab33 471 userlog.info("%s - '%s': PENDING", example['name'], pr.title)
be_bryan 0:b74591d5ab33 472 elif pr.state == 'closed':
be_bryan 0:b74591d5ab33 473 userlog.info("%s - '%s': CLOSED NOT MERGED", example['name'], pr.title)
be_bryan 0:b74591d5ab33 474 else:
be_bryan 0:b74591d5ab33 475 userlog.error("%s: Cannot find a pull request for %s", example['name'], tag)
be_bryan 0:b74591d5ab33 476
be_bryan 0:b74591d5ab33 477 if __name__ == '__main__':
be_bryan 0:b74591d5ab33 478
be_bryan 0:b74591d5ab33 479 parser = argparse.ArgumentParser(description=__doc__,
be_bryan 0:b74591d5ab33 480 formatter_class=argparse.RawDescriptionHelpFormatter)
be_bryan 0:b74591d5ab33 481 parser.add_argument('-c', '--config_file', help="Path to the configuration file (default is 'examples.json')", default='examples.json')
be_bryan 0:b74591d5ab33 482 parser.add_argument('-T', '--github_token', help="GitHub token for secure access")
be_bryan 0:b74591d5ab33 483
be_bryan 0:b74591d5ab33 484 exclusive = parser.add_mutually_exclusive_group(required=True)
be_bryan 0:b74591d5ab33 485 exclusive.add_argument('-f', '--fork', help="Update a fork", action='store_true')
be_bryan 0:b74591d5ab33 486 exclusive.add_argument('-b', '--branch', help="Update a branch", action='store_true')
be_bryan 0:b74591d5ab33 487 exclusive.add_argument('-s', '--status', help="Show examples update status", action='store_true')
be_bryan 0:b74591d5ab33 488
be_bryan 0:b74591d5ab33 489 args = parser.parse_args()
be_bryan 0:b74591d5ab33 490
be_bryan 0:b74591d5ab33 491 # Load the config file
be_bryan 0:b74591d5ab33 492 with open(os.path.join(os.path.dirname(__file__), args.config_file)) as config:
be_bryan 0:b74591d5ab33 493 if not config:
be_bryan 0:b74591d5ab33 494 userlog.error("Failed to load config file '%s'", args.config_file)
be_bryan 0:b74591d5ab33 495 sys.exit(1)
be_bryan 0:b74591d5ab33 496 json_data = json.load(config)
be_bryan 0:b74591d5ab33 497
be_bryan 0:b74591d5ab33 498
be_bryan 0:b74591d5ab33 499 github = Github(args.github_token)
be_bryan 0:b74591d5ab33 500 config = json_data['update-config']
be_bryan 0:b74591d5ab33 501 tag = config['tag']
be_bryan 0:b74591d5ab33 502
be_bryan 0:b74591d5ab33 503 user = None
be_bryan 0:b74591d5ab33 504 src = "master"
be_bryan 0:b74591d5ab33 505 dst = None
be_bryan 0:b74591d5ab33 506
be_bryan 0:b74591d5ab33 507 if args.status:
be_bryan 0:b74591d5ab33 508
be_bryan 0:b74591d5ab33 509 # This option should only be called after an update has been performed
be_bryan 0:b74591d5ab33 510 check_update_status(json_data['examples'], github, tag)
be_bryan 0:b74591d5ab33 511 exit(0)
be_bryan 0:b74591d5ab33 512
be_bryan 0:b74591d5ab33 513 # Create working directory
be_bryan 0:b74591d5ab33 514 create_work_directory('examples')
be_bryan 0:b74591d5ab33 515
be_bryan 0:b74591d5ab33 516 if args.fork:
be_bryan 0:b74591d5ab33 517 user = config['via-fork']['github-user']
be_bryan 0:b74591d5ab33 518 elif args.branch:
be_bryan 0:b74591d5ab33 519 src = config['via-branch']['src-branch']
be_bryan 0:b74591d5ab33 520 dst = config['via-branch']['dst-branch']
be_bryan 0:b74591d5ab33 521 else:
be_bryan 0:b74591d5ab33 522 userlog.error("Must specify either -f or -b command line option")
be_bryan 0:b74591d5ab33 523 exit(1)
be_bryan 0:b74591d5ab33 524
be_bryan 0:b74591d5ab33 525 # Get the github sha corresponding to the specified mbed-os tag
be_bryan 0:b74591d5ab33 526 cmd = "git rev-list -1 " + tag
be_bryan 0:b74591d5ab33 527 return_code, ref = run_cmd_with_output(cmd)
be_bryan 0:b74591d5ab33 528
be_bryan 0:b74591d5ab33 529 if return_code:
be_bryan 0:b74591d5ab33 530 userlog.error("Could not obtain SHA for tag: %s", tag)
be_bryan 0:b74591d5ab33 531 sys.exit(1)
be_bryan 0:b74591d5ab33 532
be_bryan 0:b74591d5ab33 533 # Loop through the examples
be_bryan 0:b74591d5ab33 534 failures = []
be_bryan 0:b74591d5ab33 535 successes = []
be_bryan 0:b74591d5ab33 536 results = {}
be_bryan 0:b74591d5ab33 537 template = dirname(abspath(__file__))
be_bryan 0:b74591d5ab33 538
be_bryan 0:b74591d5ab33 539 os.chdir('examples')
be_bryan 0:b74591d5ab33 540
be_bryan 0:b74591d5ab33 541 for example in json_data['examples']:
be_bryan 0:b74591d5ab33 542 # Determine if this example should be updated and if so update any found
be_bryan 0:b74591d5ab33 543 # mbed-os.lib files.
be_bryan 0:b74591d5ab33 544
be_bryan 0:b74591d5ab33 545 result = upgrade_example(github, example, tag, ref, user, src, dst, template)
be_bryan 0:b74591d5ab33 546
be_bryan 0:b74591d5ab33 547 if result:
be_bryan 0:b74591d5ab33 548 successes += [example['name']]
be_bryan 0:b74591d5ab33 549 else:
be_bryan 0:b74591d5ab33 550 failures += [example['name']]
be_bryan 0:b74591d5ab33 551
be_bryan 0:b74591d5ab33 552 os.chdir('../')
be_bryan 0:b74591d5ab33 553
be_bryan 0:b74591d5ab33 554 # Finish the script and report the results
be_bryan 0:b74591d5ab33 555 userlog.info("Finished updating examples")
be_bryan 0:b74591d5ab33 556 if successes:
be_bryan 0:b74591d5ab33 557 for success in successes:
be_bryan 0:b74591d5ab33 558 userlog.info(" SUCCEEDED: %s", success)
be_bryan 0:b74591d5ab33 559
be_bryan 0:b74591d5ab33 560 if failures:
be_bryan 0:b74591d5ab33 561 for fail in failures:
be_bryan 0:b74591d5ab33 562 userlog.info(" FAILED: %s", fail)