You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

190 lines
7.2 KiB

  1. #! python
  2. """
  3. This is an alternative to `git_hooks_post-receive.sh`
  4. The post commit hook in Gitea would include something like the following to ensure this script is called.
  5. #!/bin/sh
  6. read oldrev newrev ref
  7. python "D:\dev\websites\sandpoints-dev\custom_syadmin\git_hooks_post-receive.py" \
  8. --git_repo "D:\dev\websites\pirate-care-syllabus\gitea\gitea-repositories\gitea\pirate-care-syllabus.git" \
  9. --website "D:\dev\websites\testing-ground\sandpoints" \
  10. --website_preview "D:\dev\websites\testing-ground\sandpoints\_preview" \
  11. --library "D:\dev\websites\pirate-care-syllabus\_library" \
  12. --tmp_dir "D:\tmp\tmp"
  13. --oldrev $oldrev \
  14. --newrev $newrev \
  15. --ref $ref
  16. To test this script from the command line without relying on a commit hook being triggered, use:
  17. python "D:\dev\websites\sandpoints-dev\custom_syadmin\git_hooks_post-receive.py" --ref refs/heads/master --git_repo "D:\dev\websites\pirate-care-syllabus\gitea\gitea-repositories\gitea\pirate-care-syllabus.git" --website D:\\dev\\websites\\testing-ground\\sandpoints --website_preview D:\\dev\\websites\\testing-ground\\sandpoints\\_preview --tmp_dir D:\\tmp\\tmp --git_url "http://localhost:3000/gitea/pirate-care-syllabus" --base_url "http://localhost:8000"
  18. Just a mental note to read refs from the post-receive hook like this:
  19. oldrev,newrev,refname = sys.stdin.readline().strip().split(' ')
  20. """
  21. import os
  22. import argparse
  23. import random
  24. import string
  25. import subprocess
  26. import shutil
  27. import stat
  28. from datetime import datetime
  29. # format for dates
  30. date_format = "%m/%d/%Y, %H:%M:%S"
  31. # Set the variables we'll be allowing for (as CL arg or environment variable)
  32. vars = {
  33. 'oldrev': False, # set programmatically by git hook
  34. 'newrev': False, # set programmatically by git hook
  35. 'ref': False, # set programmatically by git hook
  36. 'library': '_library',
  37. 'website': '_website',
  38. 'website_preview': '_websitepreview',
  39. 'git_repo': False,
  40. 'base_url': False, #override the site's base
  41. 'git_url': False,
  42. 'git_path': 'git',
  43. 'hugo_path': 'hugo',
  44. 'tmp_dir': '/tmp',
  45. 'force': False,
  46. 'branch': None,
  47. 'preview_branch': None,
  48. }
  49. # logging time
  50. start_time = datetime.now().strftime(date_format)
  51. def random_string(length=3):
  52. """ Generates a random string """
  53. letters = string.ascii_lowercase
  54. return ''.join(random.choice(letters) for i in range(length))
  55. def get_value(name, default=False):
  56. """ Variables can be set as environment variable or in command line """
  57. if hasattr(args, name.lower()) and getattr(args, name.lower()) is not None:
  58. print('CLI:', name, getattr(args, name.lower()))
  59. return getattr(args, name.lower())
  60. elif name.upper() in os.environ:
  61. print('env:', name, os.environ[name.upper()])
  62. return os.environ[name.upper()]
  63. else:
  64. print('default:', name, default)
  65. return default
  66. def cmd(parts, cwd=None, env=None):
  67. """ Executes a shell command and returns the output """
  68. print(f"Command: {' '.join(parts)} ({cwd})")
  69. return subprocess.check_output(parts, cwd=cwd, env=env, universal_newlines=True).strip()
  70. def rmrf(path, keep_dir=False):
  71. """ Use safe-rm for recursive removal """
  72. """ @TODO: make it safer """
  73. def remove_readonly(func, path, excinfo):
  74. os.chmod(path, stat.S_IWRITE)
  75. func(path)
  76. # Try removal
  77. if keep_dir and os.path.exists(path) and len(os.path.realpath(path)) > 1:
  78. for root, dirs, files in os.walk(path):
  79. for f in files:
  80. os.unlink(os.path.join(root, f))
  81. for d in dirs:
  82. shutil.rmtree(os.path.join(root, d), onerror=remove_readonly)
  83. elif os.path.exists(path) and len(os.path.realpath(path)) > 1:
  84. shutil.rmtree(path, onerror=remove_readonly)
  85. else:
  86. print("Either the path doesn't exist or you are trying to delete the root directory(?!):", path)
  87. def clobber(filepath, data, append=False):
  88. """ Equivalent of > or >> when append is True """
  89. mode = 'a' if append else 'w'
  90. with open(filepath, mode) as f:
  91. f.write(data)
  92. def build_site(dest, tmp, branch=None, hugo_environment='gitea'):
  93. """ builds the website to "dest" using "tmp" as an intermediate location """
  94. global GIT_PATH, GIT_REPO, HUGO_PATH
  95. if branch:
  96. print("Cloning branch: ", branch)
  97. cmd([GIT_PATH, 'clone', '--branch', branch, '.', tmp], cwd=GIT_REPO)
  98. #print(' '.join([GIT_PATH, 'archive', '--format', 'tar', branch, '|', 'tar', '-x', '-C', tmp]))
  99. #subprocess.call(' '.join([GIT_PATH, 'archive', '--format', 'tar', branch, '|', 'tar', '-x', '-C', tmp]), shell=True, cwd=GIT_REPO)
  100. else:
  101. cmd([GIT_PATH, 'clone', '.', tmp], cwd=GIT_REPO)
  102. rmrf(os.path.join(tmp, '.git'))
  103. rmrf(dest, keep_dir=True)
  104. try:
  105. if not os.path.exists(dest):
  106. os.makedirs(dest)
  107. print("Build destination created: ", dest)
  108. else:
  109. print("Build destination exists: ", dest)
  110. except:
  111. print(f"Error creating the directory: {dest}")
  112. lcl = f'{tmp}/last-commit-log.txt'
  113. # overriding hugo config for development environments
  114. env = os.environ.copy()
  115. if BASE_URL:
  116. env["HUGO_PARAMS_BASEURL"] = BASE_URL
  117. if GIT_URL:
  118. env["HUGO_PARAMS_GITURL"] = GIT_URL
  119. # run the hugo command
  120. hugo_output = cmd([HUGO_PATH, '-e', hugo_environment, '-d', dest], cwd=tmp, env=env)
  121. clobber(lcl, hugo_output)
  122. now_time = datetime.now().strftime(date_format)
  123. clobber(lcl, f"\n{start_time}\n{now_time}", append=True)
  124. shutil.move(lcl, dest)
  125. # Parsing command line arguments
  126. parser = argparse.ArgumentParser()
  127. for v in vars:
  128. parser.add_argument(f"--{v.lower()}")
  129. args = parser.parse_args()
  130. # Load all variables from command line arguments or environment variables
  131. for v in vars:
  132. globals()[v.upper()] = get_value(v.lower(), vars[v])
  133. # set up directories
  134. os.makedirs(WEBSITE, exist_ok=True)
  135. os.makedirs(WEBSITE_PREVIEW, exist_ok=True)
  136. TMP_WEBSITE = os.path.join(TMP_DIR, 'website', random_string())
  137. TMP_WEBSITE_PREVIEW = os.path.join(TMP_DIR, 'websitepreview', random_string())
  138. os.makedirs(TMP_WEBSITE, exist_ok=True)
  139. os.makedirs(TMP_WEBSITE_PREVIEW, exist_ok=True)
  140. # start working
  141. # Check if someone edited the publish trigger
  142. published = False
  143. refs = cmd([GIT_PATH, 'diff-tree', '--no-commit-id', '--name-only', REF], cwd=GIT_REPO).split("\n")
  144. for r in refs:
  145. if FORCE or r == "PUBLISH.trigger.md":
  146. build_site(WEBSITE, TMP_WEBSITE, branch=BRANCH)
  147. published = True
  148. # Check if there is a !publish!
  149. if not published:
  150. refs = cmd([GIT_PATH, 'show', '--format="%s"', '-s'], cwd=GIT_REPO).split(' ')
  151. for r in refs:
  152. if r == "!publish!":
  153. build_site(WEBSITE, TMP_WEBSITE, branch=BRANCH)
  154. published = True
  155. # Let the world know if there hasn't been a publish
  156. if not published:
  157. print("The site wasn't build because there was no publish trigger, nor was it forced.")
  158. # create preview version
  159. build_site(WEBSITE_PREVIEW, TMP_WEBSITE_PREVIEW, branch=PREVIEW_BRANCH, hugo_environment='preview')
  160. # Clean up temp directories
  161. rmrf(TMP_WEBSITE)
  162. rmrf(TMP_WEBSITE_PREVIEW)
  163. # @TODO: link the library