使用GitHub Actions自动发布应用

使用GitHub Actions自动发布应用

GitHub Actions是GitHub提供的一项功能,通过创建工作流程(workflow)文件,开发人员可以实现对应用自动化的测试、构建和发布等任务。同时,GitHub Actions支持自定义流程工作的平台,因此跨平台的构建也成为了可能。

在GitHub Actions中可以考虑使用语义化版本Conventional Commits,实现自动化的版本管理和发布流程,提高开发效率;下面将先对这二者进行介绍。

语义化版本和Conventional Commits

语义化版本

考虑以下的版本号,Major.Minor.Patch,其中:

  • **Major:**当应用出现了重大的,破坏性的变更时,这个部分+1
  • **Minor:**当应用出现了功能性的更新(包括废弃某些API),但是没有不向后兼容的变更时,这个部分+1
  • **Patch:**应用没有任何功能性的更新,只是修复漏洞时,这个部分+1

除了以上的几点,语义化版本还有以下的要求:

  1. 版本号的数字一定是按照数字顺序递增。比如,对于1.9.0版本,如果有功能性的更新,那么他之后应该是1.10.0、1.11.0等。
  2. 对于Major为0的版本号(比如0.10.2),这表示应用还在初始开发阶段,此时应用仍然不稳定,版本的变化不一定符合语义化版本的特征。
  3. 当应用的版本为1.0.0时,应用正式发布,此后的版本号变化将严格反映语义化版本的特征。
  4. 预发布版本需要在正式版本号后添加横线,形如X.Y.Z-alpha、X.Y.Z-alpha.1等,并且它们在版本号的比较上小于X.Y.Z。

其他的语义化版本规范请参见官网

Conventional Commits

Conventional Commits是一个轻量级的提交信息的格式约定,它规定了每次提交的修改范围、描述以及附加的信息的格式。使用Conventional Commits具有以下好处:

  1. 简单清晰地描述每次提交的修改内容,方便开发人员查看和管理。
  2. 方便自动工具审查,避免多种不同提交格式所带来的混乱。
  3. 可以借助自动化工具,在每次发布时自动生成发布文档,介绍应用更新内容。

Conventional Commits的格式如下:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

用尖括号括起来的表示必选项,方括号括起来的表示可选项,下面对其中一些进行说明:

  1. **fix:**当一个提交信息的type是fix时,表示修复一个漏洞,对应语义化版本中的Patch
  2. **feat:**当一个提价信息的type是feat时,表示应用引入一个新的功能,对应语义化版本中的Minor
  3. **BREAKING CHANGE:**提交信息的footer中具有BREAKING CHANGE:,或者在type后面跟随一个!时,表示应用具有不向后兼容的破坏性更新,对应语义化版本的Major。当应用具有BREAKING CHANGE时,它可以具有任何的类型。
  4. 除了fixfeat以外,提交信息的type还可以取以下值:build、chore、ci、docs、style、refactor、prefer、test等。
  5. 除了BREAKING CHANGE:以外,提交信息的footer也被允许,其格式应当为Key: Value,例如:Closes: #123,可以表示关闭Issue #123。
  6. scope可以用来给提交信息添加额外的上下文,比如:feat(parser): add ability to parse arrays.

常用的提交类型说明如下:

  1. **docs:**只有文档被更新
  2. **style:**不影响代码含义的修改,比如空格、缩进、语句末尾的分号等。
  3. **refactor:**针对代码的修改,但是不修复bug,也不引入新功能。
  4. **pref:**为了提高代码性能所做的修改。
  5. **test:**添加新的测试,或者修改已有的测试。
  6. **build:**针对构建系统的修改,比如npm、glup等。
  7. **ci:**修改持续继承相关的文件和脚本等,比如GitHub Actions。
  8. **chore:**既不修改源文件,也不修改测试文件,比如发布新版本等。
  9. **revert:**撤回一次已有的更新。

GitHub Actions介绍

GitHub Actions是GitHub提供的持续集成服务,允许开发者自动化地测试、构建和发布代码。将GitHub Actions与前面提到的语义化版本和Conventional Commits结合,可以提高开发效率,使开发流程更加规范,便于理解和追踪代码更改。下面是一段GitHub Actions的“Hello world”代码:

name: hello-world  # 1

