GitHub Actions,臥槽!牛批

優質文章,第一時間送達!

GitHub Actions,卧槽!牛批

閱讀本文大概需要 19 分鐘。

現在 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,卧槽!牛批

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,卧槽!牛批

另外它支持三大平臺—— 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 文件,內容如下:

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

其實在這裡一個 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,卧槽!牛批

下面的 steps 就是具體執行哪些步驟了。第一步和第二步,我們可以看到它都有一個 uses 參數,內容都為 actions 開頭,這就說明我們使用了 GitHub 提供的寫好的 Action,我們只需要引用它的名字就能使用了。這兩步運行完畢之後,Python 環境會被初始化,同時會從 GitHub Clone Gerapy 項目代碼到本地。

在第三步和第四步,就是我自定義的 Task 了,這裡自持直接寫入 Shell 腳本。在這裡我分了兩步。

第三步 Install Dependencies 就是安裝 pip 和 Gerapy 安裝包,其中一句 <code>pip install ./<code>就是安裝當前 Gerapy 目錄下的內容到系統中,安裝完成之後,就可以使用 gerapy 命令了。

於是第四步 Run Gerapy 就是測試了 gerapy 命令的一些初始化使用,包括初始化工作環境、數據庫遷移、初始化賬號等等,當然還有更多,比如運行某些測試,運行服務等等,這裡我只把一些必要的內容寫進去了。

好,基本內容就是這樣。

保存這個 Action,命名為 build.yml,它會保存為 .github/workflows/build.yml 文件。同時在保存的時候,我們就相當於執行了一次 Push 任務,這時候我們就可以看到這個 Action 已經啟動了,頁面如下:

GitHub Actions,卧槽!牛批

我們所定義的每一個步驟以及對應的執行結果都會顯示在控制檯中,一目瞭然。

可以看到這裡初始化了三個版本的 Python 環境,同時都運行了其中的測試流程。如果測試成功,會打綠色的勾,如果失敗,會提示紅色的叉,並有郵件提示。

這樣以來,一些自動化的測試就完成了!!!

GitHub Actions,卧槽!牛批

同步文檔到新的 Repo

接下來我這個需求可以說稍微有點奇葩了。

寫項目免不了的要寫文檔,這裡文檔我是用 Sphinx 來寫的,可以藉助於 ReadTheDocs 自動構建並分發到 readthedocs.io 上面,類似這樣子:

GitHub Actions,卧槽!牛批

但文檔的源代碼我是放在了 Gerapy/Gerapy 這個 Repo 的 docs 文件夾,向 Scrapy 看齊,是這樣子的:

GitHub Actions,卧槽!牛批

但我想著還新建一個 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,卧槽!牛批

然後我們需要將私鑰上傳到 Action 所運行的虛擬機裡面,但我們又不能明文將其放在 yml 文件裡面,那這個怎麼做到呢?只需要將其配置到 Secrets 裡面即可,Action 是有權限訪問到的:

GitHub Actions,卧槽!牛批

嗯,做好這兩部分工作之後,接下來完善一下 yml 文件就好了,內容如下:

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

可以看到,這裡主要就分了兩步。

第一部分就是設置虛擬機的 SSH 環境,這裡 secrets.DOCS_DEPLOY_KEY 就是我們剛才在 Secrets 裡面定義的私鑰,對應的運行命令就是將私鑰添加到 ~/.ssh/id_rsa 裡面。

第二部分就是分離 docs 文件夾到新的分支,然後將其上傳到新的 Repo 下了。

GitHub Actions,卧槽!牛批

那麼這裡有兩條比較關鍵的命令:

<code>git subtree split --prefix=docs --squash -b docs/<code>

這條命令就是將 docs 文件夾的內容分離到一個新的分支的根目錄下,新的分支的名稱為 docs。

<code>git push /tmp/docs docs:docs/<code>

這條命令就是將本地的分支推送到另外一個本地 Repo 下,注意這裡 push 的目標不一定是遠端的 Repo 地址,也可以是本地的 Repo 地址。

最後,將新的 Repo 內容強制推送到遠程即可。

這樣我們就可以實現,Gerapy/Gerapy Repo docs 文件夾下內容的變動,會自動更新到 Gerapy/Docs Repo 了。

例如 docs 下是這樣的:

GitHub Actions,卧槽!牛批

Gerapy/Docs Repo 下和子文件的內容會一直維持同步,並在 master 分支上面:

GitHub Actions,卧槽!牛批

自動構建 Docker 鏡像

由於 Gerapy 是一個 Web 工程,所以它非常適合於打包一個 Docker 鏡像。對於 Docker 的鏡像,我期望有三個版本:

•當前 master 分支的版本,比較穩定,但未發佈版本。•最新版本,latest,代表最新的發佈版本。•每個歷史版本,每次發佈版本的版本號,都標記一個 tag。

最後我們自動構建的鏡像都自動 Push 到 Docker Hub 上面,這樣大家都可以使用了。

那這個怎麼做到呢,同樣藉助於 GitHub Action 也可以輕鬆做到。

首先 master 版本,由於沒有發版,所以前端需要自行 build,然後 Python Package 需要安裝本地代碼。廢話不多說了,上代碼:

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

可以看到這裡,監聽了 master 分支的變動,同時限定了路徑 workflows 文件夾和 gerapy 文件夾下變動。

流程包括了前端的構建和 Docker 的打包,Docker 打包的時候使用了 -f 命令指定了 Dockerfile 的路徑,並將打包完成之後的鏡像標記為 gerapy:master,推送到 Docker Hub 即可。

對於發佈新版本的時候,則直接監聽 tag 的變動即可:

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

可以看到這裡監聽的配置改成了 tags,tag 也變成了一個變量,可以通過 ${GITHUB_REF:11} 獲取到。

同時這裡還加了一個正則判斷是不是正式的發版,如果是 beta、rc 版本,則不構建正式 latest 的 Docker 鏡像。

最後我們看看我再一次發版之後,構建完成之後,Docker Hub 的效果:

GitHub Actions,卧槽!牛批

可以看到,我發佈了 0.9.2 版本之後,它就自動構建了 0.9.2 版本的鏡像,同時將 latest 鏡像指向 0.9.2 版本。另外對應 maser 版本也構建了一個版本。

這樣,以後媽媽再也不用擔心我忘記打 Docker 鏡像啦。

以上便是我將 GitHub Actions 應用到我的開源項目上的記錄。

GitHub Actions,卧槽!牛批

最後,如果大家對 Scrapy 爬蟲感興趣的話,也(非常)歡迎大家(高高興興的)瞭解一下我寫的 Gerapy 框架,利用它我們可以(無敵)更方便地管理(呀)、監控(呀)、(或者是)部署 Scrapy 爬蟲項目(什麼的)。

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

掰掰!

GitHub Actions,卧槽!牛批

好文章,我在看❤️


分享到:


相關文章: