From a6b84b5ccc04c35c7c8e4160e8cd833b9c431ccf Mon Sep 17 00:00:00 2001 From: Aleksei Apaseev Date: Tue, 2 Jul 2024 17:38:41 +0800 Subject: [PATCH] feat(ci): add retry job functionality to dynamic pipeline report Introduced changes: - add a manual ci job to retry failed jobs. - refactor js scripts in report template - extract the CI ENV vars related to the report generation script to the predefined constants.py module - introduce a new action "retry_failed_jobs" in helper script "gitlab_api.py" --- .gitlab-ci.yml | 1 + .gitlab/ci/common.yml | 1 + .gitlab/ci/retry_failed_jobs.yml | 14 + tools/ci/dynamic_pipelines/constants.py | 10 + tools/ci/dynamic_pipelines/models.py | 2 +- tools/ci/dynamic_pipelines/report.py | 27 +- .../templates/report.template.html | 214 ++++++++------ .../templates/retry-jobs.png | Bin 0 -> 17552 bytes .../expected_job_report.html | 216 ++++++++------ .../expected_target_test_report.html | 264 ++++++++++-------- .../test_report_generator.py | 2 +- tools/ci/dynamic_pipelines/utils.py | 37 ++- tools/ci/python_packages/gitlab_api.py | 31 +- 13 files changed, 512 insertions(+), 307 deletions(-) create mode 100644 .gitlab/ci/retry_failed_jobs.yml create mode 100644 tools/ci/dynamic_pipelines/templates/retry-jobs.png diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fc0403e0c4..0e449ec7e9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,4 +31,5 @@ include: - '.gitlab/ci/host-test.yml' - '.gitlab/ci/deploy.yml' - '.gitlab/ci/post_deploy.yml' + - '.gitlab/ci/retry_failed_jobs.yml' - '.gitlab/ci/test-win.yml' diff --git a/.gitlab/ci/common.yml b/.gitlab/ci/common.yml index b43e1d3d57..223a1f9dd9 100644 --- a/.gitlab/ci/common.yml +++ b/.gitlab/ci/common.yml @@ -12,6 +12,7 @@ stages: - test_deploy - deploy - post_deploy + - retry_failed_jobs variables: # System environment diff --git a/.gitlab/ci/retry_failed_jobs.yml b/.gitlab/ci/retry_failed_jobs.yml new file mode 100644 index 0000000000..28a2c1e06f --- /dev/null +++ b/.gitlab/ci/retry_failed_jobs.yml @@ -0,0 +1,14 @@ +retry_failed_jobs: + stage: retry_failed_jobs + tags: [shiny, fast_run] + image: $ESP_ENV_IMAGE + dependencies: null + before_script: [] + cache: [] + extends: [] + script: + - echo "Retrieving and retrying all failed jobs for the pipeline..." + - python tools/ci/python_packages/gitlab_api.py retry_failed_jobs $CI_MERGE_REQUEST_PROJECT_ID --pipeline_id $CI_PIPELINE_ID + when: manual + needs: + - generate_failed_jobs_report diff --git a/tools/ci/dynamic_pipelines/constants.py b/tools/ci/dynamic_pipelines/constants.py index 21f903338d..bbeefcfc4d 100644 --- a/tools/ci/dynamic_pipelines/constants.py +++ b/tools/ci/dynamic_pipelines/constants.py @@ -29,8 +29,18 @@ REPORT_TEMPLATE_FILEPATH = os.path.join( IDF_PATH, 'tools', 'ci', 'dynamic_pipelines', 'templates', 'report.template.html' ) +RETRY_JOB_PICTURE_PATH = 'tools/ci/dynamic_pipelines/templates/retry-jobs.png' +RETRY_JOB_TITLE = '\n\nRetry failed jobs with with help of "retry_failed_jobs" stage of the pipeline:' +RETRY_JOB_PICTURE_LINK = '![Retry Jobs Image]({pic_url})' + BUILD_ONLY_LABEL = 'For Maintainers: Only Build Tests' KNOWN_GENERATE_TEST_CHILD_PIPELINE_WARNINGS_FILEPATH = os.path.join( IDF_PATH, 'tools', 'ci', 'dynamic_pipelines', 'templates', 'known_generate_test_child_pipeline_warnings.yml' ) + +CI_JOB_TOKEN = os.getenv('CI_JOB_TOKEN', '') +CI_DASHBOARD_API = os.getenv('CI_DASHBOARD_API', '') +CI_PAGES_URL = os.getenv('CI_PAGES_URL', '') +CI_PROJECT_URL = os.getenv('CI_PROJECT_URL', '') +CI_MERGE_REQUEST_SOURCE_BRANCH_SHA = os.getenv('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '') diff --git a/tools/ci/dynamic_pipelines/models.py b/tools/ci/dynamic_pipelines/models.py index b40d827448..5565917940 100644 --- a/tools/ci/dynamic_pipelines/models.py +++ b/tools/ci/dynamic_pipelines/models.py @@ -164,7 +164,7 @@ class TestCase: 'name': node.attrib['name'], 'file': node.attrib.get('file'), 'time': float(node.attrib.get('time') or 0), - 'ci_job_url': node.attrib.get('ci_job_url') or '', + 'ci_job_url': node.attrib.get('ci_job_url') or 'Not found', 'ci_dashboard_url': f'{grafana_base_url}?{encoded_params}', 'dut_log_url': node.attrib.get('dut_log_url') or 'Not found', } diff --git a/tools/ci/dynamic_pipelines/report.py b/tools/ci/dynamic_pipelines/report.py index be8fce0af3..b36c540978 100644 --- a/tools/ci/dynamic_pipelines/report.py +++ b/tools/ci/dynamic_pipelines/report.py @@ -19,12 +19,16 @@ from prettytable import PrettyTable from .constants import COMMENT_START_MARKER from .constants import REPORT_TEMPLATE_FILEPATH +from .constants import RETRY_JOB_PICTURE_LINK +from .constants import RETRY_JOB_PICTURE_PATH +from .constants import RETRY_JOB_TITLE from .constants import TEST_RELATED_APPS_DOWNLOAD_URLS_FILENAME from .models import GitlabJob from .models import TestCase from .utils import fetch_failed_testcases_failure_ratio from .utils import format_permalink -from .utils import get_report_url +from .utils import get_artifacts_url +from .utils import get_repository_file_url from .utils import is_url from .utils import load_known_failure_cases @@ -69,13 +73,14 @@ class ReportGenerator: # for example, {URL}/-/esp-idf/-/jobs/{id}/artifacts/list_job_84.txt # CI_PAGES_URL is {URL}/esp-idf, which missed one `-` - report_url: str = get_report_url(job_id, output_filepath) + report_url: str = get_artifacts_url(job_id, output_filepath) return report_url def generate_html_report(self, table_str: str) -> str: # we're using bootstrap table table_str = table_str.replace( - '', '
' + '
', + '
', ) with open(REPORT_TEMPLATE_FILEPATH) as fr: template = fr.read() @@ -245,20 +250,23 @@ class ReportGenerator: if self.mr is None: print('No MR found, skip posting comment') return - + retry_job_picture_comment = (f'{RETRY_JOB_TITLE}\n\n' + f'{RETRY_JOB_PICTURE_LINK}').format(pic_url=get_repository_file_url(RETRY_JOB_PICTURE_PATH)) + del_retry_job_pic_pattern = re.escape(RETRY_JOB_TITLE) + r'.*?' + re.escape(f'{RETRY_JOB_PICTURE_PATH})') for note in self.mr.notes.list(iterator=True): if note.body.startswith(COMMENT_START_MARKER): updated_str = re.sub(self.REGEX_PATTERN.format(self.title), comment, note.body) if updated_str == note.body: # not updated updated_str = f'{note.body.strip()}\n\n{comment}' - note.body = updated_str + updated_str = re.sub(del_retry_job_pic_pattern, '', updated_str, flags=re.DOTALL) + note.body = updated_str + retry_job_picture_comment note.save() break else: new_comment = f"""{COMMENT_START_MARKER} -{comment}""" +{comment}{retry_job_picture_comment}""" self.mr.notes.create({'body': new_comment}) @@ -526,7 +534,7 @@ class TargetTestReportGenerator(ReportGenerator): 'Test Case', 'Test Script File Path', 'Failure Reason', - 'Failures across all other branches (40 latest testcases)', + 'Cases that failed in other branches as well (40 latest testcases)', 'Dut Log URL', 'Job URL', 'Grafana URL', @@ -534,7 +542,7 @@ class TargetTestReportGenerator(ReportGenerator): row_attrs=['name', 'file', 'failure', 'dut_log_url', 'ci_job_url', 'ci_dashboard_url'], value_functions=[ ( - 'Failures across all other branches (40 latest testcases)', + 'Cases that failed in other branches as well (40 latest testcases)', lambda item: f"{getattr(item, 'latest_failed_count', '')} / {getattr(item, 'latest_total_count', '')}", ) ], @@ -696,11 +704,10 @@ class JobReportGenerator(ReportGenerator): ) ], ) - relevant_failed_jobs_report_url = get_report_url(self.job_id, self.failed_jobs_report_file) + relevant_failed_jobs_report_url = get_artifacts_url(self.job_id, self.failed_jobs_report_file) self.additional_info += self.generate_additional_info_section( self.report_titles_map['failed_jobs'], len(relevant_failed_jobs), relevant_failed_jobs_report_url ) report_str = self.generate_html_report(''.join(report_sections)) - return report_str diff --git a/tools/ci/dynamic_pipelines/templates/report.template.html b/tools/ci/dynamic_pipelines/templates/report.template.html index bb35367f94..cefa904b46 100644 --- a/tools/ci/dynamic_pipelines/templates/report.template.html +++ b/tools/ci/dynamic_pipelines/templates/report.template.html @@ -1,88 +1,132 @@ - + - - - {{title}} - - - - - - - -
{{table}}
- - - - - - - - + th:nth-child(1), + td:nth-child(1) { + width: 5%; + } + th:nth-child(2), + td:nth-child(2), + th:nth-child(3), + td:nth-child(3) { + width: 30%; + } + th, + td { + overflow: hidden; + text-overflow: ellipsis; + } + h2 { + margin-top: 10px; + } + .copy-link-icon { + font-size: 20px; + margin-left: 10px; + color: #8f8f97; + cursor: pointer; + } + .copy-link-icon:hover { + color: #282b2c; + } + + + +
{{table}}
+ + + + + + + diff --git a/tools/ci/dynamic_pipelines/templates/retry-jobs.png b/tools/ci/dynamic_pipelines/templates/retry-jobs.png new file mode 100644 index 0000000000000000000000000000000000000000..a8a60112c01546ea8c8ff06fc5b8d30f83ed4bcd GIT binary patch literal 17552 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 literal 0 HcmV?d00001 diff --git a/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_job_report.html b/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_job_report.html index 3c75ce390f..3c35a534fd 100644 --- a/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_job_report.html +++ b/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_job_report.html @@ -1,38 +1,61 @@ - + - - - Job Report - - - - - - - -

Failed Jobs (Excludes "integration_test" and "target_test" jobs)

+ + + Job Report + + + + + + + +

Failed Jobs (Excludes "integration_test" and "target_test" jobs)

@@ -70,57 +93,78 @@
Job Name
- - - - - + + + + + - - - + + function scrollToHashLocation() { + const hash = window.location.hash; + if (hash) { + setTimeout(() => { + $("html, body").animate( + { scrollTop: $(hash).offset().top }, + 100 + ); + }, 100); + } + } + + function copyPermalink(anchorId) { + const fullUrl = `${window.location.origin}${window.location.pathname}${anchorId}`; + history.pushState(null, null, anchorId); + navigator.clipboard.writeText(fullUrl); + scrollToHashLocation(); + } + + function toggleText(e) { + e.preventDefault(); + const link = $(this), + textSpan = link.siblings(".full-text"), + toggleSpan = link.siblings(".text-toggle"); + const visible = textSpan.is(":visible"); + link.text(visible ? "Show More" : "Show Less"); + textSpan.toggle(); + toggleSpan.toggle(); + } + + function setupTextToggles() { + $("table.table td").each(function () { + var cell = $(this); + if (cell.text().length > 100) { + var originalText = cell.text(); + var displayText = + originalText.substring(0, 100) + "..."; + cell.html( + `${displayText}Show More` + ); + } + }); + } + + diff --git a/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_target_test_report.html b/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_target_test_report.html index c2d0b95c97..dc63e26152 100644 --- a/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_target_test_report.html +++ b/tools/ci/dynamic_pipelines/tests/test_report_generator/reports_sample_data/expected_target_test_report.html @@ -1,44 +1,67 @@ - + - - - Test Report - - - - - - - -

Failed Test Cases on Other branches (Excludes Known Failure Cases)

+ + + Test Report + + + + + + + +

Failed Test Cases on Other branches (Excludes Known Failure Cases)

- + @@ -51,7 +74,7 @@ - + @@ -60,7 +83,7 @@ - + @@ -69,7 +92,7 @@ - + @@ -78,7 +101,7 @@ - + @@ -87,7 +110,7 @@ - + @@ -96,7 +119,7 @@ - + @@ -105,12 +128,12 @@ - +
Test Case Test Script File Path Failure ReasonFailures across all other branches (40 latest testcases)Cases that failed in other branches as well (40 latest testcases) Dut Log URL Job URL Grafana URLfailed on setup with "EOFError" 0 / 40 linkNot found link
pexpect.exceptions.TIMEOUT: Not found "Press ENTER to see the list of tests" Bytes in current buffer (color code eliminated): ce710,len:0x2afc entry 0x403cc710 Please check the full log here: /builds/espressif/esp-idf/pytest_embedded/2024-05-17_17-50-04/esp32c3.release.test_esp_timer/dut.txt 0 / 40 linkNot found link
pexpect.exceptions.TIMEOUT: Not found "Press ENTER to see the list of tests" Bytes in current buffer (color code eliminated): 0 d4 000 00x0000 x0000x00 000000 0 Please check the full log here: /builds/espressif/esp-idf/pytest_embedded/2024-05-17_17-50-04/esp32c3.default.test_wpa_supplicant_ut/dut.txt 0 / 40 linkNot found link
failed on setup with "EOFError" 3 / 40 linkNot found link
AssertionError: Unity test failed 3 / 40 linkNot found link
pexpect.exceptions.TIMEOUT: Not found "re.compile(b'^[-]+\\s*(\\d+) Tests (\\d+) Failures (\\d+) Ignored\\s*(?POK|FAIL)', re.MULTILINE)" Bytes in current buffer (color code eliminated): Serial port /dev/ttyUSB16 Connecting.... Connecting.... esptool.py v4.7.0 Found 1 serial ports Chip is ESP32-C3 (QFN32) (revision v0.3) Features: WiFi, BLE, Embedded Flash 4MB... (total 6673 bytes) Please check the full log here: /builds/espressif/esp-idf/pytest_embedded/2024-05-17_17-50-04/esp32c3.512safe.test_wear_levelling/dut.txt 3 / 40 linkNot found link
pexpect.exceptions.TIMEOUT: Not found "re.compile(b'^[-]+\\s*(\\d+) Tests (\\d+) Failures (\\d+) Ignored\\s*(?POK|FAIL)', re.MULTILINE)" Bytes in current buffer (color code eliminated): Serial port /dev/ttyUSB16 Connecting.... Connecting.... esptool.py v4.7.0 Found 1 serial ports Chip is ESP32-C3 (QFN32) (revision v0.3) Features: WiFi, BLE, Embedded Flash 4MB... (total 24528 bytes) Please check the full log here: /builds/espressif/esp-idf/pytest_embedded/2024-05-17_17-50-04/esp32c3.release.test_wear_levelling/dut.txt 3 / 40 linkNot found link

Known Failure Cases

+ onclick="copyPermalink('#known-failure-cases')">
@@ -125,33 +148,33 @@ - + - + - + - +
Test Caseesp32c2.default.test_wpa_supplicant_ut components/wpa_supplicant/test_apps/pytest_wpa_supplicant_ut.py AssertionError: Unity test failedNot found link
esp32c3.release.test_esp_timer components/esp_timer/test_apps/pytest_esp_timer_ut.py pexpect.exceptions.TIMEOUT: Not found "Press ENTER to see the list of tests" Bytes in current buffer (color code eliminated): ce710,len:0x2afc entry 0x403cc710 Please check the full log here: /builds/espressif/esp-idf/pytest_embedded/2024-05-17_17-50-04/esp32c3.release.test_esp_timer/dut.txtNot found link
esp32c3.512safe.test_wear_levelling components/wear_levelling/test_apps/pytest_wear_levelling.py pexpect.exceptions.TIMEOUT: Not found "re.compile(b'^[-]+\\s*(\\d+) Tests (\\d+) Failures (\\d+) Ignored\\s*(?POK|FAIL)', re.MULTILINE)" Bytes in current buffer (color code eliminated): Serial port /dev/ttyUSB16 Connecting.... Connecting.... esptool.py v4.7.0 Found 1 serial ports Chip is ESP32-C3 (QFN32) (revision v0.3) Features: WiFi, BLE, Embedded Flash 4MB... (total 6673 bytes) Please check the full log here: /builds/espressif/esp-idf/pytest_embedded/2024-05-17_17-50-04/esp32c3.512safe.test_wear_levelling/dut.txtNot found link
esp32c3.default.test_wpa_supplicant_ut components/wpa_supplicant/test_apps/pytest_wpa_supplicant_ut.py pexpect.exceptions.TIMEOUT: Not found "Press ENTER to see the list of tests" Bytes in current buffer (color code eliminated): 0 d4 000 00x0000 x0000x00 000000 0 Please check the full log here: /builds/espressif/esp-idf/pytest_embedded/2024-05-17_17-50-04/esp32c3.default.test_wpa_supplicant_ut/dut.txtNot found link

Skipped Test Cases

+ onclick="copyPermalink('#skipped-test-cases')">
@@ -169,7 +192,7 @@
Test Case

Succeeded Test Cases

+ onclick="copyPermalink('#succeeded-test-cases')">
@@ -182,110 +205,131 @@ - + - + - + - + - + - + - + - + - +
Test Case
esp32c2.default.test_vfs_default components/vfs/test_apps/pytest_vfs.pyNot found link
esp32c2.iram.test_vfs_default components/vfs/test_apps/pytest_vfs.pyNot found link
test_python_interpreter_unix test_common.pyNot found link
test_invoke_confserver test_common.pyNot found link
test_ccache_used_to_build test_common.pyNot found link
test_toolchain_prefix_in_description_file test_common.pyNot found link
test_subcommands_with_options test_common.pyNot found link
test_fallback_to_build_system_target test_common.pyNot found link
test_create_component_project test_common.pyNot found link
- - - - - + + + + + - - - + + function scrollToHashLocation() { + const hash = window.location.hash; + if (hash) { + setTimeout(() => { + $("html, body").animate( + { scrollTop: $(hash).offset().top }, + 100 + ); + }, 100); + } + } + + function copyPermalink(anchorId) { + const fullUrl = `${window.location.origin}${window.location.pathname}${anchorId}`; + history.pushState(null, null, anchorId); + navigator.clipboard.writeText(fullUrl); + scrollToHashLocation(); + } + + function toggleText(e) { + e.preventDefault(); + const link = $(this), + textSpan = link.siblings(".full-text"), + toggleSpan = link.siblings(".text-toggle"); + const visible = textSpan.is(":visible"); + link.text(visible ? "Show More" : "Show Less"); + textSpan.toggle(); + toggleSpan.toggle(); + } + + function setupTextToggles() { + $("table.table td").each(function () { + var cell = $(this); + if (cell.text().length > 100) { + var originalText = cell.text(); + var displayText = + originalText.substring(0, 100) + "..."; + cell.html( + `${displayText}Show More` + ); + } + }); + } + + diff --git a/tools/ci/dynamic_pipelines/tests/test_report_generator/test_report_generator.py b/tools/ci/dynamic_pipelines/tests/test_report_generator/test_report_generator.py index abdd8a7b64..81bae786ce 100644 --- a/tools/ci/dynamic_pipelines/tests/test_report_generator/test_report_generator.py +++ b/tools/ci/dynamic_pipelines/tests/test_report_generator/test_report_generator.py @@ -44,8 +44,8 @@ class TestReportGeneration(unittest.TestCase): self.mock_project.mergerequests.get.return_value = self.mock_mr self.addCleanup(self.gitlab_patcher.stop) - self.addCleanup(self.env_patcher.stop) self.addCleanup(self.failure_rate_patcher.stop) + self.addCleanup(self.env_patcher.stop) self.addCleanup(self.cleanup_files) def cleanup_files(self) -> None: diff --git a/tools/ci/dynamic_pipelines/utils.py b/tools/ci/dynamic_pipelines/utils.py index 79cd3f44d7..08eceb1f9a 100644 --- a/tools/ci/dynamic_pipelines/utils.py +++ b/tools/ci/dynamic_pipelines/utils.py @@ -10,6 +10,11 @@ from urllib.parse import urlparse import requests import yaml +from .constants import CI_DASHBOARD_API +from .constants import CI_JOB_TOKEN +from .constants import CI_MERGE_REQUEST_SOURCE_BRANCH_SHA +from .constants import CI_PAGES_URL +from .constants import CI_PROJECT_URL from .models import GitlabJob from .models import Job from .models import TestCase @@ -95,11 +100,9 @@ def fetch_failed_jobs(commit_id: str) -> t.List[GitlabJob]: :param commit_id: The commit ID for which to fetch jobs. :return: A list of jobs if the request is successful, otherwise an empty list. """ - token = os.getenv('ESPCI_TOKEN', '') - ci_dash_api_backend_host = os.getenv('CI_DASHBOARD_API', '') response = requests.get( - f'{ci_dash_api_backend_host}/commits/{commit_id}/jobs', - headers={'Authorization': f'Bearer {token}'} + f'{CI_DASHBOARD_API}/commits/{commit_id}/jobs', + headers={'Authorization': f'Bearer {CI_JOB_TOKEN}'} ) if response.status_code != 200: print(f'Failed to fetch jobs data: {response.status_code} with error: {response.text}') @@ -113,8 +116,8 @@ def fetch_failed_jobs(commit_id: str) -> t.List[GitlabJob]: failed_job_names = [job['name'] for job in jobs if job['status'] == 'failed'] response = requests.post( - f'{ci_dash_api_backend_host}/jobs/failure_ratio', - headers={'Authorization': f'Bearer {token}'}, + f'{CI_DASHBOARD_API}/jobs/failure_ratio', + headers={'Authorization': f'Bearer {CI_JOB_TOKEN}'}, json={'job_names': failed_job_names, 'exclude_branches': [os.getenv('CI_MERGE_REQUEST_SOURCE_BRANCH_NAME', '')]}, ) if response.status_code != 200: @@ -139,12 +142,10 @@ def fetch_failed_testcases_failure_ratio(failed_testcases: t.List[TestCase], bra :param branches_filter: The filter to filter testcases by branch names. :return: A list of testcases with enriched with failure rates data. """ - token = os.getenv('ESPCI_TOKEN', '') - ci_dash_api_backend_host = os.getenv('CI_DASHBOARD_API', '') req_json = {'testcase_names': list(set([testcase.name for testcase in failed_testcases])), **branches_filter} response = requests.post( - f'{ci_dash_api_backend_host}/testcases/failure_ratio', - headers={'Authorization': f'Bearer {token}'}, + f'{CI_DASHBOARD_API}/testcases/failure_ratio', + headers={'Authorization': f'Bearer {CI_JOB_TOKEN}'}, json=req_json, ) if response.status_code != 200: @@ -191,13 +192,23 @@ def format_permalink(s: str) -> str: return formatted_string -def get_report_url(job_id: int, output_filepath: str) -> str: +def get_artifacts_url(job_id: int, output_filepath: str) -> str: """ - Generates the url of the path where the report will be stored in the job's artifacts . + Generates the url of the path where the artifact will be stored in the job's artifacts . :param job_id: The job identifier used to construct the URL. :param output_filepath: The path to the output file. :return: The modified URL pointing to the job's artifacts. """ - url = os.getenv('CI_PAGES_URL', '').replace('esp-idf', '-/esp-idf') + url = CI_PAGES_URL.replace('esp-idf', '-/esp-idf') return f'{url}/-/jobs/{job_id}/artifacts/{output_filepath}' + + +def get_repository_file_url(file_path: str) -> str: + """ + Generates the url of the file path inside the repository. + + :param file_path: The file path where the file is stored. + :return: The modified URL pointing to the file's path in the repository. + """ + return f'{CI_PROJECT_URL}/-/raw/{CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}/{file_path}' diff --git a/tools/ci/python_packages/gitlab_api.py b/tools/ci/python_packages/gitlab_api.py index 1bffbef372..7c83d6f205 100644 --- a/tools/ci/python_packages/gitlab_api.py +++ b/tools/ci/python_packages/gitlab_api.py @@ -254,7 +254,7 @@ class Gitlab(object): @staticmethod def decompress_archive(path: str, destination: str) -> str: full_destination = os.path.abspath(destination) - # By default max path lenght is set to 260 characters + # By default max path length is set to 260 characters # Prefix `\\?\` extends it to 32,767 characters if sys.platform == 'win32': full_destination = '\\\\?\\' + full_destination @@ -279,6 +279,29 @@ class Gitlab(object): job = self.project.jobs.get(job_id) return ','.join(job.tag_list) + def retry_failed_jobs(self, pipeline_id: int, retry_allowed_failures: bool = False) -> List[int]: + """ + Retry failed jobs for a specific pipeline. Optionally include jobs marked as 'allowed failures'. + + :param pipeline_id: ID of the pipeline whose failed jobs are to be retried. + :param retry_allowed_failures: Whether to retry jobs that are marked as allowed failures. + """ + pipeline = self.project.pipelines.get(pipeline_id) + jobs_to_retry = [ + job + for job in pipeline.jobs.list(scope='failed') + if retry_allowed_failures or not job.attributes.get('allow_failure', False) + ] + jobs_succeeded_retry = [] + for job in jobs_to_retry: + try: + res = self.project.jobs.get(job.id).retry() + jobs_succeeded_retry.append(job.id) + logging.info(f'Retried job {job.id} with result {res}') + except Exception as e: + logging.error(f'Failed to retry job {job.id}: {str(e)}') + return jobs_succeeded_retry + def main() -> None: parser = argparse.ArgumentParser() @@ -291,6 +314,9 @@ def main() -> None: parser.add_argument('--project_name', '-m', default=None) parser.add_argument('--destination', '-d', default=None) parser.add_argument('--artifact_path', '-a', nargs='*', default=None) + parser.add_argument( + '--retry-allowed-failures', action='store_true', help='Flag to retry jobs marked as allowed failures' + ) args = parser.parse_args() gitlab_inst = Gitlab(args.project_id) @@ -306,6 +332,9 @@ def main() -> None: elif args.action == 'get_project_id': ret = gitlab_inst.get_project_id(args.project_name) print('project id: {}'.format(ret)) + elif args.action == 'retry_failed_jobs': + res = gitlab_inst.retry_failed_jobs(args.pipeline_id, args.retry_allowed_failures) + print('job retried successfully: {}'.format(res)) elif args.action == 'get_job_tags': ret = gitlab_inst.get_job_tags(args.job_id) print(ret)