-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtasks.py
233 lines (190 loc) · 6.39 KB
/
tasks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
"""
Tasks for managing bnbalsamo.github.io
"""
import os
from datetime import datetime
from pathlib import Path
from uuid import uuid4
from invoke import Collection, task
from werkzeug.utils import secure_filename
# Change the CWD to the repo root.
_LAST_DIR = None
while not Path("./tasks.py").exists():
os.chdir("..")
_CURRENT_DIR = Path(".").resolve()
if _CURRENT_DIR == _LAST_DIR:
# We hit the FS root :(
raise FileNotFoundError("Could not find the repository root.")
# Relevant directories, relative to the repo root.
DIRECTORIES = {
"drafts": Path("./_drafts"),
"posts": Path("./_posts"),
"presentations": Path("./_presentations"),
"speaker_notes": Path("./_speaker_notes"),
}
# Used in some helper prints - never actually called.
EDITOR = os.environ.get("EDITOR") or "nano"
speaker_notes_header = """---
layout: page
---
"""
def parse_for_notes(p):
note_lines = []
with open(p) as f:
capturing = False
for line in f.readlines():
if line == "???\n":
capturing = True
continue
if line == "---\n":
# Ignore the header
if capturing:
note_lines.append(line)
capturing = False
continue
if capturing:
note_lines.append(line)
notes_markdown = "".join(note_lines)
return notes_markdown
def title_to_filename(title):
"""
Convert a post or presentation title to a file name.
"""
filename = title.lower()
filename = secure_filename(filename)
return filename
@task(name="post")
def new_post(c, title):
"""
Create a new post.
"""
print("Creating draft post")
print(f"Title: {title}")
filepath = DIRECTORIES["drafts"] / (title_to_filename(title) + ".md")
filepath = filepath.resolve()
print(f"File Path: {filepath}")
if filepath.exists():
raise FileExistsError(filepath.resolve())
with open(filepath, "w") as f:
f.write(f"---\nlayout: post\ntitle: {title}\n---\n\n")
print("Open your new post for editing with...")
print(f"{EDITOR} {filepath}")
@task(name="presentation")
def new_presentation(c, title):
"""
Create a new presentation.
"""
print("!!! Presentations don't support drafts, this will be published !!!")
print("Creating presentation")
print(f"Title: {title}")
filepath = DIRECTORIES["presentations"] / (title_to_filename(title) + ".html")
filepath = filepath.resolve()
print(f"File Path: {filepath}")
if filepath.exists():
raise FileExistsError(filepath)
with open(filepath, "w") as f:
f.write(
"---\n"
"layout: presentation\n"
f"pres_title: {title}\n---\n\n"
"class: center, middle\n"
f"# {title}\n\n"
"---\n\n"
)
print("Open your new presentation for editing with...")
print(f"{EDITOR} {filepath}")
@task(name="post")
def publish_post(c, title, year=None, month=None, day=None):
"""
Publish a post from drafts.
"""
print(f"Publishing Post: {title}")
draft_filepath = DIRECTORIES["drafts"] / (title_to_filename(title) + ".md")
draft_filepath = draft_filepath.resolve()
print(f"Draft File Path: {draft_filepath}")
if not draft_filepath.exists():
raise FileNotFoundError(draft_filepath)
if year is None:
# Zero padding here might be overkill, but it's consistent!
year = f"{datetime.now().year:04}"
if month is None:
month = f"{datetime.now().month:02}"
if day is None:
day = f"{datetime.now().day:02}"
post_filepath = DIRECTORIES["posts"] / (
f"{year}-{month}-{day}-{title_to_filename(title)}" + ".md"
)
post_filepath = post_filepath.resolve()
print(f"Post File Path: {post_filepath}")
if post_filepath.exists():
raise FileExistsError(post_filepath)
draft_filepath.rename(post_filepath)
@task(name="site")
def export_site(c):
"""
Build and export the site to the local host.
"""
print("Building site...")
c.run("sudo docker run --rm -v $(pwd):/srv/jekyll jekyll/jekyll jekyll build")
print(f"Site available in `{Path('./_site').resolve()}`")
@task(name="dockerimage")
def export_docker_image(c, image_name="bnbalsamo.github.io:latest"):
"""
Build and export the docker image to the local host.
"""
c.run(f"sudo docker build . -t {image_name}")
@task(name="speakernotes")
def export_speaker_notes(c):
"""
Build and export speaker notes to the local host.
"""
for x in Path("./_presentations").iterdir():
notes_markdown = parse_for_notes(x)
with open(DIRECTORIES["speaker_notes"] / (x.name[0:-5] + ".md"), "w") as f:
f.write(speaker_notes_header + notes_markdown)
@task(name="dockerimage")
def run_docker_image(c, image_name="bnbalsamo.github.io:latest", port=80, build=True):
"""
Run the docker image locally.
"""
uid = uuid4().hex
if build:
export_docker_image(c, image_name=image_name)
print(f"Running container. Access site at localhost:{port}")
print(f"After exiting the server run docker rm {uid} to clean up the container.")
c.run(f"sudo docker run -p {port}:80 {image_name} --name {uid}")
@task(name="testsite")
def run_test_site(c, port=4000):
"""
Run the test site locally.
"""
uid = uuid4().hex
print(f"Running test site on port {port}...")
c.run(
f"sudo docker run -d --name test_site_{uid} -p {port}:4000 "
"-v $(pwd):/srv/jekyll jekyll/jekyll jekyll serve --watch -D"
)
print("Run the following to end the test server instance...")
print(f"sudo docker stop test_site_{uid} && " f"sudo docker rm test_site_{uid}")
# Organize into our subcommands...
ns = Collection() # Make implicit root explicit
# Add "new" subcommand
new_ns = Collection("new")
new_ns.add_task(new_post)
new_ns.add_task(new_presentation)
ns.add_collection(new_ns)
# Add "publish" subcommand
publish_ns = Collection("publish")
publish_ns.add_task(publish_post)
ns.add_collection(publish_ns)
# Add "export" subcommand
export_ns = Collection("export")
export_ns.add_task(export_site)
export_ns.add_task(export_docker_image)
export_ns.add_task(export_speaker_notes)
ns.add_collection(export_ns)
# Add "run" subcommand
run_ns = Collection("run")
run_ns.add_task(run_docker_image)
run_ns.add_task(run_test_site)
ns.add_collection(run_ns)