使用Github Actions打包前端项目并自动上传到Releases

前言

应公司业务需求,最近在写一个谷歌插件,我本来想手动build后将dist目录打包成zip手动Releases一下,但leader说这样太浪费时间了,让我用github的Actions来做,我也是头一回接触这个,两眼一抹黑,花了点时间了解后,成功实现了该功能,可以理解为actions就是预先定义的操作步骤,将多个步骤组合成一个workflows工作流,每次通过触发条件触发这个工作流,有点类似CI/CD的感觉。

教程

创建workflows

首先我们需要在项目根目录创建文件夹:

.github
  └─ workflows

创建一个.github目录,在该目录下再创建一个workflows目录,之后所有的工作流文件.yml都存放在workflows这个目录下。

创建yml文件

文件名是随意的,比如我这次是处理Releases的,所以我可以创建一个releases.yml文件。

├─ .github
│  └─ workflows
│     └─ releases.yml

vscode 插件安装

建议安装下github官方插件: GitHub Actions

有了这个插件能在编写yml文件时有代码高亮和语法提示,本来官方还可以查看工作流的状态的,但是我实际使用发现一直在加载数据中,不知道什么原因。

配置工作流名称和触发条件

releases.yml

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

yml文件中,代码的缩进是非常重要的,不同的缩进长度表示的层级就不同,根就是0缩进,比如这里的name名称和on监听触发条件,都是根上的。

我的需求是当代码push到main主分支或者手动触发时才会调用这个工作流,所以这里on监听的就是push条件,分支为main,如果你有多个分支可以自动手动增加。

注意:合并分支也是push操作哦

增加工作jobs

一个yml文件可以配置很多个job工作,而工作下面就是具体的操作步骤,而管理所有job的入口,也就是父级,就是jobs。

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  job1: 
  job2:

增加build的job工作

我们增加一个打包项目的工作,但是在打包时我们需要一个代码的运行环境,我们采用github官方提供的:ubuntu-latest系统,你可以理解为这个系统就是docker的系统,只不过github官方给你提供了这个镜像,当然你也可以自己上传,但是这个就跟本次教程无关了。

官方提供的镜像一般都会预装一些基础环境依赖,但是不可能所有要用到的依赖都会安装,比如node它就默认没有,如果你想了解官方的镜像有哪些,可以打开下面这个链接:

你可以看到针对开源的仓库和私有的仓库,镜像和对应的资源是略有不同的,开源的仓库硬件会更强一些,并且除了linux还有windows和mac环境。

如果你想了解对应的镜像里面有哪些预设依赖,可以查看这个仓库:

点击对应的Included Software列下面的链接,可以查看对应镜像的依赖列表。

了解完镜像后我们通过runs-on来配置需要的镜像

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

前端用ubuntu-latest就行了

创建步骤

工作创建好后还需要有相应的操作步骤来处理具体的事情,每个工作都可以有很多步骤,所以需要一个统一的入口来进行管理。

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - 具体步骤1
      - 具体步骤2
      ...

步骤:克隆仓库代码

现在linux环境有了,我们还需要把当前仓库的代码clone到这个镜像里面去,如果仓库是一个开源的仓库,那就非常简单了。

开源仓库

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        run: |
          git clone https://github.com/<your-username>/<your-repository>.git
          cd <your-repository>
          git checkout main

你需要把<your-username><your-repository>替换成具体的内容。

私有仓库

但是如果你是一个私有的仓库,就需要考虑token校验的问题,没有token是没法克隆的,甚至于你还需要配置下git全局配置。

对于token,我们有两种方式,一种是创建账号级的token,一种是github官方预设的仓库级token。

账号级的token可以用于该账号下的所有仓库,而官方预设的只能针对当前运行actions工作流的仓库。

如果你想要账号级的,就得去账号设置中,找到底部的Developer settings,打开该页面,找到Personal access tokens选项下的tokens(classic)菜单,点击右上角的Generate new token按钮自己生成一个。

创建成功后你会得到token字符串,复制后去到需要克隆的仓库页面,在仓库的Settings页面找到Secrets and variables选项,点击里面的Actions菜单,在打开的页面中找到Repository secrets,新增一个仓库密匙,记得Name就是到时候要用的变量名了,推荐大写加下划线,自己取名,Secret就填刚刚复制的token,保存即可。

假设我们这里的name为:MY_TOKEN

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: git全局设置
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          
      - name: 克隆代码
        env:
          TOKEN: ${{ secrets.MY_TOKEN }}
        run: |
          git clone https://x-access-token:${TOKEN}@github.com/<your-username>/<your-repository>.git
          cd <your-repository>
          git checkout main

我们将git全局配置设置成github的机器人默认账号,这样你的操作显示就为显示为机器人,方便区分。

然后我们将刚刚设置的token设置成env变量,在clone的时候使用。

