GitHub Actions,卧槽!牛批!

阅读本文大概需要 19 分钟。

前段时间我更新了我的分布式爬虫管理框架—— Gerapy(话都说到这儿了打个广告,跟繁琐的命令行说拜拜!Gerapy分布式爬虫管理框架来袭!,哇,哇,就是,哇!)

现在 DevOps 的理念可谓是相当火,其中 CI/CD(持续集成、持续部署)是必不可少的环节。有了它们,我们开发完软件之后,一些测试、构建、部署的环节就可以自动化完成了。

我开发的的这款分布式爬虫管理框架—— Gerapy,代码也是放在了 GitHub 上面,但在之前 GitHub 上面是缺少原生的 CI/CD 功能支持的,可能需要根据第三工具或者 Webhook 等来配合实现项目的自动测试、构建和部署。

比如我可能有这么一些需求:

每次合并代码到 master 分支时,想测试这个项目能否在各个版本的 Python 环境下正常安装和运行。我为 Gerapy 新建了一个独立的 Repo,叫做 Gerapy/Gerapy,在 docs 文件夹下存放文档说明,但我还另外新建了一个 Repo 专门用来存放文档,叫做 Gerapy/Docs,希望能把 Gerapy/Gerapy 的 docs 子文件夹下的内容整个自动同步到 Gerapy/Docs 这个 Repo 的根目录。每次 Gerapy 发布新版本的时候,自动构建 Docker 镜像,并上传到 Docker Hub,打上 latest 标签和版本号标签。每次 master 分支提交代码的时候,自动构建 Docker 镜像,并上传到 Docker Hub,打上 master 标签,代表当前 master 分支版本。

上面的功能之前有一部分工作是手工操作的,有一部分是借助于第三方工具来自动操作的,感觉并不是一个很好的解决方案

在最近一段时间,GitHub 上面上线了 Actions 功能,它就是为 CI/CD 而生的,和 GitHub 项目原生紧密结合。然而几个月以来一直处于内测阶段。就在 11 月 13 日,GitHub Actions 功能正式上线了。

上线之后,我就开始正式使用这个功能了,是真的香!

上面的四个需求,我用 GitHub Actions 已经完全实现了自动化,非常简单方便。

接下来简单介绍下我的一些实现方式。

GitHub Actions,卧槽!牛批!_第1张图片

GitHub Actions

首先简单介绍下 GitHub Actions,其官方介绍页面为:https://github.com/features/actions,介绍语如下:

Automate your workflow from idea to production. GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. Make code reviews, branch management, and issue triaging work the way you want.

简而言之就是提供了一个高效易用的 CI/CD 工作流,帮助我们自动构建、测试、部署我们的代码。

GitHub Actions,卧槽!牛批!_第2张图片 另外它支持三大平台—— Linux、MacOS、Windows,支持任何编程语言,而且官方提供了许许多多的 Actions 库供我们直接使用,帮助我们更快地搭建工作流。

GitHub Actions 的官方文档可以见:https://help.github.com/en/actions/automating-your-workflow-with-github-actions,如果大家想好好研究下的话,一定要好好看看。

下面我就介绍我使用 GitHub Actions 实现上文所述的四个需求的方法。

自动测试

