subprocess.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import json
  2. import shlex
  3. import subprocess
  4. from typing import (
  5. Any,
  6. Dict,
  7. Iterable,
  8. List,
  9. Optional,
  10. Tuple,
  11. Union
  12. )
  13. from dataladmetadatamodel.log import logger
  14. from dataladmetadatamodel.mapper.reference import Reference
  15. def execute(arguments: Union[str, List[str]],
  16. stdin_content: Optional[Union[str, bytes]] = None) -> Any:
  17. logger.debug(f"gitbackend: execute({arguments}, {stdin_content})")
  18. return subprocess.run(
  19. shlex.split(arguments) if isinstance(arguments, str) else arguments,
  20. input=(
  21. stdin_content.encode()
  22. if isinstance(stdin_content, str)
  23. else stdin_content),
  24. stdout=subprocess.PIPE,
  25. stderr=subprocess.PIPE)
  26. def checked_execute(arguments: Union[str, List[str]],
  27. stdin_content: Optional[Union[str, bytes]] = None
  28. ) -> Tuple[List[str], List[str]]:
  29. result = execute(arguments, stdin_content)
  30. if result.returncode != 0:
  31. raise RuntimeError(
  32. f"Command failed (exit code: {result.returncode}) "
  33. f"{' '.join(arguments)}:\n"
  34. f"STDOUT:\n"
  35. f"{result.stdout.decode()}"
  36. f"STDERR:\n"
  37. f"{result.stderr.decode()}")
  38. return (
  39. result.stdout.decode().splitlines(),
  40. result.stderr.decode().splitlines())
  41. def git_command_line(repo_dir: str,
  42. command: str,
  43. arguments: List[str]
  44. ) -> List[str]:
  45. return [
  46. "git",
  47. "-P",
  48. "--git-dir",
  49. repo_dir + "/.git",
  50. command
  51. ] + arguments
  52. def git_text_result(cmd_line):
  53. result = checked_execute(cmd_line)[0]
  54. return "\n".join(result)
  55. def adapt_for_remote(repo_dir: str, object_reference: str) -> str:
  56. from dataladmetadatamodel.mapper.gitmapper.localcache import (
  57. cache_object,
  58. get_cache_realm,
  59. )
  60. if Reference.is_remote(repo_dir):
  61. cache_object(repo_dir, object_reference)
  62. return str(get_cache_realm(repo_dir))
  63. return repo_dir
  64. def git_load_str(repo_dir: str, object_reference: str) -> str:
  65. repo_dir = adapt_for_remote(repo_dir, object_reference)
  66. cmd_line = git_command_line(repo_dir, "show", [object_reference])
  67. return git_text_result(cmd_line)
  68. def git_load_json(repo_dir: str, object_reference: str) -> Union[Dict, List]:
  69. repo_dir = adapt_for_remote(repo_dir, object_reference)
  70. return json.loads(git_load_str(repo_dir, object_reference))
  71. def git_init(repo_dir: str) -> None:
  72. cmd_line = ["git", "init", repo_dir]
  73. checked_execute(cmd_line)
  74. def git_object_exists_locally(repo_dir: str,
  75. object_reference) -> bool:
  76. cmd_line = git_command_line(repo_dir, "cat-file", ["-e", object_reference])
  77. return execute(cmd_line).returncode == 0
  78. def git_read_tree_node(repo_dir: str,
  79. object_reference) -> List[str]:
  80. repo_dir = adapt_for_remote(repo_dir, object_reference)
  81. cmd_line = git_command_line(repo_dir, "cat-file", ["-p", object_reference])
  82. return checked_execute(cmd_line)[0]
  83. def git_ls_tree(repo_dir, object_reference) -> List[str]:
  84. repo_dir = adapt_for_remote(repo_dir, object_reference)
  85. cmd_line = git_command_line(repo_dir, "ls-tree", [object_reference])
  86. return checked_execute(cmd_line)[0]
  87. def git_ls_tree_recursive(repo_dir, object_reference, show_intermediate=False) -> List[str]:
  88. repo_dir = adapt_for_remote(repo_dir, object_reference)
  89. if show_intermediate is True:
  90. cmd_line = git_command_line(repo_dir, "ls-tree", ["-r", "-t", object_reference])
  91. else:
  92. cmd_line = git_command_line(repo_dir, "ls-tree", ["-r", object_reference])
  93. return checked_execute(cmd_line)[0]
  94. def git_save_str(repo_dir, content: str) -> str:
  95. cmd_line = git_command_line(repo_dir, "hash-object", ["-w", "--stdin"])
  96. return checked_execute(cmd_line, stdin_content=content)[0][0]
  97. def git_save_file_list(repo_dir, file_list: List[str]) -> List[str]:
  98. cmd_line = git_command_line(
  99. repo_dir,
  100. "hash-object",
  101. ["-w", "--no-filters", "--stdin-paths"])
  102. return checked_execute(
  103. cmd_line,
  104. stdin_content="\n".join(file_list))[0]
  105. def git_save_json(repo_dir, json_object: Union[Dict, List]) -> str:
  106. return git_save_str(repo_dir, json.dumps(json_object))
  107. def git_save_tree_node(repo_dir,
  108. entry_set: Iterable[Tuple[str, str, str, str]]
  109. ) -> str:
  110. tree_spec = "\n".join([
  111. f"{flag} {node_type} {object_hash}\t{name}"
  112. for flag, node_type, object_hash, name in entry_set
  113. ]) + "\n"
  114. cmd_line = git_command_line(repo_dir, "mktree", ["--missing", ])
  115. return checked_execute(cmd_line, stdin_content=tree_spec)[0][0]
  116. def git_save_tree(repo_dir,
  117. entry_set: Iterable[Tuple[str, str, str, str]]
  118. ) -> str:
  119. tree_spec = "\n".join([
  120. f"{flag} {node_type} {object_hash}\t{name}"
  121. for flag, node_type, object_hash, name in entry_set
  122. ]) + "\n"
  123. cmd_line = git_command_line(repo_dir, "mktree", ["--missing", ])
  124. return checked_execute(cmd_line, stdin_content=tree_spec)[0][0]
  125. def git_update_ref(repo_dir: str, ref_name: str, location: str) -> None:
  126. cmd_line = git_command_line(
  127. repo_dir,
  128. "update-ref",
  129. [ref_name, location])
  130. checked_execute(cmd_line)
  131. def git_fetch_object(repo_dir: str,
  132. remote_repo: str,
  133. remote_reference: str):
  134. return git_fetch_reference(repo_dir, remote_repo, remote_reference)
  135. def git_fetch_reference(repo_dir: str,
  136. remote_repo: str,
  137. remote_reference: str,
  138. local_reference: Optional[str] = None):
  139. cmd_line = git_command_line(
  140. repo_dir,
  141. "fetch",
  142. [
  143. remote_repo,
  144. "-f",
  145. "--no-tags",
  146. "--no-recurse-submodules",
  147. remote_reference + (
  148. f":{local_reference}"
  149. if local_reference is not None
  150. else ""
  151. )
  152. ]
  153. )
  154. checked_execute(cmd_line)