on:  # 2
  push: 
    branches: main

jobs: # 3
  hello-world: 
    runs-on: ubuntu-latest # 4
    steps:  # 5
      - name: checkout-repository
        uses: actions/checkout@v4  # 6
      - run: echo "代码仓库 ${{ github.repository }}已被克隆到runner中"  # 7
  1. 指定Action的名称,在执行Actions时会被现实在侧边栏中显示。
  2. 指定Action触发的条件(Trigger),这里是在main分支推送代码时执行。
  3. 每个Action至少会有一个Job,所有的Job都在jobs中定义。
  4. runs-on指定了这个Job运行的平台,可以指定一个平台,也可以指定多个不同的平台同时执行任务。
  5. 每个Job都至少会有一个Step,所有的step都在steps中定义,每个Step可以使使用uses调用其他的GitHub Actions,或者使用run来执行一段代码。
  6. 使用actions/checkout@v4,可以将代码从仓库中克隆到执行Action的容器中。如果需要对代码进行操作(执行测试、构建等任务时),那么必须要先使用这个Action。
  7. 在Step中使用run来执行一段shell命令,在不同平台上使用的shell不一样,比如Windows上默认命令行是Powershel。

Trigger

Trigger用来表示这个Action在什么时机被执行,可能是一次推送、一个新的Issue或者一个新的Pull Request。同时我们也可以借助Filter来实现对触发条件的筛选。下面列出几个常用的触发条件:

  1. 最简单的触发条件:

    on: push  # 只有一个事件可以触发Action执行
    on: [push, fork, issue, pull_request]  # 有多个事件可以触发Action执行
    
  2. 对代码推送或Pull Request等进行筛选:

    on:
      push:
        branches:  # 针对推送的代码分支进行过滤
          - main
        tags: 
          - "v**"
          - "releases/**"  # 使用通配符进行匹配
          - "!releases/**-alpha"  # 在前面添加感叹号表示排除掉这个筛选条件
      issue:  # 对Issue进行筛选
        types: [opened, labeled]
    
  3. 手动执行每个Action

    on:
      workflow_dispatch:  # 添加后可以手动执行Action,但是只能在默认分支上执行
        inputs:  # 可选项
    

    手动执行Actions时,还可以通过input定义手动执行时的输入,语法参见Workflow syntax for GitHub Actions - GitHub Docs

Jobs

在GitHub Actions中,jobs定义了需要执行的任务。一个简单的示例如下:

jobs:
  job_id: 
    name: Job Id  # 可选项,指定当前步骤的名称,在执行时会被现实在GitHub界面上
    runs-on: ubuntu-latest  # 每个Job必须要指定运行的平台,后续代码中为了简便将省略这一部分

定义所需要的权限

在Action或者Job执行过程中,可能会需要不同的权限,比如contents: write权限可以让开发人员发布一个新的Release。权限信息可以被直接定义在Action中,表示全局需要的权限;也可以单独定义在每个Job中,表示当前Job所需要的权限,参见Workflow syntax for GitHub Actions - GitHub Docs

name: Permission Demo
on: push

permissions: 
  contents: write

jobs: 
  make-a-pull-request: 
    permissions: 
      pull_requests: write  # 表示创建一个新的Pull Request
    # 后续省略

定义每个Job的执行条件和依赖关系

每个Job在运行时都是并行执行的,但是我们有时同样需要让这些Job按顺序执行,或者在某些条件下执行,这时就可以用到needsif标签:

jobs:
  job1:
  job2:
    needs: job1
  job3: 
    needs: [job1, job2]

在上面的代码中,job2依赖于job1,而job3又依赖于job2和job1,因此他们三个的执行顺序是:job1→job2→job3。

on: push
jobs:
  process-tag:
    if: ${{ startsWith(github.ref, 'refs/tags/') }}

上述代码表示只有在推送一个Tag的时候才会执行process-tag这个job。

在不同平台执行任务

有时同一个任务需要在不同的平台下执行,比如跨平台的构建,这时可以使用matrix实现:

jobs:
  build-multi-platform: 
    strategy: 
      matrix: 
        platform: [macos-latest, ubuntu-latest, windows-latest]  # 定义所需要运行的平台
    runs-on: ${{ matrix.platform }}
    steps: 
      step_1: 
        if: matrix.platform == 'ubuntu-latest'  # 在ubuntu-latest环境下执行这个step

