Portable Release #71
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Portable Release | |
| on: | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: [completed] | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: Branch, tag, or commit SHA to package after CI is green | |
| required: true | |
| default: main | |
| release_tag: | |
| description: Optional v* tag to create or update with portable assets | |
| required: false | |
| default: "" | |
| permissions: | |
| contents: write | |
| actions: read | |
| concurrency: | |
| group: portable-release-${{ github.event_name }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| resolve-release: | |
| name: Resolve portable release version | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| ( | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'main' | |
| ) | |
| outputs: | |
| checkout-ref: ${{ steps.version.outputs.checkout-ref }} | |
| tag: ${{ steps.version.outputs.tag }} | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || inputs.ref }} | |
| fetch-depth: 0 | |
| - name: Resolve version | |
| id: version | |
| shell: bash | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} | |
| INPUT_REF: ${{ inputs.ref }} | |
| INPUT_RELEASE_TAG: ${{ inputs.release_tag }} | |
| run: | | |
| set -euo pipefail | |
| git fetch --tags --force | |
| if [[ "$EVENT_NAME" == "workflow_run" ]]; then | |
| checkout_ref="$WORKFLOW_HEAD_SHA" | |
| else | |
| checkout_ref="$INPUT_REF" | |
| fi | |
| tag="" | |
| if [[ -n "$INPUT_RELEASE_TAG" ]]; then | |
| tag_commit="$(git rev-list -n 1 "$INPUT_RELEASE_TAG")" | |
| head_commit="$(git rev-parse HEAD)" | |
| if [[ "$tag_commit" != "$head_commit" ]]; then | |
| echo "release_tag $INPUT_RELEASE_TAG does not point at the checked out commit." >&2 | |
| exit 1 | |
| fi | |
| tag="$INPUT_RELEASE_TAG" | |
| else | |
| tag="$(git tag --points-at HEAD --list "v*" | head -n 1 || true)" | |
| fi | |
| if [[ -n "$tag" ]]; then | |
| version="${tag#v}" | |
| else | |
| short="$(git rev-parse --short HEAD)" | |
| version="dev-${short}" | |
| fi | |
| { | |
| echo "checkout-ref=$checkout_ref" | |
| echo "tag=$tag" | |
| echo "version=$version" | |
| } >> "$GITHUB_OUTPUT" | |
| windows-portable: | |
| name: Windows portable package | |
| runs-on: windows-latest | |
| timeout-minutes: 40 | |
| needs: resolve-release | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.resolve-release.outputs.checkout-ref }} | |
| fetch-depth: 0 | |
| - name: Build Windows portable package | |
| shell: pwsh | |
| run: ./packaging/windows/build-portable.ps1 -OutputDir dist -Version "${{ needs.resolve-release.outputs.version }}" | |
| - name: Upload Windows portable artifact | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ilab-gpt-conjure-windows-portable-x64 | |
| path: | | |
| dist/*.zip | |
| dist/*.sha256.txt | |
| dist/portable-build.json | |
| if-no-files-found: error | |
| windows-app-x64: | |
| name: Windows standard app ZIP | |
| runs-on: windows-latest | |
| timeout-minutes: 40 | |
| needs: resolve-release | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.resolve-release.outputs.checkout-ref }} | |
| fetch-depth: 0 | |
| - name: Build Windows standard app package | |
| shell: pwsh | |
| run: ./packaging/windows/build-app.ps1 -OutputDir dist -Version "${{ needs.resolve-release.outputs.version }}" | |
| - name: Upload Windows standard app artifact | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ilab-gpt-conjure-windows-app-x64 | |
| path: | | |
| dist/*.zip | |
| dist/*.sha256.txt | |
| dist/windows-app-build.json | |
| if-no-files-found: error | |
| macos-portable: | |
| name: macOS ${{ matrix.arch }} portable package | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 40 | |
| needs: resolve-release | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: arm64 | |
| runner: macos-15 | |
| artifact: ilab-gpt-conjure-macos-portable-arm64 | |
| - arch: x64 | |
| runner: macos-15-intel | |
| artifact: ilab-gpt-conjure-macos-portable-x64 | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.resolve-release.outputs.checkout-ref }} | |
| fetch-depth: 0 | |
| - name: Build macOS portable package | |
| shell: bash | |
| run: ./packaging/macos/build-portable.sh --output-dir "dist/${{ matrix.arch }}" --version "${{ needs.resolve-release.outputs.version }}" | |
| - name: Verify macOS package architecture | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| test -f "dist/${{ matrix.arch }}/macos-portable-build.json" | |
| grep -q '"architecture": "${{ matrix.arch }}"' "dist/${{ matrix.arch }}/macos-portable-build.json" | |
| ls "dist/${{ matrix.arch }}"/ilab-gpt-conjure_macos_portable_${{ matrix.arch }}_*.zip | |
| - name: Upload macOS portable artifact | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: | | |
| dist/${{ matrix.arch }}/*.zip | |
| dist/${{ matrix.arch }}/*.sha256.txt | |
| dist/${{ matrix.arch }}/macos-portable-build.json | |
| if-no-files-found: error | |
| macos-dmg: | |
| name: macOS ${{ matrix.arch }} standard DMG | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 40 | |
| needs: resolve-release | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: arm64 | |
| runner: macos-15 | |
| artifact: ilab-gpt-conjure-macos-dmg-arm64 | |
| - arch: x64 | |
| runner: macos-15-intel | |
| artifact: ilab-gpt-conjure-macos-dmg-x64 | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.resolve-release.outputs.checkout-ref }} | |
| fetch-depth: 0 | |
| - name: Build macOS standard app DMG | |
| shell: bash | |
| run: ./packaging/macos/build-app.sh --output-dir "dist/${{ matrix.arch }}" --version "${{ needs.resolve-release.outputs.version }}" | |
| - name: Verify macOS DMG architecture | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| test -f "dist/${{ matrix.arch }}/macos-app-build.json" | |
| grep -q '"architecture": "${{ matrix.arch }}"' "dist/${{ matrix.arch }}/macos-app-build.json" | |
| ls "dist/${{ matrix.arch }}"/iLab-GPT-CONJURE-macos-${{ matrix.arch }}-*.dmg | |
| - name: Upload macOS DMG artifact | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: | | |
| dist/${{ matrix.arch }}/*.dmg | |
| dist/${{ matrix.arch }}/*.sha256.txt | |
| dist/${{ matrix.arch }}/macos-app-build.json | |
| if-no-files-found: error | |
| github-release-assets: | |
| name: Upload GitHub release assets | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: | |
| - resolve-release | |
| - windows-portable | |
| - windows-app-x64 | |
| - macos-portable | |
| - macos-dmg | |
| if: ${{ needs.resolve-release.outputs.tag != '' }} | |
| steps: | |
| - name: Check out release notes | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.resolve-release.outputs.checkout-ref }} | |
| fetch-depth: 1 | |
| - name: Download package artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: dist-assets | |
| - name: Upload release assets | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_TAG: ${{ needs.resolve-release.outputs.tag }} | |
| ILAB_CONJURE_UPDATE_SIGNING_PRIVATE_KEY_B64: ${{ secrets.ILAB_CONJURE_UPDATE_SIGNING_PRIVATE_KEY_B64 }} | |
| run: | | |
| set -euo pipefail | |
| release_version="${RELEASE_TAG#v}" | |
| release_summary="$(awk ' | |
| /^当前版本:/ || /^本版重点:/ { | |
| print "" | |
| next | |
| } | |
| /^本版详情:/ { | |
| in_details = 1 | |
| next | |
| } | |
| in_details && /^## / { | |
| in_details = 0 | |
| } | |
| in_details { | |
| } | |
| ' RELEASES.md)" | |
| if [[ -z "${release_summary//[[:space:]]/}" ]]; then | |
| release_summary="此版本包含 iLab CONJURE 对应 tag 的源码和免安装一键包。" | |
| fi | |
| cat > release-notes.md <<EOF | |
| ## 发布说明 | |
| ${release_summary} | |
| 完整版本说明见 [RELEASES.md](https://github.com/${GITHUB_REPOSITORY}/blob/${RELEASE_TAG}/RELEASES.md)。 | |
| ## 推荐下载 | |
| - macOS Apple Silicon:iLab-GPT-CONJURE-macos-arm64-${release_version}.dmg | |
| - macOS Intel:iLab-GPT-CONJURE-macos-x64-${release_version}.dmg | |
| - Windows x64:iLab-GPT-CONJURE-windows-x64_${release_version}.zip | |
| 新用户建议优先下载标准包。v0.5.4 及更早 portable 用户首次升级到 0.5.5 时,建议手动下载完整标准包或完整 portable 包;旧更新器不保证能安装新的小兔子启动器、标准 .app / .exe 入口和迁移助手。 | |
| ## 免安装一键包 | |
| - Windows x64:ilab-gpt-conjure_windows_portable_x64_${release_version}.zip | |
| - macOS Apple Silicon:ilab-gpt-conjure_macos_portable_arm64_${release_version}.zip | |
| - macOS Intel:ilab-gpt-conjure_macos_portable_x64_${release_version}.zip | |
| 包含标准更新助手的 macOS App 可在用户确认后校验 signed latest.json 与 DMG SHA256,自动覆盖并重新启动;旧版 macOS App 需要先手动安装首个支持版本,Windows 标准 ZIP 仍手动覆盖。portable 包继续使用原有自动替换更新器。macOS 包暂未签名、未 notarize。如果 macOS 拦截启动,可以右键或 Control-click App,选择 Open;portable zip 仍可对解压目录执行 xattr -dr com.apple.quarantine。 | |
| EOF | |
| if ! gh release view "$RELEASE_TAG" >/dev/null 2>&1; then | |
| gh release create "$RELEASE_TAG" --title "$RELEASE_TAG" --notes-file release-notes.md | |
| else | |
| gh release edit "$RELEASE_TAG" --title "$RELEASE_TAG" --notes-file release-notes.md | |
| fi | |
| python scripts/build-update-manifest.py \ | |
| --assets-dir dist-assets \ | |
| --version "$release_version" \ | |
| --tag "$RELEASE_TAG" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --notes-file release-notes.md \ | |
| --signing-private-key-b64 "$ILAB_CONJURE_UPDATE_SIGNING_PRIVATE_KEY_B64" \ | |
| --require-signature \ | |
| --output dist-assets/latest.json | |
| mapfile -d '' assets < <(find dist-assets -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.sha256.txt" -o -name "latest.json" \) -print0) | |
| if [[ "${#assets[@]}" -eq 0 ]]; then | |
| echo "Portable assets were not created." >&2 | |
| exit 1 | |
| fi | |
| gh release upload "$RELEASE_TAG" "${assets[@]}" --clobber |