ci_fetch_submodule.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import re
  2. import os
  3. import subprocess
  4. import argparse
  5. import shutil
  6. import time
  7. import gitlab_api
  8. SUBMODULE_PATTERN = re.compile(r"\[submodule \"([^\"]+)\"]")
  9. PATH_PATTERN = re.compile(r"path\s+=\s+(\S+)")
  10. URL_PATTERN = re.compile(r"url\s+=\s+(\S+)")
  11. SUBMODULE_ARCHIVE_TEMP_FOLDER = "submodule_archive"
  12. class SubModule(object):
  13. # We don't need to support recursive submodule clone now
  14. GIT_LS_TREE_OUTPUT_PATTERN = re.compile(r"\d+\s+commit\s+([0-9a-f]+)\s+")
  15. def __init__(self, gitlab_inst, path, url):
  16. self.path = path
  17. self.gitlab_inst = gitlab_inst
  18. self.project_id = self._get_project_id(url)
  19. self.commit_id = self._get_commit_id(path)
  20. def _get_commit_id(self, path):
  21. output = subprocess.check_output(["git", "ls-tree", "HEAD", path])
  22. output = output.decode()
  23. # example output: 160000 commit d88a262fbdf35e5abb372280eb08008749c3faa0 components/esp_wifi/lib
  24. match = self.GIT_LS_TREE_OUTPUT_PATTERN.search(output)
  25. return match.group(1)
  26. def _get_project_id(self, url):
  27. base_name = os.path.basename(url)
  28. project_id = self.gitlab_inst.get_project_id(os.path.splitext(base_name)[0], # remove .git
  29. namespace="espressif")
  30. return project_id
  31. def download_archive(self):
  32. print("Update submodule: {}: {}".format(self.path, self.commit_id))
  33. path_name = self.gitlab_inst.download_archive(self.commit_id, SUBMODULE_ARCHIVE_TEMP_FOLDER,
  34. self.project_id)
  35. renamed_path = os.path.join(os.path.dirname(path_name), os.path.basename(self.path))
  36. os.rename(path_name, renamed_path)
  37. shutil.rmtree(self.path, ignore_errors=True)
  38. shutil.move(renamed_path, os.path.dirname(self.path))
  39. def update_submodule(git_module_file, submodules_to_update):
  40. gitlab_inst = gitlab_api.Gitlab()
  41. submodules = []
  42. with open(git_module_file, "r") as f:
  43. data = f.read()
  44. match = SUBMODULE_PATTERN.search(data)
  45. while True:
  46. next_match = SUBMODULE_PATTERN.search(data, pos=match.end())
  47. if next_match:
  48. end_pos = next_match.start()
  49. else:
  50. end_pos = len(data)
  51. path_match = PATH_PATTERN.search(data, pos=match.end(), endpos=end_pos)
  52. url_match = URL_PATTERN.search(data, pos=match.end(), endpos=end_pos)
  53. path = path_match.group(1)
  54. url = url_match.group(1)
  55. filter_result = True
  56. if submodules_to_update:
  57. if path not in submodules_to_update:
  58. filter_result = False
  59. if filter_result:
  60. submodules.append(SubModule(gitlab_inst, path, url))
  61. match = next_match
  62. if not match:
  63. break
  64. shutil.rmtree(SUBMODULE_ARCHIVE_TEMP_FOLDER, ignore_errors=True)
  65. for submodule in submodules:
  66. submodule.download_archive()
  67. if __name__ == '__main__':
  68. start_time = time.time()
  69. parser = argparse.ArgumentParser()
  70. parser.add_argument("--repo_path", "-p", default=".", help="repo path")
  71. parser.add_argument("--submodule", "-s", default="all",
  72. help="Submodules to update. By default update all submodules. "
  73. "For multiple submodules, separate them with `;`. "
  74. "`all` and `none` are special values that indicates we fetch all / none submodules")
  75. args = parser.parse_args()
  76. if args.submodule == "none":
  77. print("don't need to update submodules")
  78. exit(0)
  79. if args.submodule == "all":
  80. _submodules = []
  81. else:
  82. _submodules = args.submodule.split(";")
  83. update_submodule(os.path.join(args.repo_path, ".gitmodules"), _submodules)
  84. print("total time spent on update submodule: {:.02f}s".format(time.time() - start_time))