1#!/usr/bin/env python 2 3# internal use only 4# called by CI jobs when it uses a project related to IDF 5 6import argparse 7import json 8import os 9import re 10import subprocess 11 12IDF_GIT_DESCRIBE_PATTERN = re.compile(r'^v(\d)\.(\d)') 13RETRY_COUNT = 3 14 15 16def get_customized_project_revision(proj_name): 17 """ 18 get customized project revision defined in bot message 19 """ 20 revision = '' 21 customized_project_revisions = os.getenv('BOT_CUSTOMIZED_REVISION') 22 if customized_project_revisions: 23 customized_project_revisions = json.loads(customized_project_revisions) 24 try: 25 revision = customized_project_revisions[proj_name.lower()] 26 except (KeyError, TypeError): 27 pass 28 return revision 29 30 31def target_branch_candidates(proj_name): 32 """ 33 :return: a list of target branch candidates, from highest priority to lowest priority. 34 """ 35 candidates = [ 36 # branch name (or tag name) of current IDF 37 os.getenv('CI_COMMIT_REF_NAME'), 38 # CI_MERGE_REQUEST_TARGET_BRANCH_NAME 39 os.getenv('CI_MERGE_REQUEST_TARGET_BRANCH_NAME'), 40 ] 41 customized_candidate = get_customized_project_revision(proj_name) 42 if customized_candidate: 43 # highest priority, insert to head of list 44 candidates.insert(0, customized_candidate) 45 46 # branch name read from IDF 47 try: 48 git_describe = subprocess.check_output(['git', 'describe', 'HEAD']) 49 match = IDF_GIT_DESCRIBE_PATTERN.search(git_describe.decode()) 50 if match: 51 major_revision = match.group(1) 52 minor_revision = match.group(2) 53 # release branch 54 candidates.append('release/v{}.{}'.format(major_revision, minor_revision)) 55 # branch to match all major branches, like v3.x or v3 56 candidates.append('release/v{}.x'.format(major_revision)) 57 candidates.append('release/v{}'.format(major_revision)) 58 except subprocess.CalledProcessError: 59 # this should not happen as IDF should have describe message 60 pass 61 62 return [c for c in candidates if c] # filter out null value 63 64 65if __name__ == '__main__': 66 parser = argparse.ArgumentParser() 67 parser.add_argument('project', 68 help='the name of project') 69 parser.add_argument('project_relative_path', 70 help='relative path of project to IDF repository directory') 71 parser.add_argument('--customized_only', action='store_true', 72 help='Only to find customized revision') 73 74 args = parser.parse_args() 75 76 if args.customized_only: 77 customized_revision = get_customized_project_revision(args.project) 78 candidate_branches = [customized_revision] if customized_revision else [] 79 else: 80 candidate_branches = target_branch_candidates(args.project) 81 82 # change to project dir for checkout 83 os.chdir(args.project_relative_path) 84 85 ref_to_use = '' 86 for candidate in candidate_branches: 87 # check if the branch, tag or commit exists 88 try: 89 subprocess.check_call(['git', 'cat-file', '-t', 'origin/{}'.format(candidate)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 90 ref_to_use = candidate 91 break 92 except subprocess.CalledProcessError: 93 try: 94 # For customized commits 95 subprocess.check_call(['git', 'cat-file', '-t', candidate], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 96 ref_to_use = candidate 97 break 98 except subprocess.CalledProcessError: 99 pass 100 continue 101 102 if ref_to_use: 103 for _ in range(RETRY_COUNT): 104 # Add retry for projects with git-lfs 105 try: 106 subprocess.check_call(['git', 'checkout', '-f', ref_to_use], stdout=subprocess.PIPE) # not print the stdout 107 print('CI using ref {} for project {}'.format(ref_to_use, args.project)) 108 break 109 except subprocess.CalledProcessError: 110 pass 111 else: 112 print('Failed to use ref {} for project {}'.format(ref_to_use, args.project)) 113 exit(1) 114 else: 115 print('using default branch') 116