Building & Publishing this blog

2022, May, 23

Side ProjectCode

UPDATE 2 : Now using Nuxt 3

This blog uses Gridsome framework based on the concept of JAMstack (Javascript-API-Markup).

Content for the blog is written in Markdown files. All the markdown files and web UI code are in separate private Github repos.

Goal was to create a simple, lightweight, DIY approach to automatically build and deployed this blog on server.

Advantages of using Github:

  • No need of dedicated Content Management System (CMS) : Markdown files can be created in any editor and pushed to Github.
  • Webhooks : Github provides webhooks for all repo.

UPDATE : now using Github Actions to build and deploy this blog. Following steps are no longer used.

nodejs app to receive push webhook request

Webhook request is POST call.

Github webhook request example can be found here.

const express = require('express');
const { exec } = require("child_process");
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const port = process.env.PORT
app.post('/pushed', (req, res) => {
if(req && req.body && req.body.repository) {
console.log('webhook request for ' + req.body.repository.full_name);
exec("python3 <SCRIPT_NAME>.py", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
res.send(JSON.stringify({repo: req.body.repository.full_name}));
} else {
res.send(JSON.stringify({}));
}
})
app.listen(port, () => {
console.log(`listening on port ${port}`)
})

python script to pull content & code from github, build and deploy

from datetime import datetime as dt
import subprocess
import os
import logging
logging.basicConfig(filename='<LOG_FILE_NAME>', level=logging.DEBUG)
PUSH_SERVER = os.getenv('PUSH_SERVER')
PUSH_TOKEN = os.getenv('PUSH_TOKEN')
GH_TOKEN = os.getenv('GH_TOKEN')
def push_msg(build_id, msg):
data = {
'title': 'blog-cd : {}'.format(build_id),
'message': msg,
'priority': 5
}
requests.post('{}/message?token={}'.format(PUSH_SERVER, PUSH_TOKEN), data=data)
def build_and_publish():
build_id = str(round(dt.utcnow().timestamp()))
START = dt.now()
push_msg(build_id, 'Started')
logging.info(build_id +': Build start')
subprocess.run(['mkdir', build_id])
os.chdir(build_id)
logging.info(build_id +': Cloning')
subprocess.run(['git', 'clone', 'https://<USERNAME>:{}@github.com/<USERNAME>/<REPO>.git'.format(GH_TOKEN)])
os.chdir('<REPO>')
logging.info(build_id +': Starting npm install')
subprocess.run(['npm', 'install'])
logging.info(build_id +': Starting build')
subprocess.run(['gridsome', 'build'])
logging.info(build_id +': Copying')
os.chdir('dist')
subprocess.run(['cp', '-r', '.', '<DESTINATION>'])
logging.info(build_id + ': clean up')
os.chdir('../..')
subprocess.run(['rm', '-rf', build_id])
END = dt.now()
duration = str(END - START)
push_msg(build_id, 'Done in ' + duration)
logging.info(build_id + ': DONE in ' + duration)
if __name__ == '__main__':
build_and_publish()

This could have been also written purely as a shell script, but I decided to go with python3.

Ensure that environment variables are added permanently. export from terminal only sets them for current session.

$ export KEY="value"

To permanently set them, added them to ~/.bashrc file.

Push notification on deployment (Optional)

Using Gotify to send push message on phone whenever the build is complete and site is deployed. Push messages are sent to Gotify server by push_msg function as REST call.