尝鲜Github Action

BG76

Posted by Blue Geek on March 30, 2020

尝鲜Github Action

By 青衣极客 Blue geek In 2020-03-30

在软件的产品线中,常常有一个环节——持续集成(CI)。CI的作用是执行一些编译、测试、发布和部署这一类通用工作流程的,在工作划分清晰的团队中,通常是由QA来负责维护的。如果某次提交的代码无法正常通过CI,则QA有权禁止该代码合并到主干分支。由于CI的流程清晰,功能必要,以及多项目通用,因此有一些软件专门来做CI的自动化。对于个人或者小团队而言,维护一个CI自动化系统是繁琐而不必要的,因为Github所推出的Action功能能够帮助我们方便地执行CI,而且是免费的。

1. Github提供的资源

github在被微软收购之后,除了推出免费的私有仓库之外,还有提供了一个土豪的功能——Action。Action需要一台能够执行代码的计算机,Github就为每次执行Action功能提供一台虚拟机。

(1) 硬件资源

该虚拟机所具有的硬件资源如下:

A. 2核心的CPU
B. 7GB内存
C. 14GB的SSD

对于我们这样的小门小户而言,这样的资源完全够用。

(2) 操作系统

有多个操作系统开发经验的朋友基本会有一个共识,即最好开发用的系统与部署使用的系统一致或者至少类型一致。比如,在Windows下开发,然后在Linux系统中部署的话,就会产生很多的麻烦,稍不留神就会造成故障。Github提供了目前最常用的3种操作系统。

虚拟环境 YAML 工作流程标签
Windows Server 2019 windows-latest 或 windows-2019
Ubuntu 18.04 ubuntu-latest 或 ubuntu-18.04
Ubuntu 16.04 ubuntu-16.04
macOS Catalina 10.15 macos-latest or macos-10.15

YAML工作流程标签是可以在yaml配置文件中使用的符号,这里只需要知道Github Action可以提供Windows、Ubuntu和macOS三种操作系统的虚拟机。如果你的开发环境正在使用的操作系统是其中一种的话,可以选择对应的虚拟机操作系统。

(3) 使用限制

在使用Github Action时面临一些限制,虽然个人或者小团队用户基本不会超越,但我们仍然需要知道这一点。

A. 在workflow中的每个Job都不应超过6个小时,否则会因无法完成而失败。

B. 每个workflow执行实现不能超过72个小时。

C. 自托管Job最多只能排队24个小时。

D. 当前仓库的所有actions执行Github API的频率不能超过1000次/每小时。

E. 并行任务数量的上限见下表

GitHub 计划 同时运行的作业总数 MacOS 作业同时运行的最大数量
免费 20 5
Pro 40 5
团队 60 5
企业 180 50

F. 每个workflow的job数量不能超过256个。

2. Github Action的worflow语法简介

github的action功能有仓库根目录下的.github/workflows目录下的.yml后缀文件定义。每一个yaml文件定义一个workflow,一个workflow可以包含若干个job,每一个job可以包含若干个step,这些step会串行执行,每个step包含若干个action。

(1) name

name字段设置当前workflow的名字,默认情况下为文件名。

(2) on

on 字段用于指明当前workflow启动的时机,我们通常是需要在修改代码库时启动workflow,可以选择push或者pull_request。 例如:

on: [push, pull_request]

我们也可以设置地详细一点,比如master分支发生push事件时启动的配置如下:

on:
  push:
    branches:
      - master

(3) jobs

jobs 字段用于定义一个任务,如果这些任务存在依赖关系,可以使用need字段加以说明,例如:

jobs:
  a_job:
  b_job:
    need: a_job
  c_job:
    nedd: [a_job, b_job]

(4) run-on

run-on 字段用于指定虚拟机的类型,比如:

jobs:
  build:
    runs-on: macos-latest

就是指在“build”任务中使用“macOS”操作系统的虚拟机。

(5) steps

steps 字段用于定义一系列步骤,每一个步骤以“- name”开始定义该步骤的名字,随后可选择使用“uses”字段引用第三方action或者docker,使用“env”字段设置环境变量,使用“run”字段设置需要运行的指令。其中“uses”和“run”是二选一的必填字段。

(6) action

在“steps”中使用“uses”字段引用的就是action,该action可以自定义,也可以直接引用第三方的。通常我们都是引用github上已经写好的action。比如拉取当前的代码库的master分支的配置如下:

jobs:
  build:
    runs-on: macos-latest
    steps:
    - name: Checkout
          uses: actions/checkout@master

关于workflow配置的基本语法就讨论到这里,更加详细的说明和教程,可以参考github官网给出的文档。

3. 一个macOS编译C++程序的例子

这里展示一个在macOS虚拟机中编译C++程序的例子。

(1) C++源码

首先编写一个src/main.cpp文件:

#include <gflags/gflags.h>
#include <gtest/gtest.h>
#include <glog/logging.h>

using namespace std;

TEST(Hello, World) {
    cout << "HelloWorld" << endl;
}