Steps

每个Job都会有至少一个Step,用来表示需要执行的具体行为。与Job不同的是,Step是按照定义顺序执行的,不存在并行执行的问题。

jobs:
  job1: 
    rus-on: ubuntu-latest
    steps: 
      - name: Step 1  # 1
        id: st1  # 2
        if: condition == true  # 3
        shell: pwsh  # 4
        uses: actions/checkout@v4  # 5
        run: echo 'Hello, world!'  # 6
        with:  # 7
          key: value
        env: # 8
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  1. 可选项,用于指定GitHub上显示的名称。
  2. 可选项,用于在不同的上下文中传递参数,参见Contexts - GitHub Docs
  3. 可选项,用于指定当前Step的执行条件。
  4. 可选项,用于指定使用的命令行,在Windows上默认是pwsh,使用PowerShell,其他平台上默认是bash,使用Bash Shell。参见Workflow syntax for GitHub Actions - GitHub Docs
  5. 必选项之一,使用其他的GitHub Actions。
  6. 必选项之一,执行一段命令,usesrun可以同时使用。
  7. 可选项,用于向uses使用的GitHub Action传递参数。
  8. 用于指定运行环境中的值,这属于上下文(Context)的一部分,参见Contexts - GitHub Docs

使用Release Please自动更新版本号

Release Please是Google推出的自动工具,可以实现基于Conventional Commit的提交信息自动发布更新、自动更新项目的版本号。

注意:Release Please并不会自动将代码上传到包管理器(比如npm、Maven Repository、crates.io等),也不能处理复杂的分支管理,这些需要开发人员手动实现。

Release Please的工作是基于Release PR实现的。在被触发而工作时,Release Please会首先分析Git的提交历史,从提交信息中按照Conventional Commits的格式整理出此次更新的内容和级别(Major、Minor或Patch),然后修改版本号和修改文件(一般是CHANGELOG.md),最后向仓库中发起一个Pull Request。如果开发人员合并了这个PR,那么修改就会被合并进分支中,然后发布一个新的Release。

Release Please支持的项目类型主要有以下几种,完整列表参见GitHub

类型描述
node包含一个package.json和CHANGELOG.md。
rust包含Cargo.toml和CHANGELOG.md。
dart包含pubspec.yaml和CHANGELOG.md。
php包含composer.json和CHANGELOG.md。
maven在每次发布后生成一个SNAPSHOT版本号并且自动更新pom.xml。
simple包含一个version.txt和CHANGELOG.md。

下面是一个简单的示例:

name: release-please-demo

on: 
  push: 
    branches:
      - main

permissions:  # 1
  contents: write
  pull-requests: write

jobs: 
  release-please: 
    runs-on: ubuntu-latest
    steps: 
      - uses: google-github-actions/release-please-action@v4  # 2
        with: 
          release-type: node  # 3
          token: ${{ secrets.GITHUB_TOKEN }}  # 4
  1. Release Please需要contents: write权限来发布新版本,使用pull-request: write权限来发布Release PR。
  2. Release Please的GitHub Actions名称为:google-github-actions/release-please-action@v4
  3. 指定发布类型,这里以node为例,要求仓库中有一个package.json文件和CHANGELOG.md文件(这个如果没有会自动创建)。
  4. 附带上GitHub Token,这是必须的。GITHUB_TOKEN是GitHub在每个Action执行时自动生成的token,开发人员也可以使用自定义的Token。

Release Please的输出

上面的实例只做了检查提交信息、发起Release PR和在合并后自动发布新版本的功能,有时我们需要在成功发布版本之后做一些后续工作,这时就可以借助Release Please的输出来实现。常用的输出结果有以下几条:

  1. **release_created:**当根目录下有新的发布产生时为true。
  2. **upload_url:**上传文件的URL,与GitHub APIReleases - GitHub Docs有关。
  3. **html_url:**同样与GItHub Release API有关。
  4. **tag_name:**新发布版本的Tag,同样与GitHub Release API有关。
  5. **major、minor、patch:**新发布版本的版本号。

其他的输入输出参见README

当发布被合并时,同样会在main分支产生一次推送,因此上面的GitHub Actions会被再次执行,因此我们可以在后面根据release_created输出内容来判断Release PR是否被合并,即是否会有新发布产生:

