|
| 1 | +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD |
| 2 | + |
| 3 | +# SPDX-License-Identifier: CC0-1.0 |
| 4 | + |
| 5 | +import os |
| 6 | +import subprocess |
| 7 | +import requests |
| 8 | +import glob |
| 9 | +import argparse |
| 10 | +import logging |
| 11 | + |
| 12 | +# Gitlab Configurations |
| 13 | +gitlab_api_url = os.getenv("CI_API_V4_URL") |
| 14 | +gitlab_token = os.getenv("GITLAB_MR_COMMENT_TOKEN") |
| 15 | +ci_project_id = os.getenv("CI_PROJECT_ID") |
| 16 | +ci_merge_request_iid = os.getenv("CI_MERGE_REQUEST_IID") |
| 17 | + |
| 18 | +# Fetch the id of the pipeline for a branch with the specified commit id (default main branch) |
| 19 | +def fetch_pipeline_for_commit(commit_sha, branch_name="main"): |
| 20 | + url = f"{gitlab_api_url}/projects/{ci_project_id}/pipelines" |
| 21 | + headers = {"PRIVATE-TOKEN": gitlab_token} |
| 22 | + params = {"ref": branch_name, "sha": commit_sha} |
| 23 | + response = requests.get(url, headers=headers, params=params) |
| 24 | + response.raise_for_status() |
| 25 | + pipelines = response.json() |
| 26 | + if not pipelines: |
| 27 | + raise ValueError(f"No pipeline found for commit: {commit_sha} on branch: {branch_name}") |
| 28 | + return pipelines[0]['id'] |
| 29 | + |
| 30 | +# Fetch the versions for the gitlab MR. |
| 31 | +def fetch_merge_request_diff_versions(): |
| 32 | + url = f"{gitlab_api_url}/projects/{ci_project_id}/merge_requests/{ci_merge_request_iid}/versions" |
| 33 | + headers = {"PRIVATE-TOKEN": gitlab_token} |
| 34 | + response = requests.get(url, headers=headers) |
| 35 | + response.raise_for_status() |
| 36 | + return response.json() |
| 37 | + |
| 38 | +# Fetch the jobs specific to a pipeline id. |
| 39 | +def fetch_pipeline_jobs(pipeline_id): |
| 40 | + url = f"{gitlab_api_url}/projects/{ci_project_id}/pipelines/{pipeline_id}/jobs" |
| 41 | + headers = {"PRIVATE-TOKEN": gitlab_token} |
| 42 | + response = requests.get(url, headers=headers) |
| 43 | + response.raise_for_status() |
| 44 | + return response.json() |
| 45 | + |
| 46 | +# Download the reference map file for the MR base commit. |
| 47 | +def download_ref_map_file(chip_name, job_id, output_file): |
| 48 | + ref_artifact_path = f"examples/light/build_{chip_name}_default/light.map" |
| 49 | + url = f"{gitlab_api_url}/projects/{ci_project_id}/jobs/{job_id}/artifacts/{ref_artifact_path}" |
| 50 | + headers = {"PRIVATE-TOKEN": gitlab_token} |
| 51 | + with requests.get(url, headers=headers, stream=True) as response: |
| 52 | + response.raise_for_status() |
| 53 | + with open(output_file, 'wb') as f: |
| 54 | + for chunk in response.iter_content(chunk_size=8192): |
| 55 | + f.write(chunk) |
| 56 | + |
| 57 | +# Locate the map file artifact for the current pipeline. |
| 58 | +def locate_current_map_file(example): |
| 59 | + pattern = f"examples/{example}/build*/{example}.map" |
| 60 | + artifact_file_paths = glob.glob(pattern, recursive=True) |
| 61 | + if not artifact_file_paths: |
| 62 | + raise FileNotFoundError("No map file found!") |
| 63 | + return artifact_file_paths[0] |
| 64 | + |
| 65 | +# Execute esp_idf_size diff command to find increase/decrease in firmware size. |
| 66 | +def execute_idf_size_command(old_file_path, new_file_path): |
| 67 | + try: |
| 68 | + result = subprocess.run( |
| 69 | + ["python", "-m", "esp_idf_size", "--diff", old_file_path, new_file_path], |
| 70 | + capture_output=True, |
| 71 | + text=True, |
| 72 | + check=True, |
| 73 | + ) |
| 74 | + return result.stdout |
| 75 | + except subprocess.CalledProcessError as e: |
| 76 | + raise |
| 77 | + |
| 78 | +# Post the results to gitlab MR. |
| 79 | +def post_results_to_gitlab_mr(output, chip_name, example): |
| 80 | + if not all([gitlab_api_url, gitlab_token, ci_project_id, ci_merge_request_iid]): |
| 81 | + print("Missing required environment variables. Results not posted.") |
| 82 | + return |
| 83 | + |
| 84 | + markdown_output = f"<details open><summary><b>Static Memory Footprint for target: {chip_name}, example: {example}</b></summary>\n\n```\n{output}\n```\n</details>" |
| 85 | + url = f"{gitlab_api_url}/projects/{ci_project_id}/merge_requests/{ci_merge_request_iid}/notes" |
| 86 | + headers = {"PRIVATE-TOKEN": gitlab_token} |
| 87 | + data = {"body": markdown_output} |
| 88 | + response = requests.post(url, headers=headers, json=data) |
| 89 | + if response.status_code == 201: |
| 90 | + print("Successfully posted results to GitLab MR.") |
| 91 | + else: |
| 92 | + print("Failed to post results to GitLab MR.") |
| 93 | + |
| 94 | +def main(): |
| 95 | + |
| 96 | + logging.basicConfig(level=logging.WARNING, format="%(asctime)s - %(levelname)s - %(message)s") |
| 97 | + parser = argparse.ArgumentParser(description="Process build results and post to GitLab.") |
| 98 | + parser.add_argument("--chip", required=True, help="Specify the chip name (e.g., C2, H2)") |
| 99 | + parser.add_argument("--ref_map_file", required=True, help="Specify the reference main branch map file") |
| 100 | + parser.add_argument("--job_name", required=True, help = "Specify the job name for the job id search") |
| 101 | + parser.add_argument("--example", required=True, help = "Specify the example name for the memory footprint") |
| 102 | + args = parser.parse_args() |
| 103 | + |
| 104 | + try: |
| 105 | + diff_versions = fetch_merge_request_diff_versions() |
| 106 | + base_version = diff_versions[0] |
| 107 | + base_commit_sha = base_version["base_commit_sha"] |
| 108 | + |
| 109 | + base_commit_pipeline_id = fetch_pipeline_for_commit(base_commit_sha, branch_name="main") |
| 110 | + jobs = fetch_pipeline_jobs(base_commit_pipeline_id) |
| 111 | + |
| 112 | + target_job_id = next((job["id"] for job in jobs if job["name"] == args.job_name), None) |
| 113 | + if not target_job_id: |
| 114 | + raise ValueError("Target job not found.") |
| 115 | + |
| 116 | + download_ref_map_file(args.chip, target_job_id, args.ref_map_file) |
| 117 | + current_map_file = locate_current_map_file(args.example) |
| 118 | + |
| 119 | + size_diff_output = execute_idf_size_command(args.ref_map_file, current_map_file) |
| 120 | + post_results_to_gitlab_mr(size_diff_output, args.chip, args.example) |
| 121 | + except FileNotFoundError as e: |
| 122 | + logging.error(f"Error occurred while posting results to GitLab MR: File not found {e}") |
| 123 | + except Exception as e: |
| 124 | + logging.error(f"Error occurred while posting results to GitLab MR: An Unexpected error occurred:{e}") |
| 125 | + |
| 126 | +if __name__ == "__main__": |
| 127 | + main() |
| 128 | + |
| 129 | + |
0 commit comments