浏览代码

Initial commit

master
当前提交
41c80533cd
共有 10 个文件被更改,包括 369 次插入0 次删除
  1. +3
    -0
      .gitignore
  2. +13
    -0
      Makefile
  3. +3
    -0
      README.md
  4. +142
    -0
      build.py
  5. 二进制
      static/images/photo.jpg
  6. +28
    -0
      templates/contact.html
  7. +41
    -0
      templates/index.html
  8. +25
    -0
      templates/layout/base.html
  9. +100
    -0
      templates/theme/style.css
  10. +14
    -0
      watch.sh

+ 3
- 0
.gitignore 查看文件

@@ -0,0 +1,3 @@
__pycache__
*.pyc
/public

+ 13
- 0
Makefile 查看文件

@@ -0,0 +1,13 @@
all: build

@PHONY: build
build:
./build.py

@PHONY: watch
watch:
./watch.sh

@PHONY: clean
clean:
rm -rf public

+ 3
- 0
README.md 查看文件

@@ -0,0 +1,3 @@
all: build

build:

+ 142
- 0
build.py 查看文件

@@ -0,0 +1,142 @@
#!/usr/bin/env python3

import codecs
import logging
import os
from shutil import copyfile

import jinja2

logging.basicConfig()
log = logging.getLogger("build")
log.setLevel(logging.INFO)

class App(object):
# Global template variables.
def vars(self):
return {
"site": {
"name": "noah.is",
},
}

def __init__(self):
# Jinja environment.
self.env = jinja2.Environment(
loader=jinja2.FileSystemLoader("./templates"),
)

# Collect all the templates and all the static files.
self.templates = self.crawl("./templates") # TODO: remove list() wrapper
self.static = self.crawl("./static")

# Copy all of the static files into the public root.
if not os.path.isdir("./public"):
self.mkdir("./public")
for static in self.static:
public = static.replace("./static/", "./public/")
self.mkdir(public.rsplit("/", 1)[0]) # ensure the path will exist
if self.should_copy_static(static, public):
log.info("COPY STATIC FILE %s -> %s", static, public)
copyfile(static, public)

# And render all of the templates.
for template in self.templates:
if template.startswith("./templates/layout"): # don't create these
continue
public = self.template_to_public(template)
self.mkdir(public.rsplit("/", 1)[0]) # ensure the path will exist
log.info("RENDER TEMPLATE: %s -> %s", template, public)
self.render_template(template, public)

def render_template(self, template, public):
"""
Render a template to the public path.

Parameters:
template (str): path in ./templates
public (str): path in ./public
"""
tpl = self.env.get_template(template.replace("./templates/", ""))
html = tpl.render(self.vars())

with codecs.open(public, "w", "utf-8") as fh:
fh.write(html)


def crawl(self, path):
"""
Crawl a directory recursively and return a list of file paths.

Parameters:
path (str)

Returns:
filepaths (list[str])
"""
for root, dirs, files in os.walk(path):
for file in files:
yield os.path.join(root, file)

def should_copy_static(self, static_file, public_file):
"""
Determine if a static file should be copied over to the public folder.

This means the public file either does not exist yet, or is older
than the static file and so the new static file should be copied over it.

Parameters:
static_file (str)
public_file (str)

Returns:
bool: True if public_file doesn't exist or is older than static_file.
"""
if not os.path.exists(public_file):
return True
return os.stat(static_file).st_mtime > os.stat(public_file).st_mtime

def template_to_public(self, template_filename):
"""
Convert a template filename into a public name, preferring clean URL
paths without file extension suffixes.

Examples:
* /index.html -> /index.html
* /about.html -> /about/index.html
* /photos/index.html -> /photos/index.html
* /photos/2018.html -> /photos/2018/index.html

The `.html` and `.md` file types will suppress suffixes in this way.

Parameters:
template_filename: like ``./templates/about.html``

Returns:
public_filename: like ``./public/about/index.html``
"""
public = template_filename.replace("./templates/", "./public/")
path, filename = public.rsplit("/", 1)
basename, ext = filename.rsplit(".", 1)
if ext not in ["html", "md"]:
# Not a web page so just keep the path literal.
return public

print("template_to_public:", template_filename, public)

# See if it's already an index page or if we need to create one.
if filename in ["index.html", "index.md"]:
return public # already a good name
else:
print("HERE:", os.path.join(path, basename, "index."+ext))
log.info("basename=%r path=%r filename=%s", basename, path, filename)
return os.path.join(path, basename, "index."+ext)

def mkdir(self, path):
"""Create a directory and log it."""
if not os.path.isdir(path):
log.info("mkdir: %s", path)
os.makedirs(path, mode=0o755)

if __name__ == "__main__":
App()

二进制
static/images/photo.jpg 查看文件

之前 之后
宽度: 512  |  高度: 512  |  大小: 117 KiB

+ 28
- 0
templates/contact.html 查看文件

