面向白嫖(Baipiao-oriented)的DevOps:将定时更新任务扔给Github Actions去做

2021/08/13 18:29 下午 posted in  技术 comments

最近有个需求,由于最近在某平台更新内容,其提供的订阅机制为RSS订阅分发,不过其中带有的一些额外的信息有点尴尬,并不能直接发送到其他同类型内容平台分发,因此想到了通过程序先将不适宜信息处理后,定期更新RSS文件到自己的Github Pages上,然后其他同类型内容平台获取的RSS为自己的Github Pages上托管的RSS文件。

于是,需求比较明确了:

  • 可以托管并执行程序,不仅可以获取RSS文件内容,又可以通过程序修改;
  • 可以定时更新任务,并且根据更新任务内容push到仓库指定分支。

我第一时间想起了Github Actions这个工具,众所周知,这个工具自2019年内测到向公众开放后,众多从业者将自己的博客的编译工作从本地线下扔到了Github Actions上;一些从业者所写的前端小玩意儿,都可以很轻松的通过Github Actions将工程迅速部署,提高了效率以及节约了成本。甚至一些有后端的工程,只需要寻(bai)找(piao)一个后端存储数据的地儿,就可以低成本甚至零成本搭建个人主页,这简直是一个十分有效提高生产率的东西啊!

不过说起来,其实Github Actions与Jenkins的功能是一致的,都是一个CI/CD的DevOps生产力工具,只不过Github Actions以其简单易用一经推出就受到了十分的欢迎。

虽然一直有知道这个功能,但是苦于没什么场景可以使用,今天这个需求正好让我能够使用Github Actions,零成本的解决一下需求。

首先,准备一下更新RSS的主程序convertRSS.pyrequirements.txt

import requests
import os, sys
import argparse

# the main get content codes
def get_url(url):
    # some server requests need headers, otherwise return empty
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
    }
    page = requests.get(url, headers = headers).content
    # convert to str within utf-8 encoding
    page = page.decode('utf-8')
    return page

# the main convert codes
def convert_content(content):
    content = content.replace("ZNing源创库","张宁网·源创库")
    return content

if __name__ == '__main__':
    # need system args input
    arg_prsr = argparse.ArgumentParser()
    arg_prsr.add_argument('--url', required=True, type=str, default="", help = 'Required. Which one RSS Xml do you want to convert.')
    args = arg_prsr.parse_args(sys.argv[1:])
    url = args.url

    # place a flag to judge should write file to disk or not
    flag = False

    url_content = convert_content(get_url(url))
    url_list = url.split("/")

    if os.path.exists(url_list[-1]):
        with open(url_list[-1], 'r', encoding='utf-8') as file:
            if(file.read()!=url_content):
                flag = True
    else:
        # if the file dose not exist, the flag should placed True to convert first time
        flag = True

    if flag:
        # writing to disk codes
        with open(url_list[-1], 'w', encoding='utf-8') as file:
            file.write(url_content)
        print("The RSS Xml had update, convert done.")
        sys.exit(0)
    else:
        print("The RSS Xml have no change, convert abort.")
        sys.exit(1)

这里解释下我的程序的逻辑:通过系统执行传参--url将需要处理的RSS XML URL传值进入,后通过函数get_url()完成源文件的获取并转换成字符串,紧接着通过convert_content()函数处理替换源文件字符串。完成后紧接着判断本地是否有该文件,如果没有则直接创建,如果有则判断本地文件是否与处理替换后文件内容是否相同,如果不相同则覆盖修改本地文件完成更新,并以0返回值正常退出,如果相同则无需更新,以1返回值返回退出程序。

至于为什么设计这两个返回值,文末会讲到。

下面是该程序的requirements.txt,显然得,不再赘述:

requests==2.25.1

之后Github Actions创建一个仓库,把这俩文件放进去。

