mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'ci/use-windows-vm' into 'master'
ci: windows tests with VM See merge request espressif/esp-idf!47238
This commit is contained in:
@@ -281,6 +281,31 @@ variables:
|
|||||||
after_script: [] # ccache now is disabled for macos brew runners
|
after_script: [] # ccache now is disabled for macos brew runners
|
||||||
timeout: 30m
|
timeout: 30m
|
||||||
|
|
||||||
|
.brew-windows-settings:
|
||||||
|
image: windows_11_idf_v6_1
|
||||||
|
tags: [windows-vm, brew]
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: fetch
|
||||||
|
LC_ALL: C.UTF-8
|
||||||
|
# git clean at the final cleanup stage stuck on Windows VM
|
||||||
|
# a new VM for each job anyway, so we can skip cleaning the workspace
|
||||||
|
GIT_CLEAN_FLAGS: none
|
||||||
|
# IDF_CCACHE_ENABLE: "1"
|
||||||
|
# CCACHE_DIR: "C:/cache/idf_ccache"
|
||||||
|
before_script:
|
||||||
|
- if ($env:IDF_DONT_USE_MIRRORS) { $env:IDF_MIRROR_PREFIX_MAP = '' }
|
||||||
|
- ./install.ps1 --enable-ci
|
||||||
|
- ./export.ps1
|
||||||
|
- $env:PYTHONPATH = "$env:PYTHONPATH;$env:IDF_PATH\tools;$env:IDF_PATH\tools\esp_app_trace;$env:IDF_PATH\components\partition_table;$env:IDF_PATH\tools\ci\python_packages"
|
||||||
|
- python "${SUBMODULE_FETCH_TOOL}" -s "${SUBMODULES_TO_FETCH}"
|
||||||
|
# The time in VM may not be synced, since it's built a long time ago, so we need to sync time
|
||||||
|
- net start w32time
|
||||||
|
- w32tm /config /manualpeerlist:"time.google.com,0x9 pool.ntp.org,0x9" /syncfromflags:manual /reliable:yes /update
|
||||||
|
- net stop w32time
|
||||||
|
- net start w32time
|
||||||
|
- w32tm /resync
|
||||||
|
after_script: []
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# `default` #
|
# `default` #
|
||||||
#############
|
#############
|
||||||
|
|||||||
+25
-84
@@ -1,9 +1,8 @@
|
|||||||
# Host tests
|
|
||||||
.host_test_win_template:
|
.host_test_win_template:
|
||||||
extends: .rules:test:host_test
|
extends:
|
||||||
|
- .brew-windows-settings
|
||||||
|
- .rules:test:host_test
|
||||||
stage: host_test
|
stage: host_test
|
||||||
image: $ESP_ENV_IMAGE
|
|
||||||
tags: [windows-build, brew]
|
|
||||||
dependencies: # set dependencies to null to avoid missing artifacts issue
|
dependencies: # set dependencies to null to avoid missing artifacts issue
|
||||||
# run host_test jobs immediately, only after upload cache
|
# run host_test jobs immediately, only after upload cache
|
||||||
needs:
|
needs:
|
||||||
@@ -13,13 +12,8 @@
|
|||||||
- job: upload-submodules-cache
|
- job: upload-submodules-cache
|
||||||
optional: true
|
optional: true
|
||||||
artifacts: false
|
artifacts: false
|
||||||
variables:
|
- job: manual_gate
|
||||||
GIT_STRATEGY: fetch # use brew local mirror first
|
optional: true
|
||||||
before_script:
|
|
||||||
- if ($env:IDF_DONT_USE_MIRRORS) {
|
|
||||||
$env:IDF_MIRROR_PREFIX_MAP = ""
|
|
||||||
}
|
|
||||||
after_script: []
|
|
||||||
|
|
||||||
test_cli_installer_win:
|
test_cli_installer_win:
|
||||||
rules:
|
rules:
|
||||||
@@ -34,15 +28,12 @@ test_cli_installer_win:
|
|||||||
- tools/tools.new.json
|
- tools/tools.new.json
|
||||||
- tools/test_idf_tools/test_python_env_logs.txt
|
- tools/test_idf_tools/test_python_env_logs.txt
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
variables:
|
|
||||||
IDF_PATH: "$CI_PROJECT_DIR"
|
|
||||||
timeout: 3h
|
timeout: 3h
|
||||||
script:
|
script:
|
||||||
# Tools must be downloaded for testing
|
# Tools must be downloaded for testing
|
||||||
# cmake@version that is supported
|
# cmake@version that is supported
|
||||||
- python ${IDF_PATH}\tools\idf_tools.py download required qemu-riscv32 qemu-xtensa cmake cmake@3.22.1
|
- python tools\idf_tools.py download required qemu-riscv32 qemu-xtensa cmake cmake@3.22.1
|
||||||
- cd ${IDF_PATH}\tools\test_idf_tools
|
- cd tools\test_idf_tools
|
||||||
- python -m pip install jsonschema
|
|
||||||
- python .\test_idf_tools.py
|
- python .\test_idf_tools.py
|
||||||
- python .\test_idf_tools_python_env.py
|
- python .\test_idf_tools_python_env.py
|
||||||
|
|
||||||
@@ -53,39 +44,31 @@ test_tools_win:
|
|||||||
parallel: 4
|
parallel: 4
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- ${IDF_PATH}/*.out
|
- "*.out"
|
||||||
- ${IDF_PATH}/XUNIT_*.xml
|
- "XUNIT_*.xml"
|
||||||
reports:
|
reports:
|
||||||
junit: ${IDF_PATH}/XUNIT_*.xml
|
junit: "XUNIT_*.xml"
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
when: always
|
when: always
|
||||||
variables:
|
|
||||||
LC_ALL: C.UTF-8
|
|
||||||
PYTHONPATH: "$PYTHONPATH;$IDF_PATH\\tools;$IDF_PATH\\tools\\esp_app_trace;$IDF_PATH\\components\\partition_table;$IDF_PATH\\tools\\ci\\python_packages"
|
|
||||||
script:
|
script:
|
||||||
- python -m pip install jsonschema
|
- cd tools\test_idf_py
|
||||||
- .\install.ps1 --enable-ci
|
|
||||||
- .\export.ps1
|
|
||||||
- python "${SUBMODULE_FETCH_TOOL}" -s "all"
|
|
||||||
- cd ${IDF_PATH}/tools/test_idf_py
|
|
||||||
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
||||||
- pytest --parallel-count ${CI_NODE_TOTAL} --parallel-index ${CI_NODE_INDEX} --junitxml=${IDF_PATH}/XUNIT_RESULT.xml --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
|
- pytest --parallel-count ${CI_NODE_TOTAL} --parallel-index ${CI_NODE_INDEX} --junitxml=XUNIT_RESULT.xml --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME}
|
||||||
|
|
||||||
# Build tests
|
# Build tests
|
||||||
.test_build_system_template_win:
|
.test_build_system_template_win:
|
||||||
stage: host_test
|
extends: .host_test_win_template
|
||||||
variables:
|
|
||||||
# Enable ccache for all build jobs. See configure_ci_environment.sh for more ccache related settings.
|
|
||||||
IDF_CCACHE_ENABLE: "1"
|
|
||||||
PYTHONPATH: "$PYTHONPATH;$IDF_PATH\\tools;$IDF_PATH\\tools\\esp_app_trace;$IDF_PATH\\components\\partition_table;$IDF_PATH\\tools\\ci\\python_packages"
|
|
||||||
before_script: []
|
|
||||||
after_script: []
|
|
||||||
timeout: 4 hours
|
timeout: 4 hours
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- XUNIT_RESULT.xml
|
||||||
|
- test_build_system
|
||||||
|
expire_in: 2 days
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
junit: XUNIT_RESULT.xml
|
||||||
script:
|
script:
|
||||||
- .\install.ps1 --enable-ci
|
- cd tools\test_build_system
|
||||||
- . .\export.ps1
|
|
||||||
- python "${SUBMODULE_FETCH_TOOL}" -s "all"
|
|
||||||
- cd ${IDF_PATH}\tools\test_build_system
|
|
||||||
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
||||||
- pytest --parallel-count ${CI_NODE_TOTAL} --parallel-index ${CI_NODE_INDEX} --junitxml=${CI_PROJECT_DIR}\XUNIT_RESULT.xml --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} --durations=10
|
- pytest --parallel-count ${CI_NODE_TOTAL} --parallel-index ${CI_NODE_INDEX} --junitxml=${CI_PROJECT_DIR}\XUNIT_RESULT.xml --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} --durations=10
|
||||||
|
|
||||||
@@ -94,42 +77,16 @@ pytest_build_system_win:
|
|||||||
- .test_build_system_template_win
|
- .test_build_system_template_win
|
||||||
- .rules:labels:windows_pytest_build_system
|
- .rules:labels:windows_pytest_build_system
|
||||||
parallel: 10
|
parallel: 10
|
||||||
needs:
|
|
||||||
- job: manual_gate
|
|
||||||
optional: true
|
|
||||||
tags: [windows-build, brew]
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- XUNIT_RESULT.xml
|
|
||||||
- test_build_system
|
|
||||||
expire_in: 2 days
|
|
||||||
reports:
|
|
||||||
junit: XUNIT_RESULT.xml
|
|
||||||
when: always
|
|
||||||
|
|
||||||
pytest_build_system_win_minimal_cmake:
|
pytest_build_system_win_minimal_cmake:
|
||||||
extends:
|
extends:
|
||||||
- .test_build_system_template_win
|
- .test_build_system_template_win
|
||||||
- .rules:labels:windows_pytest_build_system
|
- .rules:labels:windows_pytest_build_system
|
||||||
parallel: 2
|
parallel: 2
|
||||||
needs:
|
|
||||||
- job: manual_gate
|
|
||||||
optional: true
|
|
||||||
tags: [windows-build, brew]
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- XUNIT_RESULT.xml
|
|
||||||
- test_build_system
|
|
||||||
expire_in: 2 days
|
|
||||||
reports:
|
|
||||||
junit: XUNIT_RESULT.xml
|
|
||||||
when: always
|
|
||||||
variables:
|
variables:
|
||||||
MINIMAL_CMAKE_VERSION: "3.22.1"
|
MINIMAL_CMAKE_VERSION: "3.22.1"
|
||||||
script:
|
script:
|
||||||
- .\install.ps1 --enable-ci
|
- python tools\idf_tools.py install cmake@${MINIMAL_CMAKE_VERSION}
|
||||||
- . .\export.ps1
|
|
||||||
- python ${IDF_PATH}\tools\idf_tools.py install cmake@${MINIMAL_CMAKE_VERSION}
|
|
||||||
- $Env:PATH = "$Env:USERPROFILE\.espressif\tools\cmake\${MINIMAL_CMAKE_VERSION}\bin;$Env:PATH"
|
- $Env:PATH = "$Env:USERPROFILE\.espressif\tools\cmake\${MINIMAL_CMAKE_VERSION}\bin;$Env:PATH"
|
||||||
- |
|
- |
|
||||||
$actualVersion = (& cmake --version).Split()[2]
|
$actualVersion = (& cmake --version).Split()[2]
|
||||||
@@ -137,8 +94,7 @@ pytest_build_system_win_minimal_cmake:
|
|||||||
Write-Error "ERROR: Wrong CMake version! Detected: $actualVersion, but expected: $Env:MINIMAL_CMAKE_VERSION"
|
Write-Error "ERROR: Wrong CMake version! Detected: $actualVersion, but expected: $Env:MINIMAL_CMAKE_VERSION"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
- python "${SUBMODULE_FETCH_TOOL}" -s "all"
|
- cd tools\test_build_system
|
||||||
- cd ${IDF_PATH}\tools\test_build_system
|
|
||||||
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
||||||
- pytest -k cmake --junitxml=${CI_PROJECT_DIR}\XUNIT_RESULT.xml --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} --durations=10
|
- pytest -k cmake --junitxml=${CI_PROJECT_DIR}\XUNIT_RESULT.xml --ignore-result-files ${KNOWN_FAILURE_CASES_FILE_NAME} --durations=10
|
||||||
|
|
||||||
@@ -147,23 +103,8 @@ pytest_buildv2_system_win:
|
|||||||
- .test_build_system_template_win
|
- .test_build_system_template_win
|
||||||
- .rules:labels:buildv2
|
- .rules:labels:buildv2
|
||||||
parallel: 10
|
parallel: 10
|
||||||
needs:
|
|
||||||
- job: manual_gate
|
|
||||||
optional: true
|
|
||||||
tags: [windows-build, brew]
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- XUNIT_RESULT.xml
|
|
||||||
- test_build_system
|
|
||||||
expire_in: 2 days
|
|
||||||
reports:
|
|
||||||
junit: XUNIT_RESULT.xml
|
|
||||||
when: always
|
|
||||||
script:
|
script:
|
||||||
- .\install.ps1 --enable-ci
|
- cd tools\test_build_system
|
||||||
- . .\export.ps1
|
|
||||||
- python "${SUBMODULE_FETCH_TOOL}" -s "all"
|
|
||||||
- cd ${IDF_PATH}\tools\test_build_system
|
|
||||||
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
- idf-ci gitlab download-known-failure-cases-file ${KNOWN_FAILURE_CASES_FILE_NAME}
|
||||||
- pytest
|
- pytest
|
||||||
--buildv2
|
--buildv2
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
import argparse
|
import argparse
|
||||||
|
import copy
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -9,13 +10,9 @@ import tarfile
|
|||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from collections.abc import Callable
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
|
||||||
from typing import Dict
|
|
||||||
from typing import List
|
|
||||||
from typing import Optional
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
import gitlab
|
import gitlab
|
||||||
|
|
||||||
@@ -36,7 +33,7 @@ def retry(func: TR) -> TR:
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
res = func(self, *args, **kwargs)
|
res = func(self, *args, **kwargs)
|
||||||
except (IOError, EOFError, gitlab.exceptions.GitlabError) as e:
|
except (OSError, EOFError, gitlab.exceptions.GitlabError) as e:
|
||||||
if isinstance(e, gitlab.exceptions.GitlabError):
|
if isinstance(e, gitlab.exceptions.GitlabError):
|
||||||
if e.response_code == 500:
|
if e.response_code == 500:
|
||||||
# retry on this error
|
# retry on this error
|
||||||
@@ -52,9 +49,11 @@ def retry(func: TR) -> TR:
|
|||||||
raise e # get out of the loop
|
raise e # get out of the loop
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
'Network failure in {}, retrying ({})'.format(getattr(func, '__name__', '(unknown callable)'),
|
'Network failure in {}, retrying ({})'.format(
|
||||||
retried))
|
getattr(func, '__name__', '(unknown callable)'), retried
|
||||||
time.sleep(2 ** retried) # wait a bit more after each retry
|
)
|
||||||
|
)
|
||||||
|
time.sleep(2**retried) # wait a bit more after each retry
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
@@ -63,26 +62,26 @@ def retry(func: TR) -> TR:
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class Gitlab(object):
|
class Gitlab:
|
||||||
JOB_NAME_PATTERN = re.compile(r'(\w+)(\s+(\d+)/(\d+))?')
|
JOB_NAME_PATTERN = re.compile(r'(\w+)(\s+(\d+)/(\d+))?')
|
||||||
|
|
||||||
DOWNLOAD_ERROR_MAX_RETRIES = 3
|
DOWNLOAD_ERROR_MAX_RETRIES = 3
|
||||||
DEFAULT_BUILD_CHILD_PIPELINE_NAME = 'Build Child Pipeline'
|
DEFAULT_BUILD_CHILD_PIPELINE_NAME = 'Build Child Pipeline'
|
||||||
|
|
||||||
def __init__(self, project_id: Union[int, str, None] = None):
|
def __init__(self, project_id: int | str | None = None):
|
||||||
config_data_from_env = os.getenv('PYTHON_GITLAB_CONFIG')
|
config_data_from_env = os.getenv('PYTHON_GITLAB_CONFIG')
|
||||||
if config_data_from_env:
|
if config_data_from_env:
|
||||||
# prefer to load config from env variable
|
# prefer to load config from env variable
|
||||||
with tempfile.NamedTemporaryFile('w', delete=False) as temp_file:
|
with tempfile.NamedTemporaryFile('w', delete=False) as temp_file:
|
||||||
temp_file.write(config_data_from_env)
|
temp_file.write(config_data_from_env)
|
||||||
config_files = [temp_file.name] # type: Optional[List[str]]
|
config_files = [temp_file.name]
|
||||||
else:
|
else:
|
||||||
# otherwise try to use config file at local filesystem
|
# otherwise try to use config file at local filesystem
|
||||||
config_files = None
|
config_files = None
|
||||||
self._init_gitlab_inst(project_id, config_files)
|
self._init_gitlab_inst(project_id, config_files)
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def _init_gitlab_inst(self, project_id: Optional[int], config_files: Optional[List[str]]) -> None:
|
def _init_gitlab_inst(self, project_id: int | None, config_files: list[str] | None) -> None:
|
||||||
gitlab_id = os.getenv('LOCAL_GITLAB_HTTPS_HOST') # if None, will use the default gitlab server
|
gitlab_id = os.getenv('LOCAL_GITLAB_HTTPS_HOST') # if None, will use the default gitlab server
|
||||||
self.gitlab_inst = gitlab.Gitlab.from_config(gitlab_id=gitlab_id, config_files=config_files)
|
self.gitlab_inst = gitlab.Gitlab.from_config(gitlab_id=gitlab_id, config_files=config_files)
|
||||||
|
|
||||||
@@ -109,7 +108,7 @@ class Gitlab(object):
|
|||||||
self.project = None
|
self.project = None
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def get_project_id(self, name: str, namespace: Optional[str] = None) -> int:
|
def get_project_id(self, name: str, namespace: str | None = None) -> int:
|
||||||
"""
|
"""
|
||||||
search project ID by name
|
search project ID by name
|
||||||
|
|
||||||
@@ -152,7 +151,7 @@ class Gitlab(object):
|
|||||||
archive_file.extractall(destination)
|
archive_file.extractall(destination)
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def download_artifact(self, job_id: int, artifact_path: List[str], destination: Optional[str] = None) -> List[bytes]:
|
def download_artifact(self, job_id: int, artifact_path: list[str], destination: str | None = None) -> list[bytes]:
|
||||||
"""
|
"""
|
||||||
download specific path of job artifacts and extract to destination.
|
download specific path of job artifacts and extract to destination.
|
||||||
|
|
||||||
@@ -169,7 +168,7 @@ class Gitlab(object):
|
|||||||
try:
|
try:
|
||||||
data = job.artifact(a_path) # type: bytes
|
data = job.artifact(a_path) # type: bytes
|
||||||
except gitlab.GitlabGetError as e:
|
except gitlab.GitlabGetError as e:
|
||||||
logging.error("Failed to download '{}' from job {}".format(a_path, job_id))
|
logging.error(f"Failed to download '{a_path}' from job {job_id}")
|
||||||
raise e
|
raise e
|
||||||
raw_data_list.append(data)
|
raw_data_list.append(data)
|
||||||
if destination:
|
if destination:
|
||||||
@@ -185,7 +184,7 @@ class Gitlab(object):
|
|||||||
return raw_data_list
|
return raw_data_list
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def find_job_id(self, job_name: str, pipeline_id: Optional[str] = None, job_status: str = 'success') -> List[Dict]:
|
def find_job_id(self, job_name: str, pipeline_id: str | None = None, job_status: str = 'success') -> list[dict]:
|
||||||
"""
|
"""
|
||||||
Get Job ID from job name of specific pipeline
|
Get Job ID from job name of specific pipeline
|
||||||
|
|
||||||
@@ -208,8 +207,9 @@ class Gitlab(object):
|
|||||||
return job_id_list
|
return job_id_list
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def download_archive(self, ref: str, destination: str, project_id: Optional[int] = None,
|
def download_archive(
|
||||||
cache_dir: Optional[str] = None) -> str:
|
self, ref: str, destination: str, project_id: int | None = None, cache_dir: str | None = None
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Download archive of certain commit of a repository and extract to destination path
|
Download archive of certain commit of a repository and extract to destination path
|
||||||
|
|
||||||
@@ -233,10 +233,11 @@ class Gitlab(object):
|
|||||||
try:
|
try:
|
||||||
project.repository_archive(sha=ref, streamed=True, action=fw.write)
|
project.repository_archive(sha=ref, streamed=True, action=fw.write)
|
||||||
except gitlab.GitlabGetError as e:
|
except gitlab.GitlabGetError as e:
|
||||||
logging.error('Failed to archive from project {}'.format(project_id))
|
logging.error(f'Failed to archive from project {project_id}')
|
||||||
raise e
|
raise e
|
||||||
logging.info('Downloaded archive size: {:.03f}MB'.format(
|
logging.info(
|
||||||
float(os.path.getsize(local_archive_file)) / (1024 * 1024)))
|
f'Downloaded archive size: {float(os.path.getsize(local_archive_file)) / (1024 * 1024):.03f}MB'
|
||||||
|
)
|
||||||
|
|
||||||
return self.decompress_archive(local_archive_file, destination)
|
return self.decompress_archive(local_archive_file, destination)
|
||||||
|
|
||||||
@@ -245,25 +246,39 @@ class Gitlab(object):
|
|||||||
try:
|
try:
|
||||||
project.repository_archive(sha=ref, streamed=True, action=temp_file.write)
|
project.repository_archive(sha=ref, streamed=True, action=temp_file.write)
|
||||||
except gitlab.GitlabGetError as e:
|
except gitlab.GitlabGetError as e:
|
||||||
logging.error('Failed to archive from project {}'.format(project_id))
|
logging.error(f'Failed to archive from project {project_id}')
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
logging.info('Downloaded archive size: {:.03f}MB'.format(float(os.path.getsize(temp_file.name)) / (1024 * 1024)))
|
logging.info(f'Downloaded archive size: {float(os.path.getsize(temp_file.name)) / (1024 * 1024):.03f}MB')
|
||||||
|
|
||||||
return self.decompress_archive(temp_file.name, destination)
|
return self.decompress_archive(temp_file.name, destination)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _to_win32_long_path(path: str) -> str:
|
||||||
|
normalized_path = os.path.normpath(os.path.abspath(path))
|
||||||
|
if normalized_path.startswith('\\\\?\\'):
|
||||||
|
return normalized_path
|
||||||
|
if normalized_path.startswith('\\\\'):
|
||||||
|
return '\\\\?\\UNC\\' + normalized_path[2:]
|
||||||
|
return '\\\\?\\' + normalized_path
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decompress_archive(path: str, destination: str) -> str:
|
def decompress_archive(path: str, destination: str) -> str:
|
||||||
full_destination = os.path.abspath(destination)
|
full_destination = os.path.abspath(destination)
|
||||||
# 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
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with tarfile.open(path, 'r') as archive_file:
|
with tarfile.open(path, 'r') as archive_file:
|
||||||
root_name = archive_file.getnames()[0]
|
members = archive_file.getmembers()
|
||||||
archive_file.extractall(full_destination)
|
root_name = members[0].name
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# tarfile keeps archive member names in POSIX form. Normalize them before
|
||||||
|
# combining with a long-path-prefixed destination to avoid invalid mixed separators.
|
||||||
|
full_destination = Gitlab._to_win32_long_path(full_destination)
|
||||||
|
members = [copy.copy(member) for member in members]
|
||||||
|
for member in members:
|
||||||
|
member.name = member.name.replace('/', '\\')
|
||||||
|
member.linkname = member.linkname.replace('/', '\\')
|
||||||
|
archive_file.extractall(full_destination, members=members)
|
||||||
except tarfile.TarError as e:
|
except tarfile.TarError as e:
|
||||||
logging.error(f'Error while decompressing archive {path}')
|
logging.error(f'Error while decompressing archive {path}')
|
||||||
raise e
|
raise e
|
||||||
@@ -280,7 +295,7 @@ class Gitlab(object):
|
|||||||
job = self.project.jobs.get(job_id)
|
job = self.project.jobs.get(job_id)
|
||||||
return ','.join(job.tag_list)
|
return ','.join(job.tag_list)
|
||||||
|
|
||||||
def get_downstream_pipeline_ids(self, main_pipeline_id: int) -> List[int]:
|
def get_downstream_pipeline_ids(self, main_pipeline_id: int) -> list[int]:
|
||||||
"""
|
"""
|
||||||
Retrieve the IDs of all downstream child pipelines for a given main pipeline.
|
Retrieve the IDs of all downstream child pipelines for a given main pipeline.
|
||||||
|
|
||||||
@@ -313,7 +328,7 @@ class Gitlab(object):
|
|||||||
|
|
||||||
return [pid for pid in child_pipeline_ids if pid is not None]
|
return [pid for pid in child_pipeline_ids if pid is not None]
|
||||||
|
|
||||||
def retry_failed_jobs(self, pipeline_id: int, retry_allowed_failures: bool = False) -> List[int]:
|
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'.
|
Retry failed jobs for a specific pipeline. Optionally include jobs marked as 'allowed failures'.
|
||||||
|
|
||||||
@@ -370,10 +385,10 @@ def main() -> None:
|
|||||||
gitlab_inst.download_archive(args.ref, args.destination)
|
gitlab_inst.download_archive(args.ref, args.destination)
|
||||||
elif args.action == 'get_project_id':
|
elif args.action == 'get_project_id':
|
||||||
ret = gitlab_inst.get_project_id(args.project_name)
|
ret = gitlab_inst.get_project_id(args.project_name)
|
||||||
print('project id: {}'.format(ret))
|
print(f'project id: {ret}')
|
||||||
elif args.action == 'retry_failed_jobs':
|
elif args.action == 'retry_failed_jobs':
|
||||||
res = gitlab_inst.retry_failed_jobs(args.pipeline_id, args.retry_allowed_failures)
|
res = gitlab_inst.retry_failed_jobs(args.pipeline_id, args.retry_allowed_failures)
|
||||||
print('jobs retried successfully: {}'.format(res))
|
print(f'jobs retried successfully: {res}')
|
||||||
elif args.action == 'get_job_tags':
|
elif args.action == 'get_job_tags':
|
||||||
ret = gitlab_inst.get_job_tags(args.job_id)
|
ret = gitlab_inst.get_job_tags(args.job_id)
|
||||||
print(ret)
|
print(ret)
|
||||||
|
|||||||
+12
-12
@@ -97,9 +97,9 @@ endmacro()
|
|||||||
#
|
#
|
||||||
function(__component_dir_quick_check var component_dir)
|
function(__component_dir_quick_check var component_dir)
|
||||||
set(res 1)
|
set(res 1)
|
||||||
get_filename_component(abs_dir ${component_dir} ABSOLUTE)
|
get_filename_component(abs_dir "${component_dir}" ABSOLUTE)
|
||||||
|
|
||||||
get_filename_component(base_dir ${abs_dir} NAME)
|
get_filename_component(base_dir "${abs_dir}" NAME)
|
||||||
string(SUBSTRING "${base_dir}" 0 1 first_char)
|
string(SUBSTRING "${base_dir}" 0 1 first_char)
|
||||||
|
|
||||||
# Check the component directory contains a CMakeLists.txt file
|
# Check the component directory contains a CMakeLists.txt file
|
||||||
@@ -149,8 +149,8 @@ function(__component_add component_dir prefix component_source)
|
|||||||
# so later in the build, these component targets actually contain the properties meant for the
|
# so later in the build, these component targets actually contain the properties meant for the
|
||||||
# corresponding component library.
|
# corresponding component library.
|
||||||
idf_build_get_property(component_targets __COMPONENT_TARGETS)
|
idf_build_get_property(component_targets __COMPONENT_TARGETS)
|
||||||
get_filename_component(abs_dir ${component_dir} ABSOLUTE)
|
get_filename_component(abs_dir "${component_dir}" ABSOLUTE)
|
||||||
get_filename_component(base_dir ${abs_dir} NAME)
|
get_filename_component(base_dir "${abs_dir}" NAME)
|
||||||
|
|
||||||
if(NOT EXISTS "${abs_dir}/CMakeLists.txt")
|
if(NOT EXISTS "${abs_dir}/CMakeLists.txt")
|
||||||
message(FATAL_ERROR "Directory '${component_dir}' does not contain a component.")
|
message(FATAL_ERROR "Directory '${component_dir}' does not contain a component.")
|
||||||
@@ -277,15 +277,15 @@ macro(__component_add_sources sources)
|
|||||||
message(WARNING "SRCS and SRC_DIRS are both specified; ignoring SRC_DIRS.")
|
message(WARNING "SRCS and SRC_DIRS are both specified; ignoring SRC_DIRS.")
|
||||||
endif()
|
endif()
|
||||||
foreach(src ${__SRCS})
|
foreach(src ${__SRCS})
|
||||||
get_filename_component(src "${src}" ABSOLUTE BASE_DIR ${COMPONENT_DIR})
|
get_filename_component(src "${src}" ABSOLUTE BASE_DIR "${COMPONENT_DIR}")
|
||||||
list(APPEND sources ${src})
|
list(APPEND sources "${src}")
|
||||||
endforeach()
|
endforeach()
|
||||||
else()
|
else()
|
||||||
if(__SRC_DIRS)
|
if(__SRC_DIRS)
|
||||||
foreach(dir ${__SRC_DIRS})
|
foreach(dir ${__SRC_DIRS})
|
||||||
get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${COMPONENT_DIR})
|
get_filename_component(abs_dir "${dir}" ABSOLUTE BASE_DIR "${COMPONENT_DIR}")
|
||||||
|
|
||||||
if(NOT IS_DIRECTORY ${abs_dir})
|
if(NOT IS_DIRECTORY "${abs_dir}")
|
||||||
message(FATAL_ERROR "SRC_DIRS entry '${dir}' does not exist.")
|
message(FATAL_ERROR "SRC_DIRS entry '${dir}' does not exist.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ macro(__component_add_sources sources)
|
|||||||
|
|
||||||
if(dir_sources)
|
if(dir_sources)
|
||||||
foreach(src ${dir_sources})
|
foreach(src ${dir_sources})
|
||||||
get_filename_component(src "${src}" ABSOLUTE BASE_DIR ${COMPONENT_DIR})
|
get_filename_component(src "${src}" ABSOLUTE BASE_DIR "${COMPONENT_DIR}")
|
||||||
list(APPEND sources "${src}")
|
list(APPEND sources "${src}")
|
||||||
endforeach()
|
endforeach()
|
||||||
else()
|
else()
|
||||||
@@ -316,11 +316,11 @@ endmacro()
|
|||||||
|
|
||||||
macro(__component_add_include_dirs lib dirs type)
|
macro(__component_add_include_dirs lib dirs type)
|
||||||
foreach(dir ${dirs})
|
foreach(dir ${dirs})
|
||||||
get_filename_component(_dir ${dir} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_LIST_DIR})
|
get_filename_component(_dir "${dir}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||||
if(NOT IS_DIRECTORY ${_dir})
|
if(NOT IS_DIRECTORY "${_dir}")
|
||||||
message(FATAL_ERROR "Include directory '${_dir}' is not a directory.")
|
message(FATAL_ERROR "Include directory '${_dir}' is not a directory.")
|
||||||
endif()
|
endif()
|
||||||
target_include_directories(${lib} ${type} ${_dir})
|
target_include_directories(${lib} ${type} "${_dir}")
|
||||||
endforeach()
|
endforeach()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[project.entry-points.idf_extension]
|
[project.entry-points.idf_extension]
|
||||||
{entry_point_name} = "{declarative_value}"
|
{entry_point_name} = "{declarative_value}"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
packages = ["test_extension_package_{suffix}"]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ def safe_cleanup_tmpdir(tmpdir: tempfile.TemporaryDirectory) -> None:
|
|||||||
"""Safely cleanup temporary directory, handling specific errors on Windows."""
|
"""Safely cleanup temporary directory, handling specific errors on Windows."""
|
||||||
try:
|
try:
|
||||||
tmpdir.cleanup()
|
tmpdir.cleanup()
|
||||||
except (PermissionError, NotADirectoryError):
|
except (PermissionError, NotADirectoryError, OSError):
|
||||||
|
# OSError: The directory is not empty
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f'Failed to cleanup temporary directory {tmpdir.name}. '
|
f'Failed to cleanup temporary directory {tmpdir.name}. '
|
||||||
'This is common on Windows when files are still in use.',
|
'This is common on Windows when files are still in use.',
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ except ImportError:
|
|||||||
import idf
|
import idf
|
||||||
|
|
||||||
current_dir = os.path.dirname(os.path.realpath(__file__))
|
current_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
idf_py_path = os.path.join(current_dir, '..', 'idf.py')
|
idf_py_path = os.path.normpath(os.path.join(current_dir, '..', 'idf.py'))
|
||||||
extension_path = os.path.join(current_dir, 'test_idf_extensions', 'test_ext')
|
extension_path = os.path.join(current_dir, 'test_idf_extensions', 'test_ext')
|
||||||
py_actions_path = os.path.join(current_dir, '..', 'idf_py_actions')
|
py_actions_path = os.path.normpath(os.path.join(current_dir, '..', 'idf_py_actions'))
|
||||||
link_path = os.path.join(py_actions_path, 'test_ext')
|
link_path = os.path.join(py_actions_path, 'test_ext')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user