release: prepare 1.1.0 and complete organization migration #95
Workflow file for this run
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: Signed Desktop Updates | |
| on: | |
| pull_request: | |
| paths: | |
| - 'apps/desktop/**' | |
| - 'scripts/desktop_*' | |
| - '.github/workflows/desktop-updater.yml' | |
| release: | |
| types: [published] | |
| schedule: | |
| - cron: "17 */6 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: signed-desktop-${{ github.event_name == 'release' && 'stable' || 'main' }} | |
| cancel-in-progress: false | |
| jobs: | |
| validate: | |
| runs-on: macos-15 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: apps/desktop/loopx-control-plane/src-tauri -> target | |
| - run: python3 scripts/desktop_runtime_bundle.py | |
| - run: python3 -m unittest discover -s tests/desktop -p 'test_*.py' | |
| - run: cargo test --locked && cargo clippy --all-targets --locked -- -D warnings | |
| working-directory: apps/desktop/loopx-control-plane/src-tauri | |
| identity: | |
| if: github.repository == 'loopx-project/loopx' && ((github.event_name == 'release' && !github.event.release.prerelease && startsWith(github.event.release.tag_name, 'v')) || (github.event_name != 'release' && github.ref == 'refs/heads/main')) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| ref: ${{ steps.identity.outputs.ref }} | |
| version: ${{ steps.identity.outputs.version }} | |
| tag: ${{ steps.identity.outputs.tag }} | |
| channel: ${{ steps.identity.outputs.channel }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.release.tag_name || 'main' }} | |
| - id: identity | |
| env: | |
| RELEASE_TAG: ${{ github.event.release.tag_name }} | |
| IS_PRERELEASE: ${{ github.event.release.prerelease || false }} | |
| run: | | |
| set -euo pipefail | |
| echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| if [ -n "$RELEASE_TAG" ]; then | |
| channel="$(python3 scripts/desktop_update_feed.py --classify-release "$RELEASE_TAG" --prerelease "$IS_PRERELEASE")" | |
| echo "channel=$channel" >> "$GITHUB_OUTPUT" | |
| echo "version=${RELEASE_TAG#v}" >> "$GITHUB_OUTPUT" | |
| echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "channel=main" >> "$GITHUB_OUTPUT" | |
| echo "version=0.0.0-main.${GITHUB_RUN_ID}.${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_OUTPUT" | |
| echo "tag=desktop-main-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_OUTPUT" | |
| fi | |
| build: | |
| needs: identity | |
| if: needs.identity.outputs.channel == 'stable' || needs.identity.outputs.channel == 'main' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: macos-15 | |
| bundle: app | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.identity.outputs.ref }} | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "24" | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: apps/desktop/loopx-control-plane/src-tauri -> target | |
| - run: npm ci | |
| working-directory: apps/desktop/loopx-control-plane | |
| - name: Build signed App and exact bundled runtime | |
| working-directory: apps/desktop/loopx-control-plane | |
| shell: bash | |
| env: | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: "" | |
| VERSION: ${{ needs.identity.outputs.version }} | |
| BUNDLE: ${{ matrix.bundle }} | |
| run: | | |
| set -euo pipefail | |
| test -n "$TAURI_SIGNING_PRIVATE_KEY" | |
| npm run build -- --bundles "$BUNDLE" --config "{\"version\":\"$VERSION\",\"bundle\":{\"createUpdaterArtifacts\":true,\"macOS\":{\"signingIdentity\":\"-\"}}}" | |
| - name: Collect native updater artifacts | |
| shell: bash | |
| run: | | |
| mkdir -p dist/updater | |
| find apps/desktop/loopx-control-plane/src-tauri/target/release/bundle -type f \( -name '*.app.tar.gz' -o -name '*.app.tar.gz.sig' -o -name '*-setup.exe' -o -name '*-setup.exe.sig' \) -exec cp {} dist/updater/ \; | |
| for artifact in dist/updater/*-setup.exe*; do | |
| if [ -f "$artifact" ]; then mv "$artifact" "${artifact/-setup.exe/-updater-setup.exe}"; fi | |
| done | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: signed-updater-${{ matrix.runner }} | |
| path: dist/updater/* | |
| if-no-files-found: error | |
| publish: | |
| needs: [identity, build, validate] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ needs.identity.outputs.ref }} | |
| - uses: actions/download-artifact@v7 | |
| with: | |
| pattern: signed-updater-* | |
| path: dist/updater | |
| merge-multiple: true | |
| - name: Generate complete feed | |
| env: | |
| VERSION: ${{ needs.identity.outputs.version }} | |
| RELEASE_TAG: ${{ needs.identity.outputs.tag }} | |
| run: python scripts/desktop_update_feed.py --directory dist/updater --version "$VERSION" --tag "$RELEASE_TAG" | |
| - name: Upload immutable artifacts, publish channel pointer last | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: ${{ needs.identity.outputs.tag }} | |
| SOURCE_REF: ${{ needs.identity.outputs.ref }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| CHANNEL: ${{ needs.identity.outputs.channel }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$EVENT_NAME" != release ]; then | |
| gh release create "$RELEASE_TAG" --prerelease --target "$SOURCE_REF" --title "Desktop main $SOURCE_REF" --notes "Signed App and matching runtime preview." | |
| fi | |
| for artifact in dist/updater/*.gz dist/updater/*.sig; do | |
| gh release upload "$RELEASE_TAG" "$artifact" | |
| done | |
| gh release upload "$RELEASE_TAG" dist/updater/desktop-updater.json --clobber | |
| # Channel pointers are independent of repository Latest, which also | |
| # serves separately versioned plugin releases. Advance only after the | |
| # immutable signed assets and feed are completely uploaded. | |
| pointer="desktop-$CHANNEL" | |
| gh release view "$pointer" >/dev/null 2>&1 || gh release create "$pointer" --prerelease --latest=false --target "$SOURCE_REF" --title "Desktop $CHANNEL channel" --notes "Pointer to the latest complete signed desktop build." | |
| gh release upload "$pointer" dist/updater/desktop-updater.json --clobber |