另外是需要去Settings-Developer settings-Personal access tokens(链接:https://github.com/settings/tokens)里面生成一个密钥,将这个密钥添加到仓库设置的Actions secrets(链接:https://github.com/``{username}/``{reponame}/settings/secrets/actions),在Actions secrets的名字即是配置文件引入的变量名,这里我的仓库叫CONVERT_TOKEN

FireShot Capture 016 - New personal access token - github.com

FireShot Capture 017 - Actions secrets - github.com

文件和密钥均备妥后,点击菜单标签Actions,后点击set up a workflow yourself,进入创建自己的工作流。

工作流创建文件使用的是YAML语法,具体使用文档可以查阅:https://docs.github.com/cn/actions 这里不再赘述更多用法,直接贴出本示例项目创建的工作流文件convert-rss.yml

name: 'Convert RSS Actions'

on:
  schedule:
    - cron: '0,15,30,45 * * * *'

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout from repo
      uses: actions/[email protected]
      with:
        ref: main

    - name: Install Python latest
      uses: actions/[email protected]
      with:
        python-version: '3.x'
        architecture: 'x64'

    - name: Install dependencies
      run: |
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    - name: Doing fetch
      run: python convertRSS.py --url https://zning.me/atom.xml

    - name: Commit files
      run: |
        git config --local user.email "[email protected]"
        git config --local user.name "ZNing"
        git add .
        git commit -m "提交RSS XML更新 $(date "+%Y-%m-%d %H:%M:%S")"
    - name: Push changes
      uses: ad-m/[email protected]
      with:
        github_token: ${{ secrets.CONVERT_TOKEN }}
        branch: main

Github会在该分支仓库里创建.github/workflow文件夹,并在里面创建工作流文件convert-rss.yml,文件名没有任何要求,Github Actions会对该文件夹下所有.yml进行解析与执行。

这里稍微解释一下我自己这个工作流文件所干的事情:

  • 工作流名称为Convert RSS Actions
  • 工作流为定时计划工作流,工作时间是每隔15分钟一次;
  • 工作流build有6个任务,顺序执行,每个步骤的具体是:
    1. 检出main仓库;
    2. 安装最新版Python 3;
    3. 根据仓库是否有requirements.txt安装Python依赖;
    4. 执行Python程序,对RSS XML进行处理;
    5. 对更新的RSS XML进行提交;
    6. 对更新的RSS XML进行推送至指定分支。

FireShot Capture 009 - Actions · zning1994_github-actions-examples - github.com

FireShot Capture 012 - New File - github.com

创建好后,就等着Github Actions自己按计划调起Actions工作即可,不过根据实际测试,由于这玩意儿全球从业者估计使用的都挺多的,所以实际执行时间并不会完全按照设定的时间走,一般会晚个8-12分钟。

仓库所有内容其实都可以本地创建,其中Actions的定义文件在本地创建.github/workflow文件夹后放置提交推送给Github也是可以的,只不过我这边为了演示方便,就不再在本地做操作了,直接在Github网页上直接将所有步骤都解决了。

FireShot Capture 019 - Actions · zning1994_github-actions-examples - github.com

FireShot Capture 020 - Update convert-rss.yml · zning1994_github-actions-examples@b2e36a9_ - github.com

这里解释一下刚才Python返回值的问题,因为我需要区分两种状态情况,而Github Actions是可以根据返回值判断该节点运行正常与否(详见:https://docs.github.com/cn/actions/creating-actions/setting-exit-codes-for-actions)只要是0就认为是正常,非0则是异常,且异常不会继续执行接下来的节点。

而我的需求其实就正好是,如果有更新则提交并推送仓库,如果没有则终止即可。因此上面的Python程序才会设置如此的返回值。

不过可能是我Python程序有点小问题,发现如果内容相同的时候,后台程序也会提交更新,但是这个情况出现的很随机,有的时候又比对相同1值退出,正常返回错误。目前不太清楚这是为什么,欢迎知道的大佬在评论区留言。

FireShot Capture 022 - 提交RSS XML更新 2021-08-13 07_50_01 · zning1994_github-actions-examples@e_ - github.com

FireShot Capture 022 - 提交RSS XML更新 2021-08-13 07_50_01 · zning1994_github-actions-examples@e_ - github.com -2-

最后,本文示例程序正常开源,协议MIT,欢迎自取使用:

Github:https://github.com/zning1994/github-actions-examples

Gitee镜像:https://gitee.com/zning/github-actions-examples