gitmirror.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. from subprocess import run,Popen, PIPE
  5. gitlab_api_token="t9cAod1DpruzessGg8bq"
  6. local_base=f"./repositories"
  7. COM=sys.argv[0]
  8. def parser():
  9. from argparse import ArgumentParser
  10. p = ArgumentParser()
  11. p.add_argument("source", help="git origin that will be used to fetch repos. Should support 'ssh <source> info'")
  12. p.add_argument("-b", "--base", default="./repositories", help="local basedir for mirror")
  13. p.add_argument("-t", "--threads", default=4, type=int, help="number of worker threads")
  14. return p
  15. def main():
  16. p = parser()
  17. args = p.parse_args()
  18. repos = repos_from_lines(git_info(args.source))
  19. repo_obj = [Repo(args.source,r,args.base) for r in repos]
  20. for output in map_threaded(
  21. lambda r:r.pull(),
  22. repo_obj,
  23. threads = args.threads,
  24. debug=lambda cur,tot,arg:print(f"{COM}: {cur}/{tot}: {arg}"),
  25. ):
  26. pass
  27. class Repo():
  28. def __init__(self,remote,path,base):
  29. self.remote = remote
  30. self.path = path
  31. self.base = base
  32. @property
  33. def origin(self):
  34. return f"{self.remote}:{self.path}"
  35. @property
  36. def local_path(self):
  37. return os.path.join(self.base, self.remote, self.path+".git")
  38. @property
  39. def basename(self):
  40. return self.path_split[-1]
  41. @property
  42. def path_split(self):
  43. return self.path.split("/")
  44. @property
  45. def namespace(self):
  46. return self.path_split[:-1]
  47. def pull(self):
  48. if not os.path.isdir(os.path.join(self.local_path , "refs","remotes","origin")):
  49. self.init()
  50. self.fetch()
  51. return True
  52. def fetch(self):
  53. proc = run(["git","fetch","-p","origin","refs/*:refs/*"], cwd=self.local_path)
  54. if not proc.returncode == 0:
  55. print(f"Error: {proc}, {self.local_path}")
  56. _rm(self.local_path)
  57. def init(self):
  58. _rm(self.local_path)
  59. run(["mkdir","-p",self.local_path])
  60. proc1 = run(["git","init","--bare"], cwd=self.local_path)
  61. proc2 = run(["git","remote","add","origin",self.origin], cwd=self.local_path)
  62. if not proc1.returncode == 0 and proc2.returncode == 0:
  63. print(f"Error: {proc1}, {proc2}, {self.local_path}")
  64. _rm(self.local_path)
  65. def __repr__(self):
  66. return f"Repo('{self.remote}','{self.path}','{self.base}')"
  67. def git_info(source_server):
  68. proc = Popen(
  69. ["ssh",source_server,"info"],
  70. stdout=PIPE,
  71. stderr=PIPE,
  72. encoding="utf-8")
  73. for line in iter(proc.stdout.readline, ""):
  74. yield line
  75. proc.wait()
  76. print("GIT_INFO DONE!!!!!!!!!!!!!!!")
  77. def repos_from_lines(lines):
  78. for line in lines:
  79. if line.find("gitolite") >= 0:
  80. continue
  81. fields = line.split()
  82. if len(fields) < 3:
  83. continue
  84. yield fields[2]
  85. '''
  86. :param target: A function to be mapped.
  87. :param args: An Array or Generator to be mapped.
  88. :return: Generator of function return values, but not in same order.
  89. '''
  90. def map_threaded(target, args=[], threads=4, debug=None):
  91. import threading
  92. import queue
  93. inq = queue.Queue()
  94. outq = queue.Queue()
  95. jobs = []
  96. def worker():
  97. while True:
  98. try:
  99. num, arg = inq.get(block=False)
  100. except:
  101. continue
  102. if num == None:
  103. break
  104. if debug:
  105. debug( num, num+inq.qsize(), arg)
  106. result = target(arg)
  107. outq.put(result)
  108. while len(jobs) < threads:
  109. th = threading.Thread(target=worker)
  110. th.start()
  111. jobs.append(th)
  112. def filler():
  113. i = 0
  114. for arg in args:
  115. i += 1
  116. inq.put((i,arg))
  117. for job in jobs:
  118. inq.put((None,None))
  119. fillThread = threading.Thread(target=filler)
  120. fillThread.start()
  121. while fillThread.is_alive():
  122. yield outq.get(block=True)
  123. fillThread.join()
  124. while not outq.empty():
  125. yield outq.get(block=True)
  126. for job in jobs:
  127. job.join()
  128. while not outq.empty():
  129. yield outq.get(block=True)
  130. def _rm(di):
  131. run(["rm","-rf", di])
  132. if __name__ == "__main__":
  133. main()