如果你只想使用仓库级的token,直接把MY_TOKEN替换成GITHUB_TOKEN就行了。

TOKEN: ${{ secrets.GITHUB_TOKEN}}

使用官方预设工作

克隆仓库这一步重复率非常高,为此github官方也提供了封装好的工作,我们可以在步骤中通过uses来使用官方提供的工作,从而减少代码的重复编写。

目前这个仓库有v2、v3、v4版本,v4是目前推荐版本。

开源仓库

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支

私有仓库

默认情况下该工作流会直接使用secrets.GITHUB_TOKEN,如果只想使用仓库级token,那么就和上面开源仓库一样,不需要额外加什么代码,如果你一定要指定token,代码如下:

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          token: ${{ secrets.MY_TOKEN }}
          ref: main # 指定主分支

步骤:安装node

由于环境中没有node,我们还需要手动安装,我们也别手搓了,github官方也给咱们预设了,仓库地址:

这个工作流也有好几个版本,其中V4版是目前推荐的,因为node16已经停止维护,所以v4适合新的node版本,如果是旧的,你可以使用v3看看,具体需要自己去测试一下,我没有具体去研究。

使用v3大概率会得到一个警告:

The following actions use a deprecated Node.js version and will be forced to run on node20: actions/checkout@v3, actions/setup-node@v3. For more info: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/
name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支
          
      - name: 指定Node.js版本
        uses: actions/setup-node@v4
        with:
          node-version: "20" # 指定 Node.js 20 版本

如果你的项目根目录下就是开发文件,存在package.json文件,那么你可以直接在指定node版本后增加一个run选项来安装你的依赖,这里我就直接引用下官方示例:

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'npm'
- run: npm ci
- run: npm test

步骤:安装pnpm

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支
          
      - name: 指定Node.js版本
        uses: actions/setup-node@v4
        with:
          node-version: "20" # 指定 Node.js 20 版本

      - name: 安装pnpm
        run: npm install -g pnpm@latest

由于github的运行是在国外,所以我们不需要换源,简直不要太爽。

步骤:安装依赖

我的项目结构如下:

serverless-template
├─ chrome-extension
├─ README.md
├─ docs
└─ serverless-template

其中chrome-extension目录下才是我的开发文件,所以我们安装依赖前还需要进入到该目录。

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支

      - name: 指定Node.js版本
        uses: actions/setup-node@v4
        with:
          node-version: "20" # 指定 Node.js 20 版本

      - name: 安装pnpm
        run: npm install -g pnpm@latest

      - name: 安装依赖
        run: |
          cd chrome-extension
          pnpm install
|符合是用于多行命令时才会用到

步骤:打包项目

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支

      - name: 指定Node.js版本
        uses: actions/setup-node@v4
        with:
          node-version: "20" # 指定 Node.js 20 版本

      - name: 安装pnpm
        run: npm install -g pnpm@latest

      - name: 安装依赖
        run: |
          cd chrome-extension
          pnpm install

      - name: 打包插件
        run: |
          cd chrome-extension  
          pnpm run build

需要注意的是,每次step步骤都是一次新的shell环境执行,所以我这里需要重新cd进入chrome-extension目录。

步骤:创建zip文件

我们需要将打包后的dist目录打成压缩包上传到Release,所以我们得用linux的zip命令制作压缩包文件。

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支

      - name: 指定Node.js版本
        uses: actions/setup-node@v4
        with:
          node-version: "20" # 指定 Node.js 20 版本

      - name: 安装pnpm
        run: npm install -g pnpm@latest

      - name: 安装依赖
        run: |
          cd chrome-extension
          pnpm install

      - name: 打包插件
        run: |
          cd chrome-extension  
          pnpm run build

      - name: 创建谷歌浏览器扩展zip文件
        run: |
          cd chrome-extension
          zip -r ../chrome-extension.zip ./dist

      - name: 创建serverless模版zip文件
        run: |
          zip -r ./serverless-template.zip ./serverless-template

当我们命令太长也可以使用|来实现换行效果。

这里我们可以看到,第一个path表示的是压缩包文件存放路径和文件名,第二个需要压缩的path。

.././我就没必要解释了吧!

步骤:获取版本号

我们在发布Release时,一般都是将标题设置为:v0.0.0.x这种版本号形式,那么最简单便捷的就是从package.json文件中获取它的version,而package中的version,又可以通过插件bumpp实现命令交互式更新。

我们在项目中安装这个依赖:

pnpm i bumpp -D

然后在package.json中配置一个scripts:

{
  "scripts": {
    "version:update": "npx bumpp --no-commit --no-tag --no-push"
  }
}

我们在开发完成,确定没问题后就可以通过运行pnpm run version:update来更新版本号,然后提交到git上去。

