所有程序员最痛恨的就是:重复的无脑的动作。所以还是交付给自动化进行处理吧。
持续集成指的是代码更改持续集成到仓库中,
cd是指让仓库的代码持续地交付到测试环境或者生产环境中。
用github actions自动集成和交付。
示例项目放在github中。
需要在项目中新建一个.github的文件夹。再在.github文件夹下新建一个workflows,就在这个目录下创建一个yaml文件,比如说test.yml文件。
需要在这个test.yml文件下写workflow包含的信息,比如说叫什么、怎样触发、做什么。
它要在什么时间做什么事情,
使用name给这个workflow起个名字,
// test.yml
name: 'Test'
on:
用on去表示这个workflow要在什么时候触发。触发的时候有很多类型。
比如提交push的时候,比如提交pr的pull_requests的时候,再比如说创建issues的时候,比如说定时触发schedule的时候:
// test.yml
name: "Test",
on:
push:
pull_reqeusts:
issues:
schedule:
基本上在github做的所有事情,都可以做成workflow,可以在文档中找到更多类型。
触发 workflow 的事件:docs.github.com/en/actions/…
在on写了多条时机的时候,就会触发workflow,考虑到我们这个workflow是用于测试代码,那我们先用push和pullRequest来看看。
但我们不会在任何分支在push或者提交pr的时候都执行这个workflow,我们只希望main分支会执行。所以我们要增加一个过滤条件。
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
如果我们只是希望main这个分支执行,我们就要增加一个过滤条件。
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
但对于pr来说,pr有好多种行为,可能是开启一个pr,或者关掉一个pr,只是想限定开启的时候,所以就要限定它的类型。限定它的类型是开启。
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
这样就完成了触发时机的设定。它可以是push到main分支的时候,也可以是对main分支开启一个pr的时候,只要满足其他一个条件,这个workflow就会被触发。
接下来就要定义这个workflow要做什么事,
需要一个关键字叫 jobs,一个job就是workflow要执行的一组任务,这个workflow要测试代码,所以我们的job就叫做test,
使用runs-on代表我们这个workflow需要在什么样的虚拟机上跑,在大部分情况下Ubuntu都是很好的选择,
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
但如果你有特殊需求也可以用MacOS或者windows。如果需要一些基本的环境,比如nodejs python go等等,可以实施一个额容器,这样即不用再在虚拟机上安装了,因为我这个是个python项目,所以用一个uv的container
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
接下来就要分步骤,说要怎样测代码
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
steps:
在我们本机测试代码,就是把代码拉下来,然后安装依赖跑测试。没问题的话,就合并了,所以第一个step就是获取代码,
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
steps:
name: Checkout
steps可以是一个命令,或者别人封好的workflow。
在github的市场可以看到很多,比如说这里的checkout,就是我们第一个step。
可以让我们获取代码。使用uses就可以用workflow了,
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
steps:
name: Checkout
uses: actions/checkout@v6
其实name这个属性可以不填,但是如果以后希望一看到就能读懂,最好还是写上去。这个地方的每一个step,都是一个列表的item,所以需要用列表形式:
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install Dependency
下一步是安装依赖,这次用的是run,而不是uses,表示我们要执行一个命令,而不是使用封装好的actions,所以我们用uv sync去安装依赖,如果是node的话,就用npmci,如果是go项目就用go mod download,总之随着你的项目来使用不同的命令。
下一步是测试代码,用pytest进行测试。
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install Dependency
run: uv sync
- name: Test
run: uv run pytest tests/
同样如果你是node项目就用npm test,如果是go,就用go test,总之什么项目用什么测试方法。这样就完成了一个job的定义。
实际上还可以写更多的job。比如说test2什么的。
name: "Test"
on:
push:
branches:
- main
pull_requests:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install Dependency
run: uv sync
- name: Test
run: uv run pytest tests/
test2:
如果说我们定义了多个job,那么这两个job会big你小姑娘运行,而不是按照先后顺序运行,但我们的需求是一个job就够了,但我们的需求是一个job就够了,
将这个workflow push到github仓库上去。
在vscode当中,可以用git的插件,commit上去 add github actions workflow for testing。
回到github页面看,
actions已经 有一个workflow。是刚刚运行有一个x表示run失败了。点进去看看,表示是pull reqeusts写错了。
name: "Test"
on:
push:
branches:
- main
pull_request:
branches:
- main
types: [ opened ]
jobs:
test:
runs-on: ubuntu-latest
container: astral/uv:python3.12-bookworm-slim
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install Dependency
run: uv sync
- name: Test
run: uv run pytest tests/
pull_request是没有s的。再次刷新。
再次失败,
没有搜到测试项目,因为在创建这个实例的时候,没有写测试项目,不重要,还是成功创建了一个workflow,
以上就是ci cd 中的ci。
cd
在workflows下创建deploy.yml,
// deploy.yml
name: "Deploy"
on:
on是触发时机,每当push或者pr的时候,不希望一有代码更改就马上部署,
// deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
可以将push的条件改成,帮我们push一个,以v开头的tag的时候,比如说v1.0,v2.0 这些push这样的一个tag就表示要发版了,
接着就是jobs。那么设置一个,
// deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
jobs:
Deploy:
runs-on: ubuntu-latest
如果是在本机的话,就先将代码拉下来放到服务器上,再到服务器上启动服务,先按流程来,这个时候我们就不需要使用容器了,因为我们并不需要运行什么东西,我们只是简单地把代码拉下来,然后放到服务器上,
// deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
如果对shell命令很熟的话,可以直接写scp命令,如果部署,还得查各种参数的用法,
所以去选择直接去市场找了一个action,
// deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Upload
uses: appleboy/scp-action@v1
with:
host:
username:
key:
appleboy/scp-action@v1 用的github上面的aciton,需要添加一些参数。
在仓库的settings,在下面的Secrets and variables里面的Actions,添加一个secret,定义名字和添加敏感信息:
// deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Upload
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username:
key:
secret 就有了,username和key也是一样的道理:
// deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Upload
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_KEY }}
接着还要写源位置和目标位置: // deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Upload
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_KEY }}
source: "./"
target: "/home/usr/projects"
表示要从现在的位置,搬到服务器上的位置。
最后在服务器上启动服务: // deploy.yml
name: "Deploy"
on:
push:
tag:
- v*
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Upload
uses: appleboy/scp-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_KEY }}
source: "./"
target: "/home/usr/projects"
- name: Start service
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_KEY }}
script: |
cd /home/usr/projects
python main.py
如果很熟悉shell命令,可以直接用run来写命令,这里用action去完成。
这样就是写好了一个cd的workflow。