Skip to content

Commit

Permalink
Switch to generating a static site
Browse files Browse the repository at this point in the history
  • Loading branch information
jamiefdhurst committed Feb 18, 2024
1 parent 71c8baf commit ef39de8
Show file tree
Hide file tree
Showing 17 changed files with 121 additions and 266 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ dist/
build/
*.egg-info/
node_modules/
tests/dist/
11 changes: 0 additions & 11 deletions Dockerfile

This file was deleted.

8 changes: 0 additions & 8 deletions Dockerfile.test

This file was deleted.

41 changes: 0 additions & 41 deletions Makefile

This file was deleted.

27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Blog

My blog website - built using Flask in Python. A standalone web app that
requires no separate database and is powered through Markdown articles.
My blog website - generated from Jinja templates and markdown articles as a
static site.

## Development

Expand All @@ -11,10 +11,10 @@ You will require Python 3. Run the following to install required modules:
python3 setup.py develop
```

You can then launch the project using:
You can then generate the site using:

```bash
FLASK_APP=blog flask run
python3 -m blog.generate
```

## Testing
Expand All @@ -27,17 +27,8 @@ pytest --verbose

## Build and Release

The Jenkins pipeline handles build and release. Upon the main branch being
updated, a new version of the blog will eb automatically released to GitHub and
the new version will be immediately deployed to the live website. The same
pipeline can be used to deploy other versions if required.

## Makefile

A Makefile has been added for convenience to run certain commands:

* `build` - build the Docker images (standard and test)
* `test` - run the tests after launching the test container
* `test-with-reports` - run the tests and output the required reports
* `run` - launch the blog container listening on port 5000
* `clean` - shut down and clean all running containers
The GitHub Actions pipeline handles testing, building and releasing the static
site version. Upon the main branch being updated, a new version of the blog will
be automatically released to GitHub and the new version will be immediately
deployed to the live website. The same pipeline can be used to deploy other
versions if required.
37 changes: 0 additions & 37 deletions blog/__init__.py
Original file line number Diff line number Diff line change
@@ -1,37 +0,0 @@
import os
from flask import Flask, g, render_template, url_for
from werkzeug.exceptions import HTTPException

def create_app(test_config=None):
app = Flask(__name__)
if test_config is None:
app.config.from_pyfile('config.py')
else:
app.config.from_mapping(test_config)

try:
os.makedirs(app.instance_path)
except OSError:
pass

@app.before_request
def load_version():
# pylint: disable=assigning-non-slot
g.version = app.config['VERSION']

from . import blog
app.register_blueprint(blog.bp)
app.add_url_rule('/', endpoint='index')

@app.errorhandler(Exception)
def handle_exception(err):
if isinstance(err, HTTPException):
return err

return render_template('500.html', err=err), 500

@app.errorhandler(404)
def page_not_found(err):
return render_template('404.html', err=err), 404

return app
3 changes: 0 additions & 3 deletions blog/articles.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from os import listdir
from os.path import isfile, join
import re
from flask import current_app
from markdown import markdown

class Article:
Expand Down Expand Up @@ -76,7 +75,6 @@ def get_paginated_articles(directory, page=1, per_page=10):
files = [f for f in listdir(directory) if isfile(join(directory, f))]
files.sort()
files.reverse()
current_app.logger.debug(f"Returned {len(files)} files...")
first_entry = (page - 1) * per_page
last_entry = first_entry + per_page
files = files[first_entry:last_entry]
Expand All @@ -86,7 +84,6 @@ def get_all_articles(directory):
files = [f for f in listdir(directory) if isfile(join(directory, f))]
files.sort()
files.reverse()
current_app.logger.debug(f"Returned {len(files)} files...")
return __parse_articles(directory, files)

def get_pages(directory, per_page=10):
Expand Down
59 changes: 0 additions & 59 deletions blog/blog.py

This file was deleted.

2 changes: 1 addition & 1 deletion blog/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from os import environ

SECRET_KEY = environ.get('SECRET_KEY', default='dev')
ARTICLES_DIR = environ.get('ARTICLES_DIR', default='articles/')
DIST_DIR = environ.get('DIST_DIR', default='dist/')
VERSION = 'v0.23.0'
71 changes: 71 additions & 0 deletions blog/generate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import os
import shutil
import sys
from jinja2 import Environment, PackageLoader, select_autoescape
from .articles import *
from .config import *

env = Environment(
loader=PackageLoader('blog'),
autoescape=select_autoescape()
)

def render_template(file, **kwargs):
template = env.get_template(file)
return template.render(**kwargs, version=VERSION)

def generate(articles_dir=ARTICLES_DIR, dist_dir=DIST_DIR):

# Create/clear dist folder
if os.path.isdir(dist_dir):
print('[INFO] Clearing existing output dir...')
shutil.rmtree(dist_dir)
print('[INFO] Creating output dir...')
os.mkdir(dist_dir)

# Get all articles and generate a page for each one
print('[INFO] Loading articles...')
items = get_all_articles(articles_dir)
print('[INFO] Loaded {} articles...'.format(len(items)))
for item in items:
print('[INFO] Rendering and writing {}...'.format(item.get_name()))
rendered = render_template('view.html', article=item)
with open(dist_dir + item.get_name() + '.html', 'w') as output_file:
output_file.write(rendered)

# Generate sitemap
print('[INFO] Rendering and writing sitemap...')
rendered = render_template('sitemap.xml', articles=items)
with open(dist_dir + 'sitemap.xml', 'w') as output_file:
output_file.write(rendered)

# Generate static pages (inc error pages)
for static_page in ['404', '500', 'now']:
print('[INFO] Rendering and writing {}...'.format(static_page))
rendered = render_template(static_page + '.html')
with open(dist_dir + static_page + '.html', 'w') as output_file:
output_file.write(rendered)

# Generate home page and paginated elements
pages = get_pages(articles_dir)
print('[INFO] Found {} pages of articles...'.format(pages))
for p in range(1, pages + 1):
print('[INFO] Rendering and writing index page {}...'.format(p))
paged_items = get_paginated_articles(articles_dir, p)
rendered = render_template(
'index.html',
articles=paged_items,
current_page=p,
pages=[None] * pages
)
with open(dist_dir + 'index.html' if p == 1 else dist_dir + 'index-{}.html'.format(p), 'w') as output_file:
output_file.write(rendered)

# Copy static assets
print('[INFO] Copying static assets...')
shutil.copytree('blog/static', dist_dir + 'static')

print('Blog {} generated and output to {}'.format(VERSION, dist_dir))

if __name__ == '__main__':
sys.exit(generate())
4 changes: 2 additions & 2 deletions blog/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<meta name="viewport" content="device-width" />

<meta name="description" content="Jamie Hurst - Software and System Engineer, Enthusiast of Terrible Cars" />
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/default.min.css') }}?v={{ g.version }}" />
<link rel="stylesheet" type="text/css" href="/static/css/default.min.css?v={{ version }}" />
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png" />
<link rel="shortcut icon" href="/static/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png" />
Expand All @@ -29,7 +29,7 @@
{% block content %}{% endblock %}
</div>
</main>
<footer role="contentinfo">&copy; Copyright Jamie Hurst 2021-2024<br />System {{ g.version }}</footer>
<footer role="contentinfo">&copy; Copyright Jamie Hurst 2021-2024<br />System {{ version }}</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-217639974-1"></script>
<script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-217639974-1');</script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
include_package_data=True,
zip_safe=False,
install_requires=[
'flask',
'jinja2',
'markdown',
],
)
24 changes: 0 additions & 24 deletions tests/conftest.py

This file was deleted.

Loading

0 comments on commit ef39de8

Please sign in to comment.