结合 GitHub Issues 与 VitePress 写博客

前一阵子一直在捣鼓 VitePress,想着之前看到有人用 Github Issues 来做静态页面的评论系统,于是就去了解了一下,目前这类项目有 gitalkgitment 以及 vssue。在这个过程中发现很早之前就有人开始用 Github Issues 来写博客。本人比较懒,我想着能不能把它和 VitePress 结合起来再通过 GitHub Pages 实现嫖上加嫖,后经过搜索也确实有人这么干。

仓库设置

首先,新建一个仓库,由于是用到 GitHub Pages,就将仓库名设置为 <yourname>.github.io,当然也可以随便命名,只是后面在 vitepress 部署 的时候会有所不同。

设置密钥

首先申请 Personal access token左上角头像 ->Settings-> 下拉找到 Developer settings->Personal access tokens->Generate new token 我这里申请的都是带有 classic 标识的 token,权限建议全选,时间设置无限制。点击 Generate token 之后页面中会出现一个 ghp_开头的字符串,复制保存好。 然后将 token 写入仓库密钥中,就在我们刚刚创建的仓库主页 左边 Settings->Secrets and variables->Actions->New repository secret 这里 name 自己设置,value 填写刚刚复制的字符串之后点击 Add secrets 就可以了。

设置变量

这边设置的变量主要是方便工作流中的判断,在刚刚创建仓库密钥的页面有个 variables 选项卡点击 New repository variable,name 自定义,value 就写你 github 的用户名,比如我的就是 ttdly,填好后点击 Add variable 就行。

Pages

由于后面会用到 GitHub Actions 进行 Pages 的发布,所以在仓库 Settings->Pages->Build and deployment->source 选择 Github Actions。

新建分支

创建一个分支,存储 md 文件,我这里就以 md 命名了,然后删除 main 分支中的文件,也就是说这个分支里什么文件都不要留,当然.git 文件夹不要删。清空之后,新建 posts 文件夹里面新建一个 placeholder 文件,什么都不要写。(主要是懒得做文件夹是否存在这个判断)最后将分支同步到 github 仓库里。

处理 Issues

把仓库设置好后,就可以开始编写 工作流程 以及代码来把你写好的 Issue 写入到 md 文档中然后上传回仓库了。

初始化

main 分支中,新建 .github/workflows/copyissue.yml

name: cpoyIssues
# 在新建、更新、重新开启、关闭 issues 的时候触发工作流程
on:
  issues:
    types:
      - "opened"
      - "edited"
      - "reopened"
      - "closed"
# 给予所有写权限,这个在后面上传文件的时候会用到。
permissions: write-all

过滤 issues

由于 Issues 谁都能来写,这又是个人博客,所以第一步就需要把不是自己开的 Issues 给过滤掉。而 Github REST API 提供了许多的接口,其中就有对 Issues 操作的接口,这里会用到 issues updateissues lock。因为 GitHub Action 能够直接执行命令,所以过滤 issues 直接用 curl 调用接口:

# .github/workflows/copyissue.yml
jobs:
  lockissue:
    runs-on: ubuntu-latest
    # vars.OWNER 中的 OWNER 是前面设置变量一节中设置的变量名
    if: ${{ github.event.action == 'opened' && github.actor != vars.OWNER }}
    steps:
      - name: Close and Lock Issues
        run: |
          curl -L \
            -X PATCH \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${{secrets.TOKEN}}"\
            -H "X-GitHub-Api-Version: 2022-11-28" \
            https://api.github.com/repos/${{github.repository}}/issues/${{github.event.issue.number}} \
            -d '{"state":"close","state_reason":"not_planned"}'
          curl -L \
            -X PUT \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${{secrets.TOKEN}}"\
            -H "X-GitHub-Api-Version: 2022-11-28" \
            https://api.github.com/repos/${{github.repository}}/issues/${{github.event.issue.number}}/lock \
            -d '{"lock_reason":"off-topic"}'

编写转换脚本

这一步的转换脚本用啥语言写都行,只要能正常请求网络接口,我这里用 js 演示。 首先切换到 md 分支,安装 vitepress,在 package.json 中的 scripts 加上一行 "write": "node ./<name>.js",在 ** 根目录 ** 新建 js 文件,名字随意,但是 wirte 好像不行。

