feat(desktop): improve browser launch and panel startup UX #51
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: Container (panel) | |
| # The Stitch (web UI) image. Separate from the bot image so a frontend change doesn't | |
| # rebuild and republish the bot, and so a broken panel build can never hold up a | |
| # bot release. | |
| # | |
| # Multi-arch: linux/amd64 and linux/arm64 on native runners, then a merge job | |
| # stitches the digests into one manifest list. Apple Silicon hosts need the | |
| # arm64 variant — a single-arch amd64 :latest fails with | |
| # "no matching manifest for linux/arm64/v8". | |
| on: | |
| pull_request: | |
| paths: | |
| - '.github/workflows/container-panel.yml' | |
| - 'Cargo.lock' | |
| - 'Cargo.toml' | |
| - 'Dockerfile.panel' | |
| - '.dockerignore' | |
| - 'src/**' | |
| - 'web/**' | |
| push: | |
| branches: [main] | |
| paths: | |
| - '.github/workflows/container-panel.yml' | |
| - 'Cargo.lock' | |
| - 'Cargo.toml' | |
| - 'Dockerfile.panel' | |
| - '.dockerignore' | |
| - 'src/**' | |
| - 'web/**' | |
| workflow_dispatch: | |
| env: | |
| REGISTRY_IMAGE: ghcr.io/textile-protocol/textile-stitch-panel | |
| permissions: | |
| contents: read | |
| packages: write | |
| concurrency: | |
| group: container-panel-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: linux/amd64 | |
| runner: ubuntu-22.04 | |
| - platform: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| steps: | |
| - name: Prepare | |
| run: | | |
| platform='${{ matrix.platform }}' | |
| echo "PLATFORM_PAIR=${platform//\//-}" >> "$GITHUB_ENV" | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| with: | |
| persist-credentials: false | |
| - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4 | |
| - name: Log in to GitHub Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6 | |
| with: | |
| images: ${{ env.REGISTRY_IMAGE }} | |
| - name: Build container image | |
| id: build | |
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 | |
| with: | |
| context: . | |
| file: Dockerfile.panel | |
| platforms: ${{ matrix.platform }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # PRs only compile-check each arch. Digests are pushed (untagged) on | |
| # main so the merge job can assemble the multi-arch manifest list. | |
| outputs: ${{ github.event_name == 'pull_request' && 'type=cacheonly' || format('type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.REGISTRY_IMAGE) }} | |
| cache-from: type=gha,scope=panel-${{ env.PLATFORM_PAIR }} | |
| # Only write the shared build cache from trusted (non-PR) runs. A PR can | |
| # run arbitrary code during the build; letting it populate the gha cache | |
| # would let it poison a layer later consumed by the main publish build. | |
| cache-to: ${{ github.event_name != 'pull_request' && format('type=gha,mode=max,scope=panel-{0}', env.PLATFORM_PAIR) || '' }} | |
| - name: Export digest | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| mkdir -p "${{ runner.temp }}/digests" | |
| digest='${{ steps.build.outputs.digest }}' | |
| touch "${{ runner.temp }}/digests/${digest#sha256:}" | |
| - name: Upload digest | |
| if: github.event_name != 'pull_request' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: digests-panel-${{ env.PLATFORM_PAIR }} | |
| path: ${{ runner.temp }}/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| merge: | |
| needs: build | |
| if: github.event_name != 'pull_request' | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - name: Download digests | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| path: ${{ runner.temp }}/digests | |
| pattern: digests-panel-* | |
| merge-multiple: true | |
| - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6 | |
| with: | |
| images: ${{ env.REGISTRY_IMAGE }} | |
| tags: | | |
| type=sha,prefix=sha- | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=ref,event=branch | |
| - name: Create manifest list and push | |
| working-directory: ${{ runner.temp }}/digests | |
| run: | | |
| # Require both digests before tagging :latest — a partial publish would | |
| # strand Apple Silicon (arm64) or Linux servers (amd64). | |
| set -euo pipefail | |
| count="$(find . -type f | wc -l | tr -d ' ')" | |
| if [ "$count" -lt 2 ]; then | |
| echo "error: expected digests for linux/amd64 and linux/arm64, found $count" >&2 | |
| find . -type f -print >&2 || true | |
| exit 1 | |
| fi | |
| # printf '%s@sha256:%s ' IMAGE d1 d2 mis-pairs args (IMAGE@sha256:d1 | |
| # then d2@sha256:empty). One printf call per digest file. | |
| # shellcheck disable=SC2046 | |
| docker buildx imagetools create \ | |
| $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ | |
| $(for digest in *; do | |
| [ -f "$digest" ] || continue | |
| printf '%s@sha256:%s ' "$REGISTRY_IMAGE" "$digest" | |
| done) | |
| - name: Verify multi-arch manifest | |
| run: | | |
| set -euo pipefail | |
| tag='${{ steps.meta.outputs.version }}' | |
| docker buildx imagetools inspect "${REGISTRY_IMAGE}:${tag}" | |
| raw="$(docker buildx imagetools inspect --raw "${REGISTRY_IMAGE}:${tag}")" | |
| arches="$(printf '%s' "$raw" | jq -r ' | |
| [.manifests[]? | |
| | select(.platform.os == "linux") | |
| | select(.platform.architecture == "amd64" or .platform.architecture == "arm64") | |
| | .platform.architecture] | |
| | unique | sort | join(",") | |
| ')" | |
| if [ "$arches" != "amd64,arm64" ]; then | |
| echo "error: ${REGISTRY_IMAGE}:${tag} must publish linux/amd64 and linux/arm64 (got: ${arches:-none})" >&2 | |
| exit 1 | |
| fi | |
| echo "ok: ${REGISTRY_IMAGE}:${tag} includes linux/amd64 and linux/arm64" |