1# Documentation: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/?view=azure-devops-rest-6.0 2 3import json 4import os 5import re 6import sys 7import time 8 9import requests 10 11 12AZURE_PIPELINE_BASE_URL = "https://aiinfra.visualstudio.com/PyTorch/" 13AZURE_DEVOPS_PAT_BASE64 = os.environ.get("AZURE_DEVOPS_PAT_BASE64_SECRET", "") 14PIPELINE_ID = "911" 15PROJECT_ID = "0628bce4-2d33-499e-bac5-530e12db160f" 16TARGET_BRANCH = os.environ.get("CIRCLE_BRANCH", "main") 17TARGET_COMMIT = os.environ.get("CIRCLE_SHA1", "") 18 19build_base_url = AZURE_PIPELINE_BASE_URL + "_apis/build/builds?api-version=6.0" 20 21s = requests.Session() 22s.headers.update({"Authorization": "Basic " + AZURE_DEVOPS_PAT_BASE64}) 23 24 25def submit_build(pipeline_id, project_id, source_branch, source_version): 26 print("Submitting build for branch: " + source_branch) 27 print("Commit SHA1: ", source_version) 28 29 run_build_raw = s.post( 30 build_base_url, 31 json={ 32 "definition": {"id": pipeline_id}, 33 "project": {"id": project_id}, 34 "sourceBranch": source_branch, 35 "sourceVersion": source_version, 36 }, 37 ) 38 39 try: 40 run_build_json = run_build_raw.json() 41 except json.decoder.JSONDecodeError as e: 42 print(e) 43 print( 44 "Failed to parse the response. Check if the Azure DevOps PAT is incorrect or expired." 45 ) 46 sys.exit(-1) 47 48 build_id = run_build_json["id"] 49 50 print("Submitted bulid: " + str(build_id)) 51 print("Bulid URL: " + run_build_json["url"]) 52 return build_id 53 54 55def get_build(_id): 56 get_build_url = ( 57 AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}?api-version=6.0" 58 ) 59 get_build_raw = s.get(get_build_url) 60 return get_build_raw.json() 61 62 63def get_build_logs(_id): 64 get_build_logs_url = ( 65 AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}/logs?api-version=6.0" 66 ) 67 get_build_logs_raw = s.get(get_build_logs_url) 68 return get_build_logs_raw.json() 69 70 71def get_log_content(url): 72 resp = s.get(url) 73 return resp.text 74 75 76def wait_for_build(_id): 77 build_detail = get_build(_id) 78 build_status = build_detail["status"] 79 80 while build_status == "notStarted": 81 print("Waiting for run to start: " + str(_id)) 82 sys.stdout.flush() 83 try: 84 build_detail = get_build(_id) 85 build_status = build_detail["status"] 86 except Exception as e: 87 print("Error getting build") 88 print(e) 89 90 time.sleep(30) 91 92 print("Bulid started: ", str(_id)) 93 94 handled_logs = set() 95 while build_status == "inProgress": 96 try: 97 print("Waiting for log: " + str(_id)) 98 logs = get_build_logs(_id) 99 except Exception as e: 100 print("Error fetching logs") 101 print(e) 102 time.sleep(30) 103 continue 104 105 for log in logs["value"]: 106 log_id = log["id"] 107 if log_id in handled_logs: 108 continue 109 handled_logs.add(log_id) 110 print("Fetching log: \n" + log["url"]) 111 try: 112 log_content = get_log_content(log["url"]) 113 print(log_content) 114 except Exception as e: 115 print("Error getting log content") 116 print(e) 117 sys.stdout.flush() 118 build_detail = get_build(_id) 119 build_status = build_detail["status"] 120 time.sleep(30) 121 122 build_result = build_detail["result"] 123 124 print("Bulid status: " + build_status) 125 print("Bulid result: " + build_result) 126 127 return build_status, build_result 128 129 130if __name__ == "__main__": 131 # Convert the branch name for Azure DevOps 132 match = re.search(r"pull/(\d+)", TARGET_BRANCH) 133 if match is not None: 134 pr_num = match.group(1) 135 SOURCE_BRANCH = f"refs/pull/{pr_num}/head" 136 else: 137 SOURCE_BRANCH = f"refs/heads/{TARGET_BRANCH}" 138 139 MAX_RETRY = 2 140 retry = MAX_RETRY 141 142 while retry > 0: 143 build_id = submit_build(PIPELINE_ID, PROJECT_ID, SOURCE_BRANCH, TARGET_COMMIT) 144 build_status, build_result = wait_for_build(build_id) 145 146 if build_result != "succeeded": 147 retry = retry - 1 148 if retry > 0: 149 print("Retrying... remaining attempt: " + str(retry)) 150 # Wait a bit before retrying 151 time.sleep((MAX_RETRY - retry) * 120) 152 continue 153 else: 154 print("No more chance to retry. Giving up.") 155 sys.exit(-1) 156 else: 157 break 158