3 """A script to schedule Pelican posts in advance, appropriate for a Git
4 post-receive hook. Requires the `at` job-scheduling utility."""
11 WORKING_REPO = "/home/mtsw/working"
12 INPUT_DIR = os.path.join(WORKING_REPO, "content")
13 OUTPUT_DIR = "/var/www/html"
14 PUBLISH_CONF = os.path.join(WORKING_REPO, "publishconf.py")
15 SITEGEN_COMMAND = "bash -c 'cd {} && source bin/activate && pelican {} -o {} -s {}'".format(
16 WORKING_REPO, INPUT_DIR, OUTPUT_DIR, PUBLISH_CONF)
18 DATELINE_REGEX = re.compile(r"^Date: *(\d{4}-\d{2}-\d{2} \d{2}:\d{2}) *$",
20 JOBLINE_REGEX = re.compile(r"\d+\s\w{3} (\w{3} +\d{1,2} \d{2}:\d{2}:\d{2} \d{4})")
22 def get_future_publication_times():
23 now = datetime.datetime.now()
25 for path, _dirnames, filenames in os.walk(INPUT_DIR):
26 if path.endswith("drafts"):
28 for filename in filenames:
29 if not filename.endswith(".md"):
31 with open(os.path.join(path, filename)) as post_file:
32 match = DATELINE_REGEX.search(post_file.read())
34 time = datetime.datetime.strptime(match.group(1),
41 def get_extant_at_job_times():
43 result = subprocess.run(["atq"], stdout=subprocess.PIPE)
44 job_lines = result.stdout.decode('utf8').split('\n')
45 for job_line in job_lines:
46 match = JOBLINE_REGEX.match(job_line)
48 times.add(datetime.datetime.strptime(match.group(1),
53 def schedule(command, when):
54 timestamp = when.strftime("%H:%M %Y-%m-%d")
55 at_command = ['at', timestamp]
56 at = subprocess.Popen(
58 stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
60 at.communicate(command.encode())
64 # sync our "working" repo with the bare one
65 subprocess.run(["git", "pull"], cwd=WORKING_REPO)
67 # look for scheduled future posts
68 future_publication_times = get_future_publication_times()
71 extant_at_job_times = get_extant_at_job_times()
73 # if there are future posts that don't have an atq entry, schedule a
74 # site-regen at that time
75 to_schedule = future_publication_times - extant_at_job_times
76 for time in to_schedule:
77 schedule(SITEGEN_COMMAND, time)
80 if __name__ == "__main__":