int main(int argc, char ** argv) {
    google::ParseCommandLineFlags(&argc, &argv, true);
    google::InitGoogleLogging(argv[0]);
    google::SetStderrLogging(google::GLOG_INFO);
    
    int ret = 0;
    // run testcases
    testing::InitGoogleTest();
    ret = RUN_ALL_TESTS();
    google::ShutdownGoogleLogging();
    return ret;
}

在“src/main.cpp”文件中依赖了gflags、glog以及gtest三个库。

(2) 编写cmake文件

为了便于组织依赖关系,这里使用cmake来编译工程,CMakeLists.txt文件内容如下:

cmake_minimum_required(VERSION 2.8.12)
project(github_action)

# depend gflags
find_package(gflags REQUIRED)
message(STATUS "depend gflags include: ${gflags_INCLUDE_DIR}")
message(STATUS "depend gflags lib: ${gflags_LIB}")
include_directories(${gflags_INCLUDE_DIR})
set(gflags_LIB gflags)
set(LIBS ${LIBS} ${gflags_LIB})

#depend gtest
find_package(GTest REQUIRED)
message(STATUS "depend gtest include: ${GTEST_INCLUDE_DIR}")
message(STATUS "depend gtest lib: ${GTEST_BOTH_LIBRARIES}")
include_directories(${GTEST_INCLUDE_DIR})
set(LIBS ${LIBS} ${GTEST_BOTH_LIBRARIES})

#depend glog
find_package(Glog REQUIRED)
message(STATUS "depend glog include: ${GLOG_INCLUDE_DIRS}")
message(STATUS "depend glog lib: ${GLOG_LIBRARIES}")
set(GLOG_INCLUDE_DIRS ${GTEST_INCLUDE_DIR})
set(GLOG_LIBRARIES glog::glog)
include_directories(${GLOG_INCLUDE_DIRS}) 
set(LIBS ${LIBS} ${GLOG_LIBRARIES})

# depend protobuf
find_package(Protobuf REQUIRED)
if(PROTOBUF_FOUND)
    message(STATUS "protobuf library found")
else()
    message(FATAL_ERROR "protobuf library is needed but cant be found")
endif() 
include_directories(${PROTOBUF_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(LIBS ${LIBS} ${PROTOBUF_LIBRARIES})

# display depending libs
message(STATUS "depend libs: ${LIBS}")

# set include and compiler params
include_directories(include/)
add_definitions(-std=c++11)
add_definitions(-g)
add_definitions(-Wall)
set(CMAKE_C_COMPILER gcc)
#set(CMAKE_CXX_COMPILER g++)
message(STATUS "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")

# create executable files
aux_source_directory(./src DIR_SRC)

add_executable(${PROJECT_NAME} ${DIR_SRC} ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(${PROJECT_NAME} ${LIBS} ${EXT_LIBS})

(3) workflow配置

以上的源码和编译依赖都组织完毕,接下来就可以配置workflow了。编写“.github/workflows/main.yml”文件:

name: TestGithubActions
on: push
jobs:
  build:
    runs-on: macos-latest
    steps:
    - name: Installation depends
      run: |
        ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
        brew install protobuf
        brew install gflags
        brew install glog
        brew install tree
        git clone http://github.com/google/googletest
        cd googletest
        echo `cmake --version`
        mkdir build && cd build
        echo "running cmake ..."
        cmake .. -DCMAKE_CXX_STANDARD=11
        echo "running make ..."
        make
        echo "running install ..."
        make install
    - name: Checkout
      uses: actions/checkout@master

    - name: Check Source Code Dir
      run: |
        pwd
        tree .
    - name: Build
      run: |
        mkdir build && mkdir logdir && cd build
        cmake .. >> ../logdir/cmake.log
        make -j2 >> ../logdir/make.log
    - name: Run TestCases and Generate Report
      run: |
        ./build/github_action >> ./logdir/testcase_report.txt
        
    - name : Upload artifact
      uses: actions/upload-artifact@master
      with:
        name: TestcaseReport
        path: ./logdir

这段配置指定了如下的流程:(1) 当前workflow的名称为TestGithubActions,(2) 在push事件发生时触发,(3) 执行build任务,(4) build任务需要在“macOS”中执行,(5) 首先在虚拟机系统中安装依赖,(6) 拉取仓库源码,(7) 检查源码目录结构,(8) 编译并执行生成日志文件,(9) 将日志文件上传到本次workflow的主页。

将以上文件组成的仓库push到github上,即可在“Actions”标签下查看workflow的运行情况。

from IPython.display import Image
Image("img/bg76/output_10_0.png")

png

等到流程走完,就可以看到产出。

Image("img/bg76/results.png")

png

通常第一次尝试都没有那么顺利,大多会由于各种问题导致执行失败。可以根据页面中展示的失败步骤日志分析失败原因,然后修正再push。每次执行失败都会自动发送邮件到用户邮箱中。实验成功之后,就可以在自己的项目中使用Github Action功能了,配置好workflow之后就可以让开发人员专注于代码逻辑,而不必分心维护CI流程。

【青衣极客】公众号



COMMENT

博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题

"BG76-尝鲜Github Action"