由于我开发的 Gerapy 是一个 Python Package,因此我看重的是测试它是否可以在各个 Python 平台下安装和正常使用,于是我新建了一个 GitHub Action,它会自动在项目目录下生成一个 .github/workflows/*.yml 文件,内容如下:

name: buildon:   push:    branches:     - master    - devjobs:  test:    runs-on:     - ubuntu-latest    strategy:      max-parallel: 3      matrix:        python-version: [3.5, 3.6, 3.7]    steps:    - uses: actions/checkout@v1    - name: Set up Python ${{ matrix.python-version }}      uses: actions/setup-python@v1      with:        python-version: ${{ matrix.python-version }}    - name: Install Dependencies      run: |        python -m pip install --upgrade pip        pip install .    - name: Run Gerapy      run: |        gerapy -v        gerapy init        cd gerapy        gerapy migrate        gerapy initadmin

其实在这里一个 Action 就是一个 YAML 文件,其后缀为 yml,它规定了一系列语法规则,我们根据它的语法规则写出一些工作流,在符合一定条件时,这些工作流会被触发,自动执行。

比如这里最开头,on 就是监听某个事件,其内容为 push,意思就是当 push 代码的时候,就会触发。再进一步地,这里定义了两个分支 master 和 dev。这什么意思呢?就是当我往 master 或者 dev 分支 push 代码的时候,我们定义的工作流就会执行。

下面的 jobs 就是工作流的定义了,包括在什么平台运行,具体执行什么步骤。

比如这里 runs-on 我就定义了在 ubuntu-latest 版本上运行,另外定义了一些并行策略和参数,比如这里就定义了 Python 的三个版本参数,在 3.5、3.6、3.7 版本上运行。

GitHub Actions,卧槽!牛批!_第3张图片

下面的 steps 就是具体执行哪些步骤了。第一步和第二步,我们可以看到它都有一个 uses 参数,内容都为 actions 开头,这就说明我们使用了 GitHub 提供的写好的 Action,我们只需要引用它的名字就能使用了。这两步运行完毕之后,Python 环境会被初始化,同时会从 GitHub Clone Gerapy 项目代码到本地。

在第三步和第四步,就是我自定义的 Task 了,这里自持直接写入 Shell 脚本。在这里我分了两步。

第三步 Install Dependencies 就是安装 pip 和 Gerapy 安装包,其中一句 pip install . 就是安装当前 Gerapy 目录下的内容到系统中,安装完成之后,就可以使用 gerapy 命令了。

于是第四步 Run Gerapy 就是测试了 gerapy 命令的一些初始化使用,包括初始化工作环境、数据库迁移、初始化账号等等,当然还有更多,比如运行某些测试,运行服务等等,这里我只把一些必要的内容写进去了。

好,基本内容就是这样。

保存这个 Action,命名为 build.yml,它会保存为 .github/workflows/build.yml 文件。同时在保存的时候,我们就相当于执行了一次 Push 任务,这时候我们就可以看到这个 Action 已经启动了,页面如下:

GitHub Actions,卧槽!牛批!_第4张图片

我们所定义的每一个步骤以及对应的执行结果都会显示在控制台中,一目了然。

可以看到这里初始化了三个版本的 Python 环境,同时都运行了其中的测试流程。如果测试成功,会打绿色的勾,如果失败,会提示红色的叉,并有邮件提示。

这样以来,一些自动化的测试就完成了!!!

GitHub Actions,卧槽!牛批!_第5张图片

同步文档到新的 Repo

接下来我这个需求可以说稍微有点奇葩了。

写项目免不了的要写文档,这里文档我是用 Sphinx 来写的,可以借助于 ReadTheDocs 自动构建并分发到 readthedocs.io 上面,类似这样子:

GitHub Actions,卧槽!牛批!_第6张图片

但文档的源代码我是放在了 Gerapy/Gerapy 这个 Repo 的 docs 文件夹,向 Scrapy 看齐,是这样子的:

GitHub Actions,卧槽!牛批!_第7张图片

但我想着还新建一个 Repo,来单独存放文档,比如我新建一个 Gerapy/Docs 这个 Repo,我在 Gerapy/Gerapy docs 子文件夹下的内容可以被自动同步到 Gerapy/Docs 根目录下面,这样我只需要往 Gerapy/Gerapy 上面提交代码,docs 子文件夹下面的内容变了,Gerapy/Docs 下面的内容也会跟着变。

那这个能不能做到呢?能!我问你答,快乐神仙;自问自答,法力无边~~

这个流程可以分为四步:

下载 Gerapy/Gerapy Repo 的源代码。利用 git 的 subtree 命令将 docs 文件夹下的内容分离到新的分支。将新分离的分支推送到 Docs 这个 Repo 下面。推送 Docs 这个 Repo 到远程 Gerapy/Docs Repo。

这里面就有一个关键地方,那就是怎样无需密码将内容推送到远程 Gerapy/Docs 这个 Repo 下面,当然就是 SSH 了。(啊,超爽der)

那 SSH 的话应该怎么设置呢?我们首先要有一对公钥和私钥,这个我们用 ssh-keygen 命令自己生成就好了。

那接下来 Gerapy/Docs 里面需要存有公钥,怎么办呢?我们可以借助于 GitHub 提供的 Deploy Key 配置好公钥即可:

GitHub Actions,卧槽!牛批!_第8张图片 然后我们需要将私钥上传到 Action 所运行的虚拟机里面,但我们又不能明文将其放在 yml 文件里面,那这个怎么做到呢? 只需要将其配置到 Secrets 里面即可,Action 是有权限访问到的: GitHub Actions,卧槽!牛批!_第9张图片

嗯,做好这两部分工作之后,接下来完善一下 yml 文件就好了,内容如下:

name: sync docson:   push:    branches:     - masterjobs:  sync:    runs-on: ubuntu-latest    steps:    - name: Set SSH Environment      env:        DOCS_DEPLOY_KEY: ${{ secrets.DOCS_DEPLOY_KEY }}      run: |        mkdir -p ~/.ssh/        echo "$DOCS_DEPLOY_KEY" > ~/.ssh/id_rsa        chmod 600 ~/.ssh/id_rsa        ssh-keyscan github.com > ~/.ssh/known_hosts        chmod 700 ~/.ssh && chmod 600 ~/.ssh/*        git config --global user.email "[email protected]"        git config --global user.name "Germey"    - name: Sync Docs of Gerapy      run: |        cd /tmp        git clone git@github.com:Gerapy/Docs.git docs        cd docs        git branch -D docs || true        git push origin --delete docs || true        git clone https://github.com/Gerapy/Gerapy.git gerapy        cd gerapy        git subtree split --prefix=docs --squash -b docs        git checkout docs        git push /tmp/docs docs:docs        cd /tmp/docs        git checkout docs        git checkout -b master || git checkout master || true        git reset --hard docs        git push origin master --force

可以看到,这里主要就分了两步。

第一部分就是设置虚拟机的 SSH 环境,这里 secrets.DOCS_DEPLOY_KEY 就是我们刚才在 Secrets 里面定义的私钥,对应的运行命令就是将私钥添加到 ~/.ssh/id_rsa 里面。

第二部分就是分离 docs 文件夹到新的分支,然后将其上传到新的 Repo 下了。

GitHub Actions,卧槽!牛批!_第10张图片

那么这里有两条比较关键的命令:

git subtree split --prefix=docs --squash -b docs

这条命令就是将 docs 文件夹的内容分离到一个新的分支的根目录下,新的分支的名称为 docs。

git push /tmp/docs docs:docs

这条命令就是将本地的分支推送到另外一个本地 Repo 下,注意这里 push 的目标不一定是远端的 Repo 地址,也可以是本地的 Repo 地址。

最后,将新的 Repo 内容强制推送到远程即可。

这样我们就可以实现,Gerapy/Gerapy Repo docs 文件夹下内容的变动,会自动更新到 Gerapy/Docs Repo 了。

例如 docs 下是这样的:

GitHub Actions,卧槽!牛批!_第11张图片

Gerapy/Docs Repo 下和子文件的内容会一直维持同步,并在 master 分支上面:

GitHub Actions,卧槽!牛批!_第12张图片

自动构建 Docker 镜像

由于 Gerapy 是一个 Web 工程,所以它非常适合于打包一个 Docker 镜像。对于 Docker 的镜像,我期望有三个版本:

当前 master 分支的版本,比较稳定,但未发布版本。最新版本,latest,代表最新的发布版本。每个历史版本,每次发布版本的版本号,都标记一个 tag。

最后我们自动构建的镜像都自动 Push 到 Docker Hub 上面,这样大家都可以使用了。

那这个怎么做到呢,同样借助于 GitHub Action 也可以轻松做到。

首先 master 版本,由于没有发版,所以前端需要自行 build,然后 Python Package 需要安装本地代码。废话不多说了,上代码:

name: build docker image masteron:  push:    branches:     - master    paths:    - .github/workflows/**    - gerapy/**jobs:  build:    runs-on: ubuntu-latest    steps:    - name: Checkout Source      uses: actions/checkout@v1    - name: Docker Login      run: docker login -u germey -p ${{ secrets.DOCKERHUB_LOGIN_PASSWORD }}    - name: Setup Node.js      uses: actions/setup-node@v1.1.0      with:        version: 10.x    - name: Build Frontend Source      run: |        cd gerapy/client        npm install        npm run build    - name: Build the Docker Image      run: |        docker build -t germey/gerapy:master -f ./docker/Dockerfile .    - name: Push the Docker Image      run: docker push germey/gerapy:master

可以看到这里,监听了 master 分支的变动,同时限定了路径 workflows 文件夹和 gerapy 文件夹下变动。

流程包括了前端的构建和 Docker 的打包,Docker 打包的时候使用了 -f 命令指定了 Dockerfile 的路径,并将打包完成之后的镜像标记为 gerapy:master,推送到 Docker Hub 即可。

对于发布新版本的时候,则直接监听 tag 的变动即可:

name: build docker image releaseon:  push:    tags:      - 'v*.*.*'jobs:  build:    runs-on: ubuntu-latest    steps:    - name: Checkout Source      uses: actions/checkout@v1    - name: Docker Login      run: docker login -u germey -p ${{ secrets.DOCKERHUB_LOGIN_PASSWORD }}    - name: Setup Node.js      uses: actions/setup-node@v1.1.0      with:        version: 10.x    - name: Build Frontend Source      run: |        cd gerapy/client        npm install        npm run build    - name: Build and Push the Docker Image      run: |        tag=${GITHUB_REF:11}        echo "Build Tag '$tag'"        docker build -t germey/gerapy:$tag -f ./docker/Dockerfile .        docker push germey/gerapy:$tag        regex='^([0-9]+\.){0,2}(\*|[0-9]+)$'        if [[ $tag =~ $regex ]]; then          echo "Build Stable Version '$tag'"          docker tag germey/gerapy:$tag germey/gerapy:latest          docker push germey/gerapy:latest        fi

可以看到这里监听的配置改成了 tags,tag 也变成了一个变量,可以通过 ${GITHUB_REF:11} 获取到。

同时这里还加了一个正则判断是不是正式的发版,如果是 beta、rc 版本,则不构建正式 latest 的 Docker 镜像。

最后我们看看我再一次发版之后,构建完成之后,Docker Hub 的效果:

GitHub Actions,卧槽!牛批!_第13张图片

可以看到,我发布了 0.9.2 版本之后,它就自动构建了 0.9.2 版本的镜像,同时将 latest 镜像指向 0.9.2 版本。另外对应 maser 版本也构建了一个版本。

这样,以后妈妈再也不用担心我忘记打 Docker 镜像啦。

以上便是我将 GitHub Actions 应用到我的开源项目上的记录。

GitHub Actions,卧槽!牛批!_第14张图片

最后,如果大家对 Scrapy 爬虫感兴趣的话,也(非常)欢迎大家(高高兴兴的)了解一下我写的 Gerapy 框架,利用它我们可以(无敌)更方便地管理(呀)、监控(呀)、(或者是)部署 Scrapy 爬虫项目(什么的)。

其 GitHub 地址为:https://github.com/Gerapy/Gerapy,文档:https://docs.gerapy.com/。

掰掰!

GitHub Actions,卧槽!牛批!_第15张图片

推荐阅读

1

Python 中更优雅的日志记录方案

2

别再造假数据了,来试试 Faker 这个库吧!

3

200 行代码实现一个滑动验证码

4

如何用一条命令将网页转成电脑 App

好文和朋友一起看~

你可能感兴趣的:(GitHub Actions,卧槽!牛批!)