@@ -0,0 +1,28 @@
{% extends "layout/base.html" %}
{% block title %}Contact Me{% endblock %}
{% block content %}
<section class="first">
<h1>Contact Me</h1>

<p>
Below are some places you can find me on the Internet and ways you can
contact me.
</p>

<table>
<tr>
<td align="right">Email:</td>
<td>
<a href="https://www.kirsle.net/contact">Send me an e-mail here</a>
</td>
</tr>
</table>

<h2>External Links</h2>

<ul>
<li><a href="https://www.kirsle.net/">Kirsle.net</a>, my web blog.</a></li>
<li><a href="https://github.com/kirsle">GitHub</a></li>
</ul>
</section>
{% endblock %}

+ 41
- 0
templates/index.html 查看文件

@@ -0,0 +1,41 @@
{% extends "layout/base.html" %}
{% block title %}Welcome{% endblock %}
{% block content %}
<section class="first">
<img src="/images/photo.jpg" class="circle center" width="320">

<h1>Hello world!</h1>

<p>
This is the personal homepage of <strong>Noah Petherbridge</strong>.
</p>

<p>
I'm a software developer in California. I write about random stuff on
my <a href="https://www.kirsle.net/" target="_blank">web blog</a> and
maintain a handful of open source projects on
<a href="https://github.com/kirsle" target="_blank">GitHub</a>
and <a href="https://git.kirsle.net/" target="_blank">git.kirsle.net</a>.
</p>

<h2>Software Development</h2>

<p>
I'm a full stack web developer and I program in Go, Python, and
JavaScript. One of my largest personal projects is
<a href="https://www.rivescript.com/" target="_blank">RiveScript</a>,
a scripting language for programming chat bots &mdash; matching a user's
message to a certain response.
</p>

<p>
My web blog, <a href="https://www.kirsle.net/" target="_blank">Kirsle.net</a>,
is written in Go and I keep that source code <a href="https://github.com/kirsle/blog" target="_blank">here</a>
for now. Previously it was written in
<a href="https://git.kirsle.net/apps/rophako" target="_blank">Python</a>
and its <a href="https://git.kirsle.net/kirsle/kirsle.net" target="_blank">HTML pages</a>
are readable too. The site you're reading is a simple Python-generated
static site <a href="https://git.kirsle.net/kirsle/noah.is">here</a>.
</p>
</section>
{% endblock %}

+ 25
- 0
templates/layout/base.html 查看文件

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Untitled{% endblock %} - {{ site.name }}</title>

<link rel="stylesheet" type="text/css" href="/theme/style.css">
</head>
<body>

<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
<h1><a href="/">{{ site.name }}</a></h1>
</nav>
</header>

<main>
{% block content %}{% endblock %}
</main>

</body>
</html>

+ 100
- 0
templates/theme/style.css 查看文件

@@ -0,0 +1,100 @@
html, body {
height: 100%;
margin: 0;
}
body {
background-color: #0099FF;
font-family: Arial, Helvetica, sans-serif;
font-size: medium;
line-height: 1.6rem;
color: #222;
margin: 0 auto;
max-width: 950px;
}

a:link, a:visited {
color: #006699;
}
a:hover, a:active {
color: #0099FF;
}

h1 {
font-size: 24pt;
}
h2 {
font-size: 14pt;
}

img {
max-width: 100%;
height: auto;
}
img.circle {
border-radius: 50%;
}

header {
position: fixed;
top: 0;
left: 0;
right: 0;
line-height: 3rem;
background-color: rgba(255, 153, 255, 0.8);
box-shadow: 0px 2px 2px #000;
}
header h1 {
margin: 0;
font-size: larger;
font-style: italic;
}
header nav {
max-width: 860px;
margin: 0 auto;
padding: 0 2rem;
font-size: large;
}
header nav ul {
list-style: none;
float: right;
margin: 0;
}
header nav ul li {
display: inline;
padding-left: 8px;
}
header nav a {
color: #000 !important;
font-weight: bold;
text-decoration: none;
}

main {
min-height: 100%;
margin: 0 24px;
background-color: #FFF;
box-shadow: 0px 0px 4px #000;
}
section {
padding: 2rem;
}
section.first {
padding-top: 4rem;
}

footer {
max-width: 860px;
padding: 0 4rem;
margin: 2rem auto;
color: #036;
font-weight: bold;
text-align: center;
font-style: italic;
}

.center {
display: block;
text-align: center;
margin-left: auto;
margin-right: auto;
}

+ 14
- 0
watch.sh 查看文件

@@ -0,0 +1,14 @@
#!/bin/bash

function die() {
echo >&2 $1
exit 1
}

# Before we crash and burn, make sure necessary programs are installed!
command -v inotifywait || die "I need the inotifywait command (apt install inotify-tools)"

make
while inotifywait -r -e modify,move,create,delete static templates; do
make
done

正在加载...
取消
保存