使用 GitHub-Workflow 进行多个前端项目部署

387 阅读2分钟

使用 GitHub-Workflow 进行前端项目部署

部署多个域名下的项目,具有以下步骤:

  1. 建立一个项目,并且把所有域名下的项目作为子库引入

    这是我的项目结构

    ├── DockerFile
    ├── README.md
    ├── 子库 1
    ├── 子库 2
    ├── nginx.conf
    ├── package.json
    └── script

  2. 更新主库代码时,触发 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/workflowsyml 文件建立不同的流水线。故建立 .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.sitemaster 分支,并且将代码存储在执行机当前路径下的 book 文件夹中

如何添加 secret 到项目中?

在子库中生成 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;
    }
  }
}