ci_fetch_submodule.py 3.9 KB

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