Building GitHub Action to publish Hexo post from GitHub Issue

I wanted to write and publish a Hexo post without tedious commands such as git clone, git pull, hexo new, hexo generate, git commit and git push. I saw the feasibility to build an automated publishing system using GitHub Issue and GitHub Actions for this purpose.

Here’s workflow what I thought:

  • A user makes an issue, a draft of a post on GitHub Issue
  • GitHub Workflow is triggered against the issue
  • The workflow converts the issue to a Hexo post

GitHub Issue

I thought that GitHub Issue is a perfect system to draft a post. It has a title, a content and a date/time of update and those information can be a title, a content and a date/time of a post. One thing missing is tags of a post. To achieve this, I got an idea to use labels of an issue as tags of a post.

GitHub Action

I did a research whether there exists GitHub Actions converting an issue to a Hexo post, but I found nothing. So I decided to build a GitHub Action for it.

First of all, it takes two parameters, issue_url and token and extracts endpoint from issue_url:

1
2
3
4
5
6
import * as core from '@actions/core';

const issueUrl = core.getInput('issue_url');
const index = issueUrl.indexOf('/repos');
const endpoint = issueUrl.substring(index);
const token = core.getInput('token');

And then, it initializes Hexo:

1
2
3
4
5
6
7
8
import Hexo from 'hexo';

const hexo = new Hexo(process.cwd(), {});
hexo.init().then(() => {
// Described below
}).catch((reason) => {
core.setFailed(reason);
});

After Hexo is initialized, it retrieves information of an issue:

1
2
3
4
5
6
7
8
9
10
11
12
import { Octokit } from 'octokit';

// After resolving `hexo.init()`
const gh = new Octokit({ auth: token });

console.log(`Converting issue ${endpoint} to Hexo post...`);
gh.request(`GET ${endpoint}`).then((response) => {
const { title, updated_at: date, labels, milestone, body: content } = response.data;
// Described below
}).catch((reason) => {
core.setFailed(reason);
});

If a draft is set to be published, it derives tags and creates a Hexo post:

1
2
3
4
5
6
7
8
9
10
11
12
13
const MILESTONE_PUBLISH = 'publish';

if (milestone.title !== MILESTONE_PUBLISH) {
console.log(`Issue does not have milestone ${MILESTONE_PUBLISH}`);
} else {
const tags = labels.map((label: any) => label.name);
hexo.post.create({
title,
date,
tags,
content,
} as any);
}

At this point, a markdown file is generated and further actions to be prepared to complete publishing a post. The complete implementation of this action can be found here.

Workflow

Now a complete workflow needs to be setup. The workflow below converts an issue to a post and pushes it to the repository:

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
name: Issue to Hexo
on:
issues:
# Sufficient to trigger this workflow when an issue is milestoned
types: [ milestoned ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
with:
persist-credentials: 'false'
- uses: gsongsong/[email protected]
with:
issue_url: ${{ github.event.issue.url }}
# Personal access token used to get information of Issue
token: ${{ secrets.token }}
# At this point, a markdown file is generated and untracked
# Take further action, e.g. generate (`hexo generate`), commit and push
- name: Commit post
run: |
git add .
git config user.name "issue-to-hexo bot"
git config.user.email "<>"
git commit -m "Add a post"
- name: Push
uses: ad-m/[email protected]
with:
github_token: ${{ secrets.token }}

I have another workflow which generates (hexo generate) a blog from the pushed code. I made two separate workflows because I both use GitHub Issue to draft a post and manually write and push a markdown draft on my PC. Here’s the workflow:

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
name: Publish
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
with:
persist-credentials: 'false'
submodules: 'true'
- uses: actions/[email protected]
with:
node-version: '14'
# This submodule is a directory that contains generated Hexo blog
- name: Checkout submodule
run: |
cd public
git checkout master
- name: Build
run: |
npm install
npm run build
- name: Commit submodule
run: |
cd public
git add .
git config user.name "gsongsong/hexo-src bot"
git config user.email "<>"
git commit -m "Publish"
- name: Push submodule
uses: ad-m/[email protected]
with:
github_token: ${{ secrets.token }}
directory: 'public'
repository: 'gsongsong/gsongsong.github.io'
# These actions commits and pushes the submodule to the main module
- name: Commit main module
run: |
git add .
git config user.name "gsongsong/hexo-src bot"
git config user.email "<>"
git commit -m "Publish"
- name: Push main module
uses: ad-m/[email protected]