I am creating a custom built static site to replace an old WordPress site for a client. The main aim is to make it easier to maintain and faster to load. Choosing Cloudflare pages is my default for static sites these days, and I wanted to be able to deploy the website with a simple git push. For a few other websites I have built this can all be done in the Cloudflare dashboard, but for this particular site the requirement was to build a series of HTML pages with a python script and jinja2 template, with the data coming from CSV files. This can obviously be done locally and then pushed to Github / Cloudflare, but I wanted to do this in one step, by running the python script on Github Actions. Many of the static site generators have their own pre-built actions, but I needed to create a custom one. This is also useful knowledge as it could be used to run any kind of build action post push to github, whether that is using python to generate HTML or something else.
Setup Github repo
The github repo is structured as follows, and all of the html files generated will get but in the ‘output’ folder (known as the build folder in Cloudflare pages.
├── custom-build-script.py ├── output │ └── custom.css ├── README.md ├── requirements.txt ├── source │ ├── menu_main.csv │ └── blog_pages.csv └── templates ├── base.html ├── menu.html └── blog.html
Create Cloudflare Pages Deployment
To setup the Cloudflare Pages Deployment, you can follow the instructions here (https://developers.cloudflare.com/pages/get-started/), which simply involves connecting your github account to Cloudflare and choosing the correct repo, branch and folder. You don’t need to specify any build commands to Cloudflare, as we’re going to use github actions for that part.
Add Custom Action to Github repo
In the github repo you need to add this file: .github/workflows/customaction.yml
(https://github.com/marketplace/actions/cloudflare-pages-github-action)
on: [push] jobs: build: runs-on: ubuntu-latest permissions: contents: write deployments: write steps: - name: checkout repo content uses: actions/checkout@v3 # checkout repo to the runner - name: setup python uses: actions/setup-python@v4 with: python-version: '3.10.6' - name: install python packages run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: execute py script run: python custom-build-script.py - name: commit files run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git add -A git commit -m "github action auto build" -a - name: push changes uses: ad-m/github-push-action@v0.6.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} branch: main - name: Publish to Cloudflare Pages uses: cloudflare/pages-action@v1 with: apiToken: ${{ secrets.CF_API_KEY}} accountId: ${{ secrets.CF_ACCOUNT_ID}} projectName: NAME_OF_PROJECT branch: main directory: output
Steps
Each of the steps are performed in a Github runner (docker on linux), and many have pre-set actions you can simply reference, (see ‘uses’) such as repo checkout, python setup and publish to Cloudflare. The other custom actions can be ‘run’ as by specifying command line options. Here that includes installing the python env, running the python script and committing the changes back to the repo. The key step is running the custom-build-script.py which builds the html. Check this post out for an interesting critique of Github Actions.
Build HTML with Python and Jinja2
Here is a simplified example of custom-build-script.py, along with the jinja2 templates and a CSV file containing the data.
custom-build-script.py:
import pandas as pd from jinja2 import Environment, FileSystemLoader import time file_loader = FileSystemLoader('templates') env = Environment(loader=file_loader) def create_pages(data_source, template_file, folder): template = env.get_template(template_file) pages = pd.read_csv(data_source, encoding='utf-8') for index, page in pages.iterrows(): output = template.render(data=page) output_filepath = 'output/' + folder + \ '/' + str(index + 1).zfill(2) + '.html' with open(output_filepath, "w") as fh: fh.write(output) create_pages('source/blog_pages.csv', 'blog.html', 'blog') # create blog
blog_pages.csv:
id,title,content,imgurl 1,Next Level Python,"If you want to level up your python, here are ten top things to learn",python.jpg 2,Code Editor Review,"Using a code editor you are comfortable with can dramatically improve your proficiency",eds.jpg 3,Top 5 AWS services to learn,"Here are the top 5 basic services which you should know on AWS",aws.jpg 4,Raspberry Pi Project List,"With Raspberry Pi, only your imagination is the limit",pi.jpg
blog.html: (note base.html not shown but contains opening html tags, header, css and meta data)
{% extends "base.html" %} {% block content %} <div class="jumbotron text-center"> <div class="h3"">{{data.title}}</div> </div> <div> <img src="{{data.imgurl}}"> </div> <div class="main-content"> {{data.content}} </div> {% endblock %}
So we’ve got a minimal example of creating a static website on Cloudflare pages using python and github actions to run the build. You could take this in other directions too, such as running the build on a schedule based on taking some external data and automatically building and commiting the changes.