使用 GitHub-Workflow 进行前端项目部署
部署多个域名下的项目,具有以下步骤:
-
建立一个项目,并且把所有域名下的项目作为子库引入
这是我的项目结构
├── DockerFile
├── README.md
├── 子库 1
├── 子库 2
├── nginx.conf
├── package.json
└── script -
更新主库代码时,触发 github workflow 工作流
- 获取 master 分支代码
- 分别打包项目
- 生成镜像,并推送镜像到 Docker Hub
- ssh 登陆云服务器
- 拉取最新镜像,生成容器后启动
submodule
引入子库
git submodule add <project url> <project path>
ps: 可以使用以下指令删除
git rm --cached <project path>
以 prettier 项目为例,将项目引入项目 submodule 文件夹,且子库文件夹名称命名为 myPrettier,则指令为
# 新增
git submodule add git@github.com:prettier/eslint-plugin-prettier.git submodule/myPrettier
# 删除
git rm --cached submodule/myPrettier
workflow
github 会根据项目的 .github/workflows 下 yml 文件建立不同的流水线。故建立 .github/workflows/main.yml 文件
yml 文件格式如下
# 流水线名称
name: CI
# master 的 push 和 pull 操作会触发此流水线
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# 流水线执行步骤
jobs:
build:
# 流水线执行环境
runs-on: ubuntu-latest
steps:
# 使用 GitHub Actions 获取最新代码
- uses: actions/checkout@v2
# 执行 shell 命令
- name: Run a one-line script
run: echo Hello, world!
上述指令中,使用 github 提供的 action 可以直接获取最新的 master 分支代码,但无法获得子库代码。下述指令将带有子库 token 去请求 sufuwang/book.sufu.site 的 master 分支,并且将代码存储在执行机当前路径下的 book 文件夹中
在子库中生成 secret 并添加到主库中 Settings/Secrets/Action 中
- name: 获取 book 源码
uses: actions/checkout@v2
with:
ref: master
repository: sufuwang/book.sufu.site
token: ${{ secrets.SubmoduleToken }}
path: ./book
获取到所有子库代码后,开始进行打包、生成和推送镜像,这些操作可以写成脚本存储于主库中,流水线只需执行某个脚本即可。在此之前需要执行机登陆 docker 账户,与拉取子库一致,需要使用 token 进行登陆
- name: 登陆 docker 账户
uses: docker/login-action@v1
with:
username: sufuwang
password: ${{ secrets.DockerPassWord }}
- name: 执行脚本
env:
imageName: sufuwang/sufu.site
containerName: sufu.site.nginx
run: yarn run workflow:dev
上述步骤执行一个 yarn 命令,这个 yarn 命令对应执行某个脚本,并且为当前环境注入两个环境变量,用于镜像和容器命名
# 打包子库
mkdir dist
./script/build.sh -name book
./script/build.sh -name life
# 删除重名镜像和容器(流水线永远不会有重名镜像和容器)
docker rm -f $containerName
docker rmi $imageName
# 生成镜像
docker build -f ./DockerFile -t $imageName . --platform linux/amd64
# 推送镜像
docker login
docker push $imageName
上述脚本中的 build.sh 内容如下。需要提到的是,每个子项都需要使用统一的 yarn 命令进行打包
submodule_name=''
if [[ $1 == '-name' ]]
then
submodule_name=$2
fi
cd ./$submodule_name
yarn
yarn run build
mkdir ../dist/$submodule_name
cp -R ./dist/* ../dist/$submodule_name/
镜像推送到 Docker Hub 后,需要登陆服务器进行镜像更新,并启动容器
- name: 更新远端镜像
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSHHost }}
port: 22
username: ubuntu
password: ${{ secrets.SSHPassWord }}
script: |
sudo chmod 666 /var/run/docker.sock
docker rm sufu.site.nginx -f
docker rmi sufuwang/sufu.site
docker pull sufuwang/sufu.site
docker run -d -p 80:80 --name sufu.site.nginx sufuwang/sufu.site
所使用到的 DockerFile 如下
FROM nginx
WORKDIR /etc/nginx
COPY ./nginx.conf .
WORKDIR /usr/share/nginx/html/book
COPY ./dist/book .
WORKDIR /usr/share/nginx/html/life
COPY ./dist/life .
RUN ls
EXPOSE 80
所使用到的 nginx.conf 如下
user root;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
listen [::]:80;
server_name book.sufu.site;
location / {
root /usr/share/nginx/html/book;
index index.html;
}
}
server {
listen 80;
listen [::]:80;
server_name life.sufu.site;
location / {
root /usr/share/nginx/html/life;
index index.html;
}
}
server {
listen 80;
listen [::]:80;
server_name sufu.site;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}