release desktop UI #647
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: release desktop UI | |
| on: | |
| workflow_run: | |
| workflows: ["release"] | |
| types: [completed] | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: Desktop UI release version (semver, without v) | |
| required: true | |
| type: string | |
| ui_abi: | |
| description: Desktop shell/UI ABI | |
| required: true | |
| default: "1" | |
| type: string | |
| min_shell_version: | |
| description: Minimum compatible desktop shell version | |
| required: true | |
| default: "0.2.94" | |
| type: string | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: desktop-ui-release | |
| cancel-in-progress: false | |
| jobs: | |
| resolve-source: | |
| name: resolve current desktop UI source | |
| if: >- | |
| ${{ | |
| (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') || | |
| (github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main') | |
| }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| source_sha: ${{ steps.source.outputs.source_sha }} | |
| current: ${{ steps.source.outputs.current }} | |
| steps: | |
| - name: resolve source against current main | |
| id: source | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| SOURCE_SHA: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} | |
| run: | | |
| set -euo pipefail | |
| echo "source_sha=$SOURCE_SHA" >> "$GITHUB_OUTPUT" | |
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | |
| echo "current=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| CURRENT_MAIN_SHA="$(gh api "repos/$GITHUB_REPOSITORY/commits/main" --jq .sha)" | |
| if [ "$SOURCE_SHA" = "$CURRENT_MAIN_SHA" ]; then | |
| echo "current=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "current=false" >> "$GITHUB_OUTPUT" | |
| echo "Skipping superseded Desktop UI source $SOURCE_SHA; current main is $CURRENT_MAIN_SHA" | |
| fi | |
| build-sign-publish: | |
| name: build, sign, and publish desktop UI | |
| needs: resolve-source | |
| if: needs.resolve-source.outputs.current == 'true' | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: release | |
| permissions: | |
| contents: write | |
| env: | |
| SOURCE_SHA: ${{ needs.resolve-source.outputs.source_sha }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ env.SOURCE_SHA }} | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: validate and derive release metadata | |
| env: | |
| INPUT_VERSION: ${{ inputs.version }} | |
| INPUT_UI_ABI: ${{ inputs.ui_abi }} | |
| INPUT_MIN_SHELL_VERSION: ${{ inputs.min_shell_version }} | |
| run: | | |
| set -euo pipefail | |
| semver='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$' | |
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | |
| UI_VERSION="$INPUT_VERSION" | |
| else | |
| UI_VERSION="0.0.${GITHUB_RUN_NUMBER}" | |
| fi | |
| UI_ABI="${INPUT_UI_ABI:-1}" | |
| MIN_SHELL_VERSION="${INPUT_MIN_SHELL_VERSION:-0.2.94}" | |
| [[ "$UI_VERSION" =~ $semver ]] || { echo "::error::invalid Desktop UI version"; exit 1; } | |
| [[ "$MIN_SHELL_VERSION" =~ $semver ]] || { echo "::error::invalid minimum shell version"; exit 1; } | |
| [[ "$UI_ABI" =~ ^(0|[1-9][0-9]*)$ ]] || { echo "::error::invalid UI ABI"; exit 1; } | |
| if [ "$UI_ABI" -lt 1 ] || [ "$UI_ABI" -gt 65535 ]; then | |
| echo "::error::UI ABI must be between 1 and 65535" | |
| exit 1 | |
| fi | |
| ASSET="agentparty-desktop-ui-v${UI_VERSION}.tar.gz" | |
| VERSIONED_MANIFEST="agentparty-desktop-ui-v${UI_VERSION}.json" | |
| PUBLISHED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| { | |
| echo "UI_VERSION=$UI_VERSION" | |
| echo "UI_ABI=$UI_ABI" | |
| echo "MIN_SHELL_VERSION=$MIN_SHELL_VERSION" | |
| echo "ASSET=$ASSET" | |
| echo "VERSIONED_MANIFEST=$VERSIONED_MANIFEST" | |
| echo "PUBLISHED_AT=$PUBLISHED_AT" | |
| } >> "$GITHUB_ENV" | |
| - name: build production web UI | |
| working-directory: web | |
| run: VITE_API_BASE=https://agentparty.leeguoo.com bunx vite build | |
| - name: bundle Desktop UI deterministically | |
| run: | | |
| bun scripts/desktop-ui-bundle.ts \ | |
| --source web/dist \ | |
| --output "dist/$ASSET" | |
| - name: decide whether Desktop UI content changed | |
| id: publication | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| echo "reason=manual" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| mkdir -p dist/current | |
| if ! gh release download desktop-ui \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --pattern desktop-ui.json \ | |
| --dir dist/current; then | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| echo "reason=current-missing" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| UPDATER_PUBLIC_KEY="$(jq -er '.plugins.updater.pubkey' desktop/src-tauri/tauri.conf.json)" | |
| decision="$(bun scripts/desktop-ui-publication.ts \ | |
| --archive "dist/$ASSET" \ | |
| --manifest dist/current/desktop-ui.json \ | |
| --public-key "$UPDATER_PUBLIC_KEY" \ | |
| --ui-abi "$UI_ABI" \ | |
| --min-shell-version "$MIN_SHELL_VERSION")" | |
| publish="$(jq -er '.publish | tostring' <<<"$decision")" | |
| reason="$(jq -er .reason <<<"$decision")" | |
| echo "publish=$publish" >> "$GITHUB_OUTPUT" | |
| echo "reason=$reason" >> "$GITHUB_OUTPUT" | |
| echo "Desktop UI publication decision: $reason" | |
| - name: sign Desktop UI archive | |
| if: steps.publication.outputs.publish == 'true' | |
| working-directory: desktop | |
| env: | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_V2 }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD_V2 }} | |
| run: | | |
| set -euo pipefail | |
| [ -n "$TAURI_SIGNING_PRIVATE_KEY" ] || { echo "::error::TAURI_SIGNING_PRIVATE_KEY_V2 is required"; exit 1; } | |
| [ -n "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" ] || { echo "::error::TAURI_SIGNING_PRIVATE_KEY_PASSWORD_V2 is required"; exit 1; } | |
| bunx tauri signer sign "../dist/$ASSET" | |
| [ -s "../dist/$ASSET.sig" ] || { echo "::error::Desktop UI signature was not generated"; exit 1; } | |
| - name: generate Desktop UI manifest | |
| if: steps.publication.outputs.publish == 'true' | |
| run: | | |
| bun scripts/desktop-ui-manifest.ts \ | |
| --version "$UI_VERSION" \ | |
| --ui-abi "$UI_ABI" \ | |
| --min-shell-version "$MIN_SHELL_VERSION" \ | |
| --build-id "$SOURCE_SHA" \ | |
| --published-at "$PUBLISHED_AT" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --archive "dist/$ASSET" \ | |
| --output dist/desktop-ui-manifest-payload.json | |
| - name: sign Desktop UI manifest | |
| if: steps.publication.outputs.publish == 'true' | |
| working-directory: desktop | |
| env: | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_V2 }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD_V2 }} | |
| run: | | |
| set -euo pipefail | |
| bunx tauri signer sign "../dist/desktop-ui-manifest-payload.json" | |
| [ -s ../dist/desktop-ui-manifest-payload.json.sig ] || { echo "::error::Desktop UI manifest signature was not generated"; exit 1; } | |
| - name: finalize signed Desktop UI manifest envelope | |
| if: steps.publication.outputs.publish == 'true' | |
| run: | | |
| bun scripts/desktop-ui-manifest.ts \ | |
| --payload dist/desktop-ui-manifest-payload.json \ | |
| --signature dist/desktop-ui-manifest-payload.json.sig \ | |
| --output "dist/$VERSIONED_MANIFEST" | |
| cp "dist/$VERSIONED_MANIFEST" dist/desktop-ui.json | |
| # GitHub caps a Release at 1000 assets; every publication appends four versioned files, | |
| # so prune before uploading or the upload itself is what hits the ceiling (#974). | |
| - name: prune desktop-ui release assets | |
| if: steps.publication.outputs.publish == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| DESKTOP_UI_KEEP_VERSIONS: "60" | |
| run: | | |
| set -euo pipefail | |
| bun scripts/desktop-ui-prune-assets.ts \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --keep "$DESKTOP_UI_KEEP_VERSIONS" \ | |
| --incoming "$UI_VERSION" | |
| - name: publish fixed desktop-ui release channel | |
| if: steps.publication.outputs.publish == 'true' | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: desktop-ui | |
| name: AgentParty Desktop UI | |
| body: Independent signed Desktop UI bundle channel. Use desktop-ui.json for the current manifest. | |
| prerelease: true | |
| make_latest: false | |
| overwrite_files: true | |
| fail_on_unmatched_files: true | |
| files: | | |
| dist/agentparty-desktop-ui-v*.tar.gz | |
| dist/agentparty-desktop-ui-v*.tar.gz.sha256 | |
| dist/agentparty-desktop-ui-v*.tar.gz.sig | |
| dist/agentparty-desktop-ui-v*.json | |
| dist/desktop-ui.json |