# 上面的内容不变
jobs:
  release-please: 
    runs-on: ubuntu-latest
    steps: 
      - id: release  # 1
        uses: google-github-actions/release-please-action@v4
        with: 
          release-type: node
          token: ${{ secrets.GITHUB_TOKEN }}
    outputs:  # 2
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.release_created }}

  build-release: 
    needs: release-please  # 3
    runs-on: ubuntu-latest
    if: ${{ needs.release-please.outputs.release_created }}  # 4
    steps: 
      - name: Echo tag name
        run: echo ${{ needs.release-please.outputs.tag_name }}
  1. 指定这个Step的id,便于传递参数。
  2. 将Release Please的输出结果暴露出来,在后面继续使用。
  3. 因为这个build-release需要在release-please之后执行,因此需要指定依赖关系。
  4. 判断是否产生新的Release,用到了前面Job的输出。

附:使用Tauri构建应用并上传

最后附带一个完整的示例代码,用于在不同平台构建Tauri应用并上传至Release:

name: Create release  # 指定名称

on: 
  push:
    branches:
      - master  # 在master分支推送代码时触发,新项目可能是main分支

permissions:  # 指定需要的权限
  contents: write
  pull-requests: write

jobs:
  create-release:
    runs-on: ubuntu-latest
    steps:
      - name: Create release
        id: release
        uses: google-github-actions/release-please-action@v3  # 使用Release Please Action,并指定类型为node
        with:
          release-type: node
          token: ${{ secrets.GITHUB_TOKEN }}  # 使用GitHub自动生成的token
    outputs:  # 将输出暴露除去
      release-created: ${{ steps.release.outputs.release_created }}
      tag-name: ${{ steps.release.outputs.tag_name }}

  build-tauri:
    needs: create-release
    if: ${{ needs.create-release.outputs.release-created }}

    env:  # 指定GH_TOKEN,gh是GitHub自带的CLI,用于实现一些自动化任务,比如向Release上传构建产物等
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    strategy:  # 1
      fail-fast: false
      matrix: 
        platform: [macos-latest, ubuntu-20.04, windows-latest]

    runs-on: ${{ matrix.platform }}

    steps:
      - name: Echo tag name
        run: echo "Build Application ${{ needs.create-release.outputs.tag-name }} for platform ${{ matrix.platform }}."

      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install dependencies(Ubuntu only)  # 安装Linux特定的依赖
        if: matrix.platform == 'ubuntu-20.04'
        run: |
          sudo apt-get update
          sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev

      - name: Rust setup
        uses: dtolnay/rust-toolchain@stable

      - name: Rust cache
        uses: swatinem/rust-cache@v2
        with:
          workspaces: "./src-tauri -> target"

      - name: Sync node version and setup cache
        uses: actions/setup-node@v4
        with:
          node-version: "lts/*"
          cache: "yarn"

      - name: Install frontend dependencies
        run: yarn install

      - name: Build Application
        id: build
        uses: tauri-apps/tauri-action@v0  # 使用tauri-action来构建应用
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # 传递GITHUB_TOKEN,这是必须的

      - name: Upload artifacts(Linux)
        if: matrix.platform == 'ubuntu-20.04'
        run: |  # 在这里使用竖线,将后面的每行代码通过\n连接起来
          gh release upload ${{ needs.create-release.outputs.tag-name }} ${{ github.workspace }}/src-tauri/target/release/bundle/deb/*.deb
          gh release upload ${{ needs.create-release.outputs.tag-name }} ${{ github.workspace }}/src-tauri/target/release/bundle/appimage/*.AppImage

      - name: Upload artifacts(macOS)
        if: matrix.platform == 'macos-latest'
        run: |
          cd ${{ github.workspace }}/src-tauri/target/release/bundle/macos
          zip -r Application.app.zip Application.app  # 在macOS中,xxx.app是一个文件夹,因此需要打包成一个压缩包
          gh release upload ${{ needs.create-release.outputs.tag-name }} ${{ github.workspace }}/src-tauri/target/release/bundle/dmg/*.dmg
          gh release upload ${{ needs.create-release.outputs.tag-name }} ${{ github.workspace }}/src-tauri/target/release/bundle/macos/*.app.zip

      - name: Upload artifacts(Windows)
        if: matrix.platform == 'windows-latest'
        run: |  # 2
          Get-ChildItem -Path "${{ github.workspace }}\src-tauri\target\release\bundle\msi\*.msi" | ForEach-Object { 
            gh release upload ${{ needs.create-release.outputs.tag-name }} $_.FullName 
          }

          Get-ChildItem -Path "${{ github.workspace }}\src-tauri\target\release\bundle\nsis\*.exe" | ForEach-Object { 
            gh release upload ${{ needs.create-release.outputs.tag-name }} $_.FullName 
          }

关于GitHub Actions Matrix

GitHub Actions允许同样的工作在不同的参数下执行,可以是使用的操作系统,也可以是自定义的其他参数。示例代码如下:

jobs:
  example_matrix:
    strategy:
      matrix:
        version: [10, 12, 14]
        os: [ubuntu-latest, windows-latest]

这段代码会产生6个不同的运行平台,分别是:

  • { version: 10, os: ubuntu-latest }
  • { version: 10, os: windows-latest }
  • { version: 12, os: ubuntu-latest }
  • { version: 12, os: windows-latest }
  • { version: 14, os: ubuntu-latest }
  • { version: 14, os: windows-latest }

每个Job最多能创建256个Matrix,同样可以使用include和exclude来添加或删除运行矩阵,参见添加矩阵排除矩阵

fail-fast参数默认为true,当它为true时,只要运行的矩阵中有一个失败,那么所有的矩阵全部停止运行,在这里我们不希望在某个平台构建失败后影响到其他平台的构建结果,因此需要将其设置为false

关于运行矩阵的完整内容,参见对作业使用矩阵 - GitHub 文档

Job运行时使用的Shell

在非Windows平台下的命令行是Bash,在Windows平台下的命令行默认为PowerShell,二者语法有较大差异,因此在跨平台执行任务时需要注意区分。下面列出所支持的全部命令行:

支持的平台命令行选项执行参数是否为默认
Linux/macOSbash -e {0}是(非Windows平台下)
Allbashbash --noprofile --norc -eo pipefail {0}是(非Windows平台下),没有时使用sh
Allpwshpwsh -command ". ‘{0}’”
Allpythonpython {0}
Linux/macOSshsh -e {0}非Windows平台下缺失bash时的默认
Windowscmd%ComSpec% /D /E:ON /V:OFF /S /C "CALL “{0}””
Windowspwshpwsh -command ". ‘{0}’”是,Windows平台下
Windowspowershellpowershell -command ". ‘{0}’”否,旧版PowerShell

另一个常用的命令行是gh,即GitHub CLI,关于他的使用方法,可以参见用户手册

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
GitHub Actions是一项强大的自动化工作流工具,可以与各种云平台集成,包括阿里云。通过GitHub Actions,我们可以在代码提交或其他事件触发时,自动部署我们的应用程序到阿里云。 首先,我们需要在GitHub仓库中创建一个新的工作流文件(workflow file)。这个文件定义了部署到阿里云的步骤和操作。我们可以使用YAML格式编写这个文件,并将其放置在仓库的.github/workflows目录下。 在工作流文件中,我们可以定义多个job(作业),每个job执行一个或多个步骤。我们可以指定触发条件,例如当代码推送到特定分支时触发部署。 为了部署到阿里云,我们需要提供阿里云的访问凭据和其他必要的配置信息。我们可以使用GitHub仓库的Secrets功能来安全地存储这些凭据。在工作流文件中,我们可以通过workflow的env属性获取这些凭据,并将其传递给部署步骤。 在部署步骤中,我们可以使用阿里云提供的CLI命令或API来执行具体的部署操作。例如,我们可以通过CLI命令将我们的应用程序打包并上传到阿里云的存储服务,然后通过API请求将应用程序部署到阿里云的云服务器。 完成工作流的编写和配置后,我们可以将其保存并提交到GitHub仓库。在每次满足触发条件的事件发生时,GitHub Actions将自动运行我们的工作流,并执行部署到阿里云的步骤。 通过GitHub Actions部署到阿里云,我们可以实现自动化的持续集成和部署,大大简化了我们的工作流程,提高了开发和部署的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OriginCoding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值