| # Documentation: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/?view=azure-devops-rest-6.0 |
| |
| import json |
| import os |
| import re |
| import sys |
| import time |
| |
| import requests |
| |
| AZURE_PIPELINE_BASE_URL = "https://aiinfra.visualstudio.com/PyTorch/" |
| AZURE_DEVOPS_PAT_BASE64 = os.environ.get("AZURE_DEVOPS_PAT_BASE64_SECRET", "") |
| PIPELINE_ID = "911" |
| PROJECT_ID = "0628bce4-2d33-499e-bac5-530e12db160f" |
| TARGET_BRANCH = os.environ.get("CIRCLE_BRANCH", "main") |
| TARGET_COMMIT = os.environ.get("CIRCLE_SHA1", "") |
| |
| build_base_url = AZURE_PIPELINE_BASE_URL + "_apis/build/builds?api-version=6.0" |
| |
| s = requests.Session() |
| s.headers.update({"Authorization": "Basic " + AZURE_DEVOPS_PAT_BASE64}) |
| |
| |
| def submit_build(pipeline_id, project_id, source_branch, source_version): |
| print("Submitting build for branch: " + source_branch) |
| print("Commit SHA1: ", source_version) |
| |
| run_build_raw = s.post( |
| build_base_url, |
| json={ |
| "definition": {"id": pipeline_id}, |
| "project": {"id": project_id}, |
| "sourceBranch": source_branch, |
| "sourceVersion": source_version, |
| }, |
| ) |
| |
| try: |
| run_build_json = run_build_raw.json() |
| except json.decoder.JSONDecodeError as e: |
| print(e) |
| print( |
| "Failed to parse the response. Check if the Azure DevOps PAT is incorrect or expired." |
| ) |
| sys.exit(-1) |
| |
| build_id = run_build_json["id"] |
| |
| print("Submitted bulid: " + str(build_id)) |
| print("Bulid URL: " + run_build_json["url"]) |
| return build_id |
| |
| |
| def get_build(_id): |
| get_build_url = ( |
| AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}?api-version=6.0" |
| ) |
| get_build_raw = s.get(get_build_url) |
| return get_build_raw.json() |
| |
| |
| def get_build_logs(_id): |
| get_build_logs_url = ( |
| AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}/logs?api-version=6.0" |
| ) |
| get_build_logs_raw = s.get(get_build_logs_url) |
| return get_build_logs_raw.json() |
| |
| |
| def get_log_content(url): |
| resp = s.get(url) |
| return resp.text |
| |
| |
| def wait_for_build(_id): |
| build_detail = get_build(_id) |
| build_status = build_detail["status"] |
| |
| while build_status == "notStarted": |
| print("Waiting for run to start: " + str(_id)) |
| sys.stdout.flush() |
| try: |
| build_detail = get_build(_id) |
| build_status = build_detail["status"] |
| except Exception as e: |
| print("Error getting build") |
| print(e) |
| |
| time.sleep(30) |
| |
| print("Bulid started: ", str(_id)) |
| |
| handled_logs = set() |
| while build_status == "inProgress": |
| try: |
| print("Waiting for log: " + str(_id)) |
| logs = get_build_logs(_id) |
| except Exception as e: |
| print("Error fetching logs") |
| print(e) |
| time.sleep(30) |
| continue |
| |
| for log in logs["value"]: |
| log_id = log["id"] |
| if log_id in handled_logs: |
| continue |
| handled_logs.add(log_id) |
| print("Fetching log: \n" + log["url"]) |
| try: |
| log_content = get_log_content(log["url"]) |
| print(log_content) |
| except Exception as e: |
| print("Error getting log content") |
| print(e) |
| sys.stdout.flush() |
| build_detail = get_build(_id) |
| build_status = build_detail["status"] |
| time.sleep(30) |
| |
| build_result = build_detail["result"] |
| |
| print("Bulid status: " + build_status) |
| print("Bulid result: " + build_result) |
| |
| return build_status, build_result |
| |
| |
| if __name__ == "__main__": |
| # Convert the branch name for Azure DevOps |
| match = re.search(r"pull/(\d+)", TARGET_BRANCH) |
| if match is not None: |
| pr_num = match.group(1) |
| SOURCE_BRANCH = f"refs/pull/{pr_num}/head" |
| else: |
| SOURCE_BRANCH = f"refs/heads/{TARGET_BRANCH}" |
| |
| MAX_RETRY = 2 |
| retry = MAX_RETRY |
| |
| while retry > 0: |
| build_id = submit_build(PIPELINE_ID, PROJECT_ID, SOURCE_BRANCH, TARGET_COMMIT) |
| build_status, build_result = wait_for_build(build_id) |
| |
| if build_result != "succeeded": |
| retry = retry - 1 |
| if retry > 0: |
| print("Retrying... remaining attempt: " + str(retry)) |
| # Wait a bit before retrying |
| time.sleep((MAX_RETRY - retry) * 120) |
| continue |
| else: |
| print("No more chance to retry. Giving up.") |
| sys.exit(-1) |
| else: |
| break |