#! python """ This is an alternative to `git_hooks_post-receive.sh` The post commit hook in Gitea would include something like the following to ensure this script is called. #!/bin/sh read oldrev newrev ref python "D:\dev\websites\sandpoints-dev\custom_syadmin\git_hooks_post-receive.py" \ --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" \ --library "D:\dev\websites\pirate-care-syllabus\_library" \ --tmp_dir "D:\tmp\tmp" --oldrev $oldrev \ --newrev $newrev \ --ref $ref To test this script from the command line without relying on a commit hook being triggered, use: 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" Just a mental note to read refs from the post-receive hook like this: oldrev,newrev,refname = sys.stdin.readline().strip().split(' ') """ import os import argparse import random import string import subprocess import shutil import stat from datetime import datetime # format for dates date_format = "%m/%d/%Y, %H:%M:%S" # Set the variables we'll be allowing for (as CL arg or environment variable) vars = { 'oldrev': False, # set programmatically by git hook 'newrev': False, # set programmatically by git hook 'ref': False, # set programmatically by git hook 'library': '_library', 'website': '_website', 'website_preview': '_websitepreview', 'git_repo': False, 'base_url': False, #override the site's base 'git_url': False, 'git_path': 'git', 'hugo_path': 'hugo', 'tmp_dir': '/tmp', 'force': False, 'branch': None, 'preview_branch': None, } # logging time start_time = datetime.now().strftime(date_format) def random_string(length=3): """ Generates a random string """ letters = string.ascii_lowercase return ''.join(random.choice(letters) for i in range(length)) def get_value(name, default=False): """ Variables can be set as environment variable or in command line """ if hasattr(args, name.lower()) and getattr(args, name.lower()) is not None: print('CLI:', name, getattr(args, name.lower())) return getattr(args, name.lower()) elif name.upper() in os.environ: print('env:', name, os.environ[name.upper()]) return os.environ[name.upper()] else: print('default:', name, default) return default def cmd(parts, cwd=None, env=None): """ Executes a shell command and returns the output """ print(f"Command: {' '.join(parts)} ({cwd})") return subprocess.check_output(parts, cwd=cwd, env=env, universal_newlines=True).strip() def rmrf(path, keep_dir=False): """ Use safe-rm for recursive removal """ """ @TODO: make it safer """ def remove_readonly(func, path, excinfo): os.chmod(path, stat.S_IWRITE) func(path) # Try removal if keep_dir and os.path.exists(path) and len(os.path.realpath(path)) > 1: for root, dirs, files in os.walk(path): for f in files: os.unlink(os.path.join(root, f)) for d in dirs: shutil.rmtree(os.path.join(root, d), onerror=remove_readonly) elif os.path.exists(path) and len(os.path.realpath(path)) > 1: shutil.rmtree(path, onerror=remove_readonly) else: print("Either the path doesn't exist or you are trying to delete the root directory(?!):", path) def clobber(filepath, data, append=False): """ Equivalent of > or >> when append is True """ mode = 'a' if append else 'w' with open(filepath, mode) as f: f.write(data) def build_site(dest, tmp, branch=None, hugo_environment='gitea'): """ builds the website to "dest" using "tmp" as an intermediate location """ global GIT_PATH, GIT_REPO, HUGO_PATH if branch: print("Cloning branch: ", branch) cmd([GIT_PATH, 'clone', '--branch', branch, '.', tmp], cwd=GIT_REPO) #print(' '.join([GIT_PATH, 'archive', '--format', 'tar', branch, '|', 'tar', '-x', '-C', tmp])) #subprocess.call(' '.join([GIT_PATH, 'archive', '--format', 'tar', branch, '|', 'tar', '-x', '-C', tmp]), shell=True, cwd=GIT_REPO) else: cmd([GIT_PATH, 'clone', '.', tmp], cwd=GIT_REPO) rmrf(os.path.join(tmp, '.git')) rmrf(dest, keep_dir=True) try: if not os.path.exists(dest): os.makedirs(dest) print("Build destination created: ", dest) else: print("Build destination exists: ", dest) except: print(f"Error creating the directory: {dest}") lcl = f'{tmp}/last-commit-log.txt' # overriding hugo config for development environments env = os.environ.copy() if BASE_URL: env["HUGO_PARAMS_BASEURL"] = BASE_URL if GIT_URL: env["HUGO_PARAMS_GITURL"] = GIT_URL # run the hugo command hugo_output = cmd([HUGO_PATH, '-e', hugo_environment, '-d', dest], cwd=tmp, env=env) clobber(lcl, hugo_output) now_time = datetime.now().strftime(date_format) clobber(lcl, f"\n{start_time}\n{now_time}", append=True) shutil.move(lcl, dest) # Parsing command line arguments parser = argparse.ArgumentParser() for v in vars: parser.add_argument(f"--{v.lower()}") args = parser.parse_args() # Load all variables from command line arguments or environment variables for v in vars: globals()[v.upper()] = get_value(v.lower(), vars[v]) # set up directories os.makedirs(WEBSITE, exist_ok=True) os.makedirs(WEBSITE_PREVIEW, exist_ok=True) TMP_WEBSITE = os.path.join(TMP_DIR, 'website', random_string()) TMP_WEBSITE_PREVIEW = os.path.join(TMP_DIR, 'websitepreview', random_string()) os.makedirs(TMP_WEBSITE, exist_ok=True) os.makedirs(TMP_WEBSITE_PREVIEW, exist_ok=True) # start working # Check if someone edited the publish trigger published = False refs = cmd([GIT_PATH, 'diff-tree', '--no-commit-id', '--name-only', REF], cwd=GIT_REPO).split("\n") for r in refs: if FORCE or r == "PUBLISH.trigger.md": build_site(WEBSITE, TMP_WEBSITE, branch=BRANCH) published = True # Check if there is a !publish! if not published: refs = cmd([GIT_PATH, 'show', '--format="%s"', '-s'], cwd=GIT_REPO).split(' ') for r in refs: if r == "!publish!": build_site(WEBSITE, TMP_WEBSITE, branch=BRANCH) published = True # Let the world know if there hasn't been a publish if not published: print("The site wasn't build because there was no publish trigger, nor was it forced.") # create preview version build_site(WEBSITE_PREVIEW, TMP_WEBSITE_PREVIEW, branch=PREVIEW_BRANCH, hugo_environment='preview') # Clean up temp directories rmrf(TMP_WEBSITE) rmrf(TMP_WEBSITE_PREVIEW) # @TODO: link the library