玩转Qt(11)-github-Actions自动化发行
本文于
854
天之前发表,文中内容可能已经过时。
简介 在上一篇文章《github-Actions自动化编译》中,介绍了github-Actions的基本用法,
本文来介绍github-Actions的自动化发布。
Qt项目的编译流程 先来回顾一下,上一篇文章中的Qt项目的编译流程
安装Qt环境
这一步用第三方Action模板:install-qt-action
获取项目代码
这一步用Actions官方核心模板:actions/checkout
执行qmake、make
这一步用自定义脚本,也可以换成cmake、qbs、gn、ninja等构建工具
执行test
这一步可以引入单元测试、自动化UI测试等。暂无完善的方案,以后再说。
发布
见下文。
Qt项目的发布流程 Qt程序在编译完成后,发布的大致流程是:
1、 查找依赖库
2、制作压缩包或者安装包
3、上传压缩包或者安装包到网站、网盘。
查找依赖 Qt官方提供的查找依赖库的命令行工具,包括:Windows平台的Windeployqt、MacOS平台的Macosdeployqt。
在这两个平台,只使用Qt库的情况下,这两个工具足够了。
制作包 做压缩包比较简单。(我们常说的‘绿色软件’,就是一个压缩包)
一般安装7z、rar之类的压缩工具,用一条命令行就行了。
涛哥这里再说一下,github-Actions给所有平台都提供了PowerShell,而PowerShell内置了压缩命令Compress-Archive。
使用也很简单,只要路径和名字,例如:
1 Compress-Archive -Path .\MyFolder 'MyRelease.zip'
做安装包,Qt官方有功能很全面的安装包制作工具:QtInstallFrameWork, 稍微翻看一下文档或者例子即可。本文先不展开了。
上传 github 本身提供了’Release’功能,每个仓库都有一个’Release’页面
可以将打包好的压缩包或者安装包,直接上传上去, 供他人下载。
github-Actions还提供了 创建’Release’、上传’Release’的模板
actions/create-release
actions/upload-release-asset
这两个模板的用法也很简单,在yml文件中直接use就行了,不赘述了。
定制发布流程 前面介绍了一些简单的理论,接下来通过实例,教大家github-Actions的使用。
以HelloActions-Qt 项目为例,做一些定制。
需求如下:
1、每次提交代码,同时在Windows、MacOS、Ubuntu、Android、IOS五个平台编译
2、每次提交tag,在windows和MacOS平台制作软件包,并发布到同一个github-‘Release’
需求1已经实现了,着重讨论一下需求2:
发布时机 ‘每次提交tag’限定了发布的时机。
涛哥尝试了一番,最终得到答案。
回顾一下, Windows平台的编译配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 name: Windows on: [push,pull_request] jobs: build: name: Build runs-on: windows-latest strategy: matrix: qt_ver: [5.12.6] qt_target: [desktop] qt_arch: [win64_msvc2017_64, win32_msvc2017] include: - qt_arch: win64_msvc2017_64 msvc_arch: x64 - qt_arch: win32_msvc2017 msvc_arch: x86 steps: - name: Install Qt uses: jurplel/install-qt-action@v2.0.0 with: version: ${{ matrix.qt_ver }} target: ${{ matrix.qt_target }} arch: ${{ matrix.qt_arch }} - uses: actions/checkout@v1 with: fetch-depth: 1 - name: build-msvc shell: cmd env: vc_arch: ${{ matrix.msvc_arch }} run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %vc_arch% qmake nmake
steps中的每一个步骤,可以有触发条件。我们可以在这里指定,只有github的事件为tag时才执行:
1 2 3 4 5 6 7 steps: 。。。 - name: package if: startsWith(github.event.ref, 'refs/tags/' ) run: | 。。。
打包步骤 这里给出一个实际的打包步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - name: package if: startsWith(github.event.ref, 'refs/tags/' ) env: VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC' archiveName: ${{ matrix.qt_ver }}-${{ matrix.qt_target }}-${{ matrix.qt_arch }} targetName: HelloActions-Qt.exe shell: pwsh run: | # 创建文件夹 New-Item -ItemType Directory ${env:archiveName} # 拷贝exe Copy-Item bin\${env:targetName} ${env:archiveName}\ # 拷贝依赖 windeployqt --qmldir . ${env:archiveName}\${env:targetName} # 打包zip Compress-Archive -Path ${env:archiveName} ${env:archiveName}'.zip' # 记录环境变量packageName给后续step $name = ${env:archiveName} echo "::set-env name=packageName::$name" # 打印环境变量packageName Write-Host 'packageName:'${env:packageName}
做一些说明:
其中的VCINSTALLDIR环境变量,是给windeployqt用的。有了这个环境变量,windeployqt会去msvc的安装路径提取‘运行时安装程序’。
打包完以后,将包名设置为环境变量,后续的步骤就可以通过环境变量拿到包名字了。
普通的设置环境变量,在步骤执行完成后就失效了,
这里使用github-Actions的‘记录命令’set-env ,具体可以参考文档github-Actions记录命令
文档说不要用双引号,应该都是针对linux的,我试出来的PowerShell用法如下:
1 2 $name = ${env:archiveName}echo "::set-env name=packageName::$name "
先取环境变量到一个局部变量,再在‘记录命令’中引用局部变量。
多平台发布 如果只有一个平台、一种配置,直接用那两个模板就能解决问题。
这是官方给的例子upload-release-asset :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 steps: - name: Checkout code uses: actions/checkout@master - name: Build project run: | zip --junk-paths my-artifact README.md - name: Create Release id: create_release uses: actions/create-release@v1.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} draft: false prerelease: false - name: Upload Release Asset id: upload-release-asset uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ./my-artifact.zip asset_name: my-artifact.zip asset_content_type: application/zip
在多平台 或者 多配置的情况下,同一个tag, 只有第一个执行create-release的任务可以成功,后续任务
再次执行create-release时,该tag下已经有了同名的‘Release’,所以会create失败。
这个问题折磨了涛哥好一阵子。找不到现成的解决方案,涛哥就自己实现了一种:
先用github的REST API去判断该tag下有没有‘Release’:
没有则执行create-release,并提取upload_url;
有则提取upload_url。
最后执行upload-release-asset
调用REST API,涛哥依旧使用了方便的PowerShell,
实际的配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 - name: queryReleaseWin id: queryReleaseWin if: startsWith(github.event.ref, 'refs/tags/' ) shell: pwsh env: githubFullName: ${{ github.event.repository.full_name }} ref: ${{ github.event.ref }} run: | [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1) [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag} $response={} try { $response = Invoke-RestMethod -Uri $url -Method Get } catch { Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription # 没查到,输出 echo "::set-output name=needCreateRelease::true" return } [string]$latestUpUrl = $response.upload_url Write-Host 'latestUpUrl:'$latestUpUrl if ($latestUpUrl.Length -eq 0) { # 没查到,输出 echo "::set-output name=needCreateRelease::true" } # tag 创建github-Release - name: createReleaseWin id: createReleaseWin if: startsWith(github.event.ref, 'refs/tags/' ) && steps.queryReleaseWin.outputs.needCreateRelease == 'true' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} uses: actions/create-release@v1.0.0 with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} body: ${{ github.event.head_commit.message }} draft: false prerelease: false - name: getLatestTagRelease if: startsWith(github.event.ref, 'refs/tags/' ) shell: pwsh env: githubFullName: ${{ github.event.repository.full_name }} upUrl: ${{ steps.createReleaseWin.outputs.upload_url }} ref: ${{ github.event.ref }} run: | # upUrl不为空,导出就完事 if (${env:upUrl}.Length -gt 0) { $v=${env:upUrl} echo "::set-env name=uploadUrl::$v" return } # upUrl为空则重新获取 [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1) [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag} $response = Invoke-RestMethod -Uri $url -Method Get [string]$latestUpUrl = $response.upload_url Write-Host 'latestUpUrl:'$latestUpUrl # 导出 echo "::set-env name=uploadUrl::$latestUpUrl" Write-Host 'env uploadUrl:'${env:uploadUrl} # tag 上传Release - name: uploadRelease id: uploadRelease if: startsWith(github.event.ref, 'refs/tags/' ) env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} uses: actions/upload-release-asset@v1.0.1 with: upload_url: ${{ env.uploadUrl }} asset_path: ./${{ env.packageName }}.zip asset_name: ${{ env.packageName }}.zip asset_content_type: application/zip
最终配置 windows版的最终配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 name: Windows on: push: paths-ignore: - 'README.md' - 'LICENSE' pull_request: paths-ignore: - 'README.md' - 'LICENSE' jobs: build: name: Build runs-on: windows-latest strategy: matrix: qt_ver: [5.12.6] qt_target: [desktop] qt_arch: [win64_msvc2017_64, win32_msvc2017] include: - qt_arch: win64_msvc2017_64 msvc_arch: x64 qt_arch_install: msvc2017_64 - qt_arch: win32_msvc2017 msvc_arch: x86 qt_arch_install: msvc2017 env: targetName: HelloActions-Qt.exe steps: - name: Install Qt uses: jurplel/install-qt-action@v2.0.0 with: version: ${{ matrix.qt_ver }} target: ${{ matrix.qt_target }} arch: ${{ matrix.qt_arch }} - uses: actions/checkout@v1 with: fetch-depth: 1 - name: build-msvc shell: cmd env: vc_arch: ${{ matrix.msvc_arch }} run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %vc_arch% qmake nmake # tag 打包 - name: package if: startsWith(github.event.ref, 'refs/tags/' ) env: VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC' archiveName: ${{ matrix.qt_ver }}-${{ matrix.qt_target }}-${{ matrix.qt_arch }} shell: pwsh run: | # 创建文件夹 New-Item -ItemType Directory ${env:archiveName} # 拷贝exe Copy-Item bin\${env:targetName} ${env:archiveName}\ # 拷贝依赖 windeployqt --qmldir . ${env:archiveName}\${env:targetName} # 打包zip Compress-Archive -Path ${env:archiveName} ${env:archiveName}'.zip' # 记录环境变量packageName给后续step $name = ${env:archiveName} echo "::set-env name=packageName::$name" # 打印环境变量packageName Write-Host 'packageName:'${env:packageName} # tag 查询github-Release - name: queryReleaseWin id: queryReleaseWin if: startsWith(github.event.ref, 'refs/tags/' ) shell: pwsh env: githubFullName: ${{ github.event.repository.full_name }} ref: ${{ github.event.ref }} run: | [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1) [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag} $response={} try { $response = Invoke-RestMethod -Uri $url -Method Get } catch { Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription # 没查到,输出 echo "::set-output name=needCreateRelease::true" return } [string]$latestUpUrl = $response.upload_url Write-Host 'latestUpUrl:'$latestUpUrl if ($latestUpUrl.Length -eq 0) { # 没查到,输出 echo "::set-output name=needCreateRelease::true" } # tag 创建github-Release - name: createReleaseWin id: createReleaseWin if: startsWith(github.event.ref, 'refs/tags/' ) && steps.queryReleaseWin.outputs.needCreateRelease == 'true' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} uses: actions/create-release@v1.0.0 with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} body: ${{ github.event.head_commit.message }} draft: false prerelease: false - name: getLatestTagRelease if: startsWith(github.event.ref, 'refs/tags/' ) shell: pwsh env: githubFullName: ${{ github.event.repository.full_name }} upUrl: ${{ steps.createReleaseWin.outputs.upload_url }} ref: ${{ github.event.ref }} run: | # upUrl不为空,导出就完事 if (${env:upUrl}.Length -gt 0) { $v=${env:upUrl} echo "::set-env name=uploadUrl::$v" return } [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1) [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag} $response = Invoke-RestMethod -Uri $url -Method Get [string]$latestUpUrl = $response.upload_url Write-Host 'latestUpUrl:'$latestUpUrl echo "::set-env name=uploadUrl::$latestUpUrl" Write-Host 'env uploadUrl:'${env:uploadUrl} # tag 上传Release - name: uploadRelease id: uploadRelease if: startsWith(github.event.ref, 'refs/tags/' ) env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} uses: actions/upload-release-asset@v1.0.1 with: upload_url: ${{ env.uploadUrl }} asset_path: ./${{ env.packageName }}.zip asset_name: ${{ env.packageName }}.zip asset_content_type: application/zip
MacOS最终配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 name: MacOS on: push: paths-ignore: - 'README.md' - 'LICENSE' pull_request: paths-ignore: - 'README.md' - 'LICENSE' jobs: build: name: Build runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest] qt_ver: [5.12.6] qt_arch: [clang_64] env: targetName: HelloActions-Qt steps: - name: Install Qt uses: jurplel/install-qt-action@v2.0.0 with: version: ${{ matrix.qt_ver }} - uses: actions/checkout@v1 with: fetch-depth: 1 - name: build macos run: | qmake make # tag 打包 - name: package if: startsWith(github.event.ref, 'refs/tags/' ) run: | # 拷贝依赖 macdeployqt bin/${targetName}.app -qmldir=. -verbose=1 -dmg # tag 查询github-Release - name: queryRelease id: queryReleaseMacos if: startsWith(github.event.ref, 'refs/tags/' ) shell: pwsh env: githubFullName: ${{ github.event.repository.full_name }} ref: ${{ github.event.ref }} run: | [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1) [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag} $response={} try { $response = Invoke-RestMethod -Uri $url -Method Get } catch { Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription # 没查到,输出 echo "::set-output name=needCreateRelease::true" return } [string]$latestUpUrl = $response.upload_url Write-Host 'latestUpUrl:'$latestUpUrl if ($latestUpUrl.Length -eq 0) { # 没查到,输出 echo "::set-output name=needCreateRelease::true" } # tag 创建github-Release - name: createReleaseWin id: createReleaseWin if: startsWith(github.event.ref, 'refs/tags/' ) && steps.queryReleaseMacos.outputs.needCreateRelease == 'true' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} uses: actions/create-release@v1.0.0 with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} body: ${{ github.event.head_commit.message }} draft: false prerelease: false - name: getLatestTagRelease if: startsWith(github.event.ref, 'refs/tags/' ) shell: pwsh env: githubFullName: ${{ github.event.repository.full_name }} upUrl: ${{ steps.queryReleaseMacos.outputs.upload_url }} ref: ${{ github.event.ref }} run: | # upUrl不为空,导出就完事 if (${env:upUrl}.Length -gt 0) { $v=${env:upUrl} echo "::set-env name=uploadUrl::$v" return } [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1) [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag} $response = Invoke-RestMethod -Uri $url -Method Get [string]$latestUpUrl = $response.upload_url Write-Host 'latestUpUrl:'$latestUpUrl echo "::set-env name=uploadUrl::$latestUpUrl" Write-Host 'env uploadUrl:'${env:uploadUrl} # tag 上传Release - name: uploadRelease id: uploadRelease if: startsWith(github.event.ref, 'refs/tags/' ) env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} uses: actions/upload-release-asset@v1.0.1 with: upload_url: ${{ env.uploadUrl }} asset_path: ./bin/${{ env.targetName }}.dmg asset_name: ${{ env.targetName }}.dmg asset_content_type: application/applefile
结果和代码
代码在github HelloActions-Qt
另外在涛哥的Qml控件库TaoQuick,也使用了这一套配置
TaoQuick