idf_ci_utils.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. # internal use only for CI
  2. # some CI related util functions
  3. #
  4. # SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
  5. # SPDX-License-Identifier: Apache-2.0
  6. #
  7. import logging
  8. import os
  9. import subprocess
  10. import sys
  11. from typing import List, Optional
  12. IDF_PATH = os.path.abspath(os.getenv('IDF_PATH', os.path.join(os.path.dirname(__file__), '..', '..')))
  13. def get_submodule_dirs(full_path: bool = False) -> List:
  14. """
  15. To avoid issue could be introduced by multi-os or additional dependency,
  16. we use python and git to get this output
  17. :return: List of submodule dirs
  18. """
  19. dirs = []
  20. try:
  21. lines = subprocess.check_output(
  22. ['git', 'config', '--file', os.path.realpath(os.path.join(IDF_PATH, '.gitmodules')),
  23. '--get-regexp', 'path']).decode('utf8').strip().split('\n')
  24. for line in lines:
  25. _, path = line.split(' ')
  26. if full_path:
  27. dirs.append(os.path.join(IDF_PATH, path))
  28. else:
  29. dirs.append(path)
  30. except Exception as e: # pylint: disable=W0703
  31. logging.warning(str(e))
  32. return dirs
  33. def _check_git_filemode(full_path): # type: (str) -> bool
  34. try:
  35. stdout = subprocess.check_output(['git', 'ls-files', '--stage', full_path]).strip().decode('utf-8')
  36. except subprocess.CalledProcessError:
  37. return True
  38. mode = stdout.split(' ', 1)[0] # e.g. 100644 for a rw-r--r--
  39. if any([int(i, 8) & 1 for i in mode[-3:]]):
  40. return True
  41. return False
  42. def is_executable(full_path: str) -> bool:
  43. """
  44. os.X_OK will always return true on windows. Use git to check file mode.
  45. :param full_path: file full path
  46. :return: True is it's an executable file
  47. """
  48. if sys.platform == 'win32':
  49. return _check_git_filemode(full_path)
  50. return os.access(full_path, os.X_OK)
  51. def get_git_files(path: str = IDF_PATH, full_path: bool = False) -> List[str]:
  52. """
  53. Get the result of git ls-files
  54. :param path: path to run git ls-files
  55. :param full_path: return full path if set to True
  56. :return: list of file paths
  57. """
  58. try:
  59. # this is a workaround when using under worktree
  60. # if you're using worktree, when running git commit a new environment variable GIT_DIR would be declared,
  61. # the value should be <origin_repo_path>/.git/worktrees/<worktree name>
  62. # This would effect the return value of `git ls-files`, unset this would use the `cwd`value or its parent
  63. # folder if no `.git` folder found in `cwd`.
  64. workaround_env = os.environ.copy()
  65. workaround_env.pop('GIT_DIR', None)
  66. files = subprocess.check_output(['git', 'ls-files'], cwd=path, env=workaround_env) \
  67. .decode('utf8').strip().split('\n')
  68. except Exception as e: # pylint: disable=W0703
  69. logging.warning(str(e))
  70. files = []
  71. return [os.path.join(path, f) for f in files] if full_path else files
  72. def is_in_directory(file_path: str, folder: str) -> bool:
  73. return os.path.realpath(file_path).startswith(os.path.realpath(folder) + os.sep)
  74. def get_pytest_dirs(folder: str, under_dir: Optional[str] = None) -> List[str]:
  75. from io import StringIO
  76. import pytest
  77. from _pytest.nodes import Item
  78. class CollectPlugin:
  79. def __init__(self) -> None:
  80. self.nodes: List[Item] = []
  81. def pytest_collection_modifyitems(self, items: List[Item]) -> None:
  82. for item in items:
  83. self.nodes.append(item)
  84. collector = CollectPlugin()
  85. sys_stdout = sys.stdout
  86. sys.stdout = StringIO() # swallow the output
  87. pytest.main(['--collect-only', folder], plugins=[collector])
  88. sys.stdout = sys_stdout # restore sys.stdout
  89. test_file_paths = set(node.fspath for node in collector.nodes)
  90. if under_dir:
  91. return [os.path.dirname(file) for file in test_file_paths if is_in_directory(file, under_dir)]
  92. return [os.path.dirname(file) for file in test_file_paths]