既然version的来源已经搞定,我们在步骤中就好办了。

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支

      - name: 指定Node.js版本
        uses: actions/setup-node@v4
        with:
          node-version: "20" # 指定 Node.js 20 版本

      - name: 安装pnpm
        run: npm install -g pnpm@latest

      - name: 安装依赖
        run: |
          cd chrome-extension
          pnpm install

      - name: 打包插件
        run: |
          cd chrome-extension  
          pnpm run build

      - name: 创建谷歌浏览器扩展zip文件
        run: |
          cd chrome-extension
          zip -r ../chrome-extension.zip ./dist

      - name: 创建serverless模版zip文件
        run: |
          zip -r ./serverless-template.zip ./serverless-template

      - name: 获取新版本号
        id: get_chrome_extension_version
        run: |
          # 尝试从 package.json 中获取版本号
          VERSION=$(node -p "require('./chrome-extension/package.json').version" 2>/dev/null || echo "v0.0.1")
          echo "VERSION=${VERSION}" >> $GITHUB_ENV
          echo "New version:${VERSION}"

读取package.json的版本号,以防万一做个判空,为空时返回v0.0.1,并将这个值设置成全局环境变量VERSION

步骤:创建Release

创建Release这个步骤也是高频操作,所以github官方也预设了工作流,仓库地址:

目前有v1和v2版本,v2是推荐版本。

我自己的创建逻辑相对简单,只要main有push操作,就进行一次构建发布,有的人可能需要更加精准,比如官方示例中的,他会判断这次push有tag变动才会发布,代码示例如下:

name: Main

on: push

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Build
        run: echo ${{ github.sha }} > Release.txt
      - name: Test
        run: cat Release.txt
      - name: Release
        uses: softprops/action-gh-release@v2
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: Release.txt

if条件判断,如果这次提交有tags标签,才会执行,如果你有其他条件,可以看文档自己调整。

其中with下的files表示需要上传的文件,是可以上传多个的,多个也是用|管道符来编写。

对于token,默认也是会用secrets.GITHUB_TOKEN,如果需要指定,也是配置token属性。

name: 打包插件并发布到Releases

# 检测只有主分支的push操作会触发该工作流
on:
  push:
    branches:
      - main
  workflow_dispatch: # 允许手动触发该工作流

jobs:
  build:
    name: 打包工作
    runs-on: ubuntu-latest

    steps:
      - name: 克隆代码
        uses: actions/checkout@v4
        with:
          ref: main # 指定主分支

      - name: 指定Node.js版本
        uses: actions/setup-node@v4
        with:
          node-version: "20" # 指定 Node.js 20 版本

      - name: 安装pnpm
        run: npm install -g pnpm@latest

      - name: 安装依赖
        run: |
          cd chrome-extension
          pnpm install

      - name: 打包插件
        run: |
          cd chrome-extension  
          pnpm run build

      - name: 创建谷歌浏览器扩展zip文件
        run: |
          cd chrome-extension
          zip -r ../chrome-extension.zip ./dist

      - name: 创建serverless模版zip文件
        run: |
          zip -r ./serverless-template.zip ./serverless-template

      - name: 获取新版本号
        id: get_chrome_extension_version
        run: |
          # 尝试从 package.json 中获取版本号
          VERSION=$(node -p "require('./chrome-extension/package.json').version" 2>/dev/null || echo "v0.0.1")
          echo "VERSION=${VERSION}" >> $GITHUB_ENV
          echo "New version:${VERSION}"

      - name: 创建Release
        id: create_release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ env.VERSION }}
          name: v${{ env.VERSION }}
          draft: false
          prerelease: false
          files: |
            ./chrome-extension.zip
            ./serverless-template.zip

指定token:

- name: 创建Release
        id: create_release
        uses: softprops/action-gh-release@v2
        with:
          token: ${{ secrets.MY_TOKEN }}
          tag_name: ${{ env.VERSION }}
          name: v${{ env.VERSION }}
          draft: false
          prerelease: false
          files: |
            ./chrome-extension.zip
            ./serverless-template.zip

除了token,我还指定了tag_namename,这两个都是创建Release必填的。

  • draft的值为布尔值,表示这次是否为草稿;
  • prerelease的值为布尔值,表示这次是否为预发布版本;

当然with还有很多配置项,具体可以查看仓库文档。

步骤的id作用

关于步骤中的id,其实在本流程中可以要,也可以不要,配置了id可以被其他步骤通过id获取该步骤的echo输出。

示例:

- name: 获取版本号
  id: get_version
  run: echo "version=1.0.0" >>$GITHUB_ENV

- name: 使用版本号
  run: echo "The version is ${{ steps.get_version.outputs.version }}"

触发

当我们给main分支push代码的时候,或者合并分支的时候,都会触发该工作流,我们可以在仓库的Actions中查看进展。

这是自动操作,我这里还配置了手动触发,我们点击具体的工作流,在该页面右上角可以手动运行。

分类: 教程 标签: githubactions自动打包releases自动发版

评论

暂无评论数据

暂无评论数据

目录