[TOC]
【CICID】GitHub-Actions语法
1 场景
当我们开发过程中,经常需要提交代码,打包,部署新代码到对应的环境,整个过程都是人工手动操作,占据开发人员大量时间,并且很繁琐容易出错。所以需要借助一些CI/CD
工具让这整个流程自动化,节约生命。
一般公司都有自己的CICD工具或平台,但如何搭建一个属于自己的一套CICD工作流呢?
常见的CI/CD工具有Jenkins、GitLab、GitHub Actions等,各有优缺点,自行百度吧~
考虑代码都在GitHub托管,也不用额外购买服务器,最终选定GitHub Actions方式
附:推荐参考
- 如果公司已有完整的代码托管平台,希望尽量少改动已有的工作流程,选 Jenkins
- 如果是公司使用+新项目,选GitLab,自带代码托管和CICD
- 如果是个人项目,希望可以使用免费资源,选Github Actions
- 如果是个人项目+有自己服务器,也可以选GitLab
2 CI/CD
2.1 什么是CI/CD
-
CI/CD 属于DevOps,代表持续集成、持续部署
-
CI/CD自动化上将新代码从提交到生产,配置基础设施所需的大部分或全部手动人工干预流程
-
借助CI/CD,开发人员可以对代码进行更改,然后自动测试并推出,以进行持续部署
-
以实现停机时间最小化,代码发布速度更快
2.2 持续集成(CI)
持续集成是在提交或合并代码时,自动测试每个更改,并自动启动构建。可以大大减少开发和运维人员的重复工作,可以在软件开发生命周期的更早阶段更轻松的发现并修复错误和安全问题。
2.3 持续部署(CD)
持续交付是一种软件开发实践,一般与持续集成结合使用,以自动化基础设置供应和应用程序发布过程。
一旦代码作为 CI 流程的一部分进行了测试和构建,持续交付将在最后阶段接管,以确保可以随时部署并将部署所需要的环境打包在一起。
通过持续交付,可以随时将构建的软件部署到生产环境。可以手动触发部署,也可以进行自动化部署。
3 介绍
GitHub Actions 是一种持续集成和持续交付 (CI/CD) 平台,可用于自动执行生成、测试和部署管道。 您可以创建工作流程来构建和测试存储库的每个拉取请求,或将合并的拉取请求部署到生产环境。
3.1 优点
-
GitHub直接托管代码
-
Actions直接依赖于Github,不需额外安装任何东西
-
集成部署常用依赖库
-
支持Docker
-
不需要额外购买服务器搭建服务(重点!)
4 工作流常用语法
工作流文件使用 YAML 语法,并且必须具有 .yml
或 .yaml
文件扩展名。
4.1 name : 工作流名称
name: 这是工作流名称
4.2 on : 触发工作流时机
-
on.<event_name>.types
定义将触发工作流运行的事件活动类型
# 代码push 或者 fork等时触发 on: # 当向分支进行git push时触发该工作流 push: branches: - "main" # 当向分支合并分支时触发该工作流 pull_request: branches: - "main"
-
on.<push|pull_request>.<branches|tags>
定义事件筛选器,可让你更好地控制工作流的运行时间,可以指定分支和分支版本
name: MYCI on: push: branches: - master
-
on.<push|pull_request>.paths
使用
push
和pull_request
事件时,指定文件路径生效- 指定路径
on: push: paths: - '**.js'
-
排除路径
on: push: paths-ignore: - 'docs/**'
-
on.schedule
定义触发事件,使用 cron
on: schedule: # 每天 5:30 和 17:30 UTC 触发工作流程 - cron: '30 5,17 * * *' # 每周一至周四 5:30 - cron: '30 5 * * 1,3' - cron: '30 5 * * 2,4'
4.3 jobs : 作业
工作流运行由一个或多个 jobs
组成,默认情况下并行运行。
-
jobs.<job_id>
使用
jobs.<job_id>
为作业提供唯一标识符,是一个字符串jobs: my_first_job: name: My first job my_second_job: name: My second job
-
jobs.<job_id>.name
设置作业名称
-
jobs.<job_id>.needs
此作业之前必须成功完成的所有作业
-
要求成功的依赖项作业
按顺序执行 job1 -> job2 -> job3
jobs: job1: job2: # 依赖job1完成 needs: job1 job3: # 依赖job1和job2完成 needs: [job1, job2]
-
无论是否成功,都要执行
always()
按顺序执行 job1 -> job2 -> job3 ,但job3 无论是否成功,都会执行
jobs: job1: job2: needs: job1 job3: if: ${{ always() }} needs: [job1, job2]
-
-
jobs.<job_id>.runs_on
定义要运行环境
- 指定操作系统
runs-on: ubuntu-latest
-
公共免费GitHub托管的标准运行器
-
jobs.<job_id>.steps
作业包含一系列任务,称为
steps
步骤,步骤可以运行命令、运行设置任务,每个步骤在运行器环境中以其自己的进程运行,且可以访问工作区和文件系统,步骤之间不会保留环境变量的更改。-
jobs.<job_id>.steps[*].uses
选择要作为作业中步骤的一部分运行的操作,
jobs: my-jobname: steps: # checkout出v1版本代码 - uses: actions/checkout@v1
-
-
jobs.<job_id>.timeout-minutes
工作执行超时时间,超过则取消,默认 360分钟(6小时)
-
jobs.<job_id>.strategy
作业使用的矩阵策略,根据定义数组的笛卡尔积,创建作业
jobs: example_matrix: strategy: matrix: version: [10, 12] os: [ubuntu-latest, windows-latest]
根据上述矩阵,按顺序创建作业
{version: 10, os: ubuntu-latest}
{version: 10, os: windows-latest}
{version: 12, os: ubuntu-latest}
{version: 12, os: windows-latest}
-
jobs.<job_id>.container
注意:如果工作流使用 Docker 容器操作、作业容器或服务容器,则必须使用 Linux 运行器:
* 如果您要使用 GitHub 托管的运行器,则必须使用 Ubuntu 运行器。 * 如果您要使用自托管运行器,则必须使用 Linux 机器作为运行器,并且必须安装 Docker。
用于运行作业中尚未指定容器的任何步骤的容器
-
在容器中运行作业
name: CI on: push: branches: [ main ] jobs: container-test-job: # 运行的外部环境 runs-on: ubuntu-latest # 环境下运行的容器(Docker定义) container: # 容器中使用镜像 image: node:18 # 容器中定义环境 env: NODE_ENV: development # 容器中定义端口 ports: - 80 # 容器中定义映射位置 volumes: - my_docker_volume:/volume_mount options: --cpus 1 steps: - name: Check for dockerenv file run: (ls /.dockerenv && echo Found dockerenv) || (echo No dockerenv)
-
只指定容器映像时,可以忽略
image
关键词jobs: container-test-job: runs-on: ubuntu-latest container: node:18
-
-
jobs.<job_id>.services
注意:如果工作流使用 Docker 容器操作、作业容器或服务容器,则必须使用 Linux 运行器:
-
如果您要使用 GitHub 托管的运行器,则必须使用 Ubuntu 运行器。
-
如果您要使用自托管运行器,则必须使用 Linux 机器作为运行器,并且必须安装 Docker。
多个服务编排
services: # 1. nginx服务 nginx: image: nginx # ngxin 80 映射 docker 8080端口 ports: - 8080:80 # 2. redis服务 redis: image: redis # ngxin 6379 映射 docker 6379 ports: - 6379/tcp
-
-
单个事件
# 代码push on: push
-
多个事件
# 代码push 或者 fork on:[push,fork]
-
指定分支触发
name: MYCI on: push: branches: - master
5 环境变量
Github action 定义变量很多方式,很容易搞混,记录下常用方式
口诀:
-
普通使用
$xxx
, -
命令行或字符串拼接使用
${xxx}
, -
遇到
uses
则用${{xxxx}}
使用别人写好的Action,要用两个花括号
5.1 自定义变量
5.1.1 在env中定义变量
定义变量
jobs:
build:
runs-on: ubuntu-latest
env:
# 端口号
Port: 8080
# 云服务地址
Host_IP: ${{secrets.REMOTE_SERVER01_IP}}
# 云服务密码
Host_PWD: ${{secrets.REMOTE_SERVER01_PWD}}
# 账号
Host_Role: root
# 路径
Host_Dir: "/cicd"
变量取值
- 普通使用,
$xxx
# 测试环境变量
- name: Use variable from environment
run: |
echo "端口号:$Port"
echo "服务器地址: $Host_IP"
- 有命令行或拼接使用,
${xxxx}
# 上传新的docker镜像
- name: upload new docker image and start-up script
run:
sshpass -p ${Host_PWD} scp -r -o StrictHostKeyChecking=no ./${FILE_NAME}.tar ${Host_Role}@${Host_IP}:${Host_Dir}
-
当使用外部
uses
时,${{env.xxx}}
run
里面也要用${{env.xxx}}
# 加载tar文件,加载docker镜像
- name: Load new docker image
uses: matheusvanzan/sshpass-action@v2
with:
host: ${{env.Host_IP}}
user: ${{env.Host_Role}}
pass: ${{env.Host_PWD}}
run:
docker load -i ${{env.Host_Dir}}/${{env.FILE_NAME}}.tar
5.1.2 Run通过写入到github变量
在run里面定义了变量,需要给后面step
使用,一定要写入github环境中echo "key=$xxx" >> "$GITHUB_ENV"
定义
steps:
# 定义环境变量
- name: Define environment variables
run: |
# 定义全局变量
PROJECT_NAME=$(basename $GITHUB_REPOSITORY | tr '[:upper:]' '[:lower:]')
FILE_NAME=${PROJECT_NAME}-backend
VERSION=0.1
# 写入环境中
echo "PROJECT_NAME=$PROJECT_NAME" >> "$GITHUB_ENV"
echo "FILE_NAME=$FILE_NAME" >> "$GITHUB_ENV"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
取值
-
普通使用,
$xxx
# 测试环境变量 - name: Use variable from environment run: | echo "项目名称 (从环境变量): $PROJECT_NAME" echo "文件名称 (从环境变量): $FILE_NAME" echo "项目版本 (从环境变量): $VERSION"
-
有命令行或拼接使用,
${xxxx}
# 构建镜像 - name: Build docker image run: | docker build -t ${FILE_NAME}:${VERSION} . # 构建docker镜像,命令最后的点代表Dockerfile所在目录
-
当使用外部
uses
时,${{env.xxx}}
# 删除旧的docker镜像压缩包 - name: Delete old docker tar uses: matheusvanzan/sshpass-action@v2 with: host: ${{env.Host_IP}} user: ${{env.Host_Role}} pass: ${{env.Host_PWD}} run: | echo "Attempting to delete ${{env.Host_Dir}}/${{env.FILE_NAME}}.tar" rm -rf ${{env.Host_Dir}}/${{env.FILE_NAME}}.tar
5.2 默认环境变量
环境变量 | 说明 |
---|---|
CODESPACE_NAME |
代码空间的名称 例如,octocat-literate-space-parakeet-mld5 |
CODESPACES |
在代码空间中始终为 true |
GIT_COMMITTER_EMAIL |
未来 git 提交的“作者”字段的电子邮件。 |
GIT_COMMITTER_NAME |
未来 git 提交的“提交者”字段的名称。 |
GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN |
返回 GitHub Codespaces 转发端口的域。 例如 app.github.dev 。 |
GITHUB_API_URL |
返回 API URL。 例如 https://api.github.com 。 |
GITHUB_GRAPHQL_URL |
返回 GraphQL API URL。 例如 https://api.github.com/graphql 。 |
GITHUB_REPOSITORY |
所有者和仓库名称。 例如,octocat/Hello-World 。 |
GITHUB_SERVER_URL |
返回 GitHub 服务器的 URL。 例如,https://github.com 。 |
GITHUB_TOKEN |
代表代码空间中用户的签名身份验证令牌。 您可以使用它对 GitHub API 进行经过身份验证的调用。 有关详细信息,请参阅“GitHub Codespaces 中的安全性”。 |
GITHUB_USER |
启动代码空间的用户的名称。 例如,octocat 。 |
-
使用方式
-
项目名称:
${GITHUB_REPOSITORY}
-
分支名称:
${GITHUB_REF##*/}
-
refs/heads/main
->main
-
refs/tags/v1.0.0
->v1.0.0
-
示例
- name: Print Environment run: | echo $GITHUB_REPOSITORY # 仓库全名 echo ${GITHUB_REF##*/}
-
6 常用示例
-
定义环境变量
- name: Define environment variables run: | # 定义全局变量 # 当前项目名(小写) PROJECT_NAME=$(basename $GITHUB_REPOSITORY | tr '[:upper:]' '[:lower:]') # 文件名 FILE_NAME=${PROJECT_NAME}-backend # 常量 VERSION=0.1 # 写入环境中 echo "PROJECT_NAME=$PROJECT_NAME" >> "$GITHUB_ENV" echo "FILE_NAME=$FILE_NAME" >> "$GITHUB_ENV" echo "VERSION=$VERSION" >> "$GITHUB_ENV"
-
拉取代码
# 拉取最新代码 - name: Pull latest code uses: actions/checkout@v3 # 使用官方的checkout action,用于将仓库中的最新代码检查并拉取到工作目录中
-
连接远程服务器操作
# 连接远程服务器操作 - name: SSH Commonds uses: matheusvanzan/sshpass-action@v2 with: host: "远程主机IP" user: root pass: "远程主机密码" run: | # 命令行 pwd ls
-
传输文件
# 上传新的docker镜像 - name: upload new docker image and start-up script run: # sshpass -p 远程服务器密码 scp -r -o StrictHostKeyChecking=no 当前文件 账号@远程服务器地址:目标目录 sshpass -p ${Host_PWD} scp -r -o StrictHostKeyChecking=no ./${FILE_NAME}.tar ${Host_Role}@${Host_IP}:${Host_Dir}
7 参考资料
-
【CICD】github新功能actions全方位讲解!!
-
GitHub文档-Actions
https://docs.github.com/zh/actions/learn-github-actions/understanding-github-actions
-
GitHub Actions 的工作流语法
-
Github Actions 的一份不完全指南
遇到问题
-
run 报错: bash: -c: line 1: syntax error: unexpected end of file
如下图所示:
- name: xxxx with: run: | echo "只有一条命令会报错"
原因:run后面的
|
: 运行执行多个命令,但一条命令时会报错解决方法:
# 单条命令,去掉run后面的 `|` - name: xxxx with: run: echo "只有一条命令会报错" # 多条命令 - name: xxxx with: run: | echo "只有一条命令会报错" echo "两条命令就不会报语法错误"