// <name>.js
const fs = require('fs');
// 获取命令行传递的参数
const repo = process.argv [2],
    number = process.argv [3],
    token = process.argv [4];

const wirteD = function (response) {
// 把 labels 转成我想要的形式,不需要可以不写
    let tags = 'tags: ["';
    let labels = response.labels;
    let text;
    if (labels.length !== 0) {
        labels = labels.map(ele => ele.name);
        tags += labels.join('","');
        tags += '"]\n';
    } else {
        tags = '';
    }
// 拼接 frontmatter
    text = `---\ntitle: ${response.title}\ndate: ${response.created_at}\n${tags}---\n${response.body}`

    fs.writeFile(`./posts/${response.number}.md`, text, (err) => {
        if (err) {
            console.log("posts:", err);
            return
        }
        console.log("写入 posts 成功")
    });
    fs.writeFile(`./index.md`, text, (err) => {
        if (err) {
            console.log("index:", err);
            return
        }
        console.log("写入 index 成功")
    });

}
// 获取 issue 信息
const url = `https://api.github.com/repos/${repo}/issues/${number}`;
const headers = new Headers({
    'Accept': 'application/vnd.github+json',
    'X-GitHub-Api-Version': '2022-11-28',
    'Authorization': "Bearer" + token
});
fetch(url, {method: 'GET', headers}).then(res => res.json())
    .catch(error => console.error('Error:', error))
    .then(response => {
// 这里就是调用写入的函数了,你可以打印获取到的信息,决定怎么写这个文件
        wirteD(response);
    });

自动上传并且发布

这一步就要用到一些别人写好的 actions 了

# .github/workflows/copyissue.yml
# 这是 jobs 中的一个 job,注意缩进
issue2page:
  runs-on: ubuntu-latest
  if: ${{ github.actor == vars.OWNER && github.event.action != 'closed' }}
  steps:
    # ref:<name > 填写你存放 md 文件的分支
    - name: Checkout md
      uses: actions/checkout@v3
      with:
        ref: md
        fetch-depth: 0
    # 用的是 pnpm,这里安装 pnpm 但不安装依赖
    - uses: pnpm/action-setup@v2
      name: Install pnpm
      id: pnpm-install
      with:
        version: 7.27.0
        run_install: false
    # 获取 pnpm cache 存储路径
    - name: Get pnpm store directory
      id: pnpm-cache
      shell: bash
      run: |
        echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
    # 查找是否存在 cache,并且把 cache 还原,这个主要是加快依赖安装
    - uses: actions/cache@v3
      name: Setup pnpm cache
      with:
        path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
        key: ${{ runner.os }}-pnpm-store-${{ hashFiles ('**/pnpm-lock.yaml') }}
        restore-keys: |
          ${{ runner.os }}-pnpm-store-
    # 安装依赖
    - name: Install dependencies
      run: pnpm install
    # 执行写入脚本
    - name: Run Issue to Md Script
      run: pnpm run write ${{github.repository}} ${{github.event.issue.number}} ${{secrets.GITHUB_TOKEN}}
    # 存储修改
    - name: Commit files
      run: |
        git config --local user.email "github-actions [bot]@users.noreply.github.com"
        git config --local user.name "github-actions [bot]"
        git add .
        git commit -m "auto copy /posts/${{github.event.issue.number}}.md"
    # branch 是要上传 md 文件的分支
    - name: Push changes
      uses: ad-m/github-push-action@master
      with:
        branch: md
        github_token: ${{secrets.GITHUB_TOKEN}}

    - name: Build
      run: pnpm run build
    # 发布 GitHub Pages,VitePress 官方提供的方案
    - uses: actions/configure-pages@v2
    - uses: actions/upload-pages-artifact@v1
      with:
        path: ./.vitepress/dist
    - name: Deploy
      id: deployment
      uses: actions/deploy-pages@v1

完成

到这里不出意外,不管是新建 issue 还是修改 issue 都会自动更新文件,上传分支。想要在关闭 issues 的时候删除文件,可以自己尝试一下,我现在还没写,然后 pages 页面也会更新。

cd ..