name: Attach the desktop build to the release

# The Arch package goes to the pacman registry and the APK to the generic
# one, but a release page with nothing on it to download is a release page
# nobody can use.  This builds the plain Linux x86_64 binary and attaches
# it, so "get the latest version" has an answer that needs no package
# manager at all.
#
# **Linux only, and macOS is not an oversight.**  `GOOS=darwin
# CGO_ENABLED=0` fails at `wails/v3/pkg/mac: build constraints exclude all
# Go files` — the darwin backend is Objective-C behind cgo, so a .app
# needs a macOS host, and the runner is a Linux container.  That is
# exactly why the Homebrew formula builds from source on the user's own
# Mac, and it stays the macOS channel.
#
# Windows *does* cross-compile (GOOS=windows CGO_ENABLED=0 succeeds in a
# couple of seconds — nothing in the audio, database or webview path needs
# cgo there), and is deliberately not published: no Windows build of this
# app has ever been run, and no tier here can exercise one.  Shipping it
# would be a promise nothing in this repo can keep.  Revisit when someone
# has actually booted it.

on:
  push:
    tags: ["v*"]
  workflow_dispatch:
    inputs:
      version:
        description: "Version to build and attach (default: the latest v* tag)"
        required: false

concurrency:
  group: desktop-assets-${{ github.ref }}
  cancel-in-progress: true

jobs:
  linux:
    runs-on: ubuntu-latest
    container:
      image: ubuntu:24.04
      volumes:
        - /home/logan/docker/gitea/data/runner/cache/tool:/cache/tool
        - /home/logan/docker/gitea/data/runner/cache/pnpm-store:/cache/pnpm-store
    env:
      PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
      SERVER_URL: ${{ github.server_url }}
      REPO: ${{ github.repository }}
      SHA: ${{ github.sha }}
      REF_NAME: ${{ github.ref_name }}
      DEBIAN_FRONTEND: noninteractive
      GO_VERSION: '1.25.0'
      npm_config_store_dir: /cache/pnpm-store
    steps:
      # The same set ci.yml's check job installs: the app is cgo, and
      # without alsa.pc oto/v3 fails at `pkg-config --cflags -- alsa`
      # before anything is compiled.
      - name: System packages
        run: |
          set -eu
          apt-get update -qq
          apt-get install -y -qq --no-install-recommends \
            ca-certificates curl git jq build-essential pkg-config \
            libwebkitgtk-6.0-dev libgtk-4-dev libasound2-dev

      - name: Clone repo at this commit
        run: |
          set -eu
          git clone --quiet \
            "https://x-access-token:${PACKAGE_TOKEN}@${SERVER_URL#https://}/${REPO}.git" /src
          git -C /src checkout --quiet --detach "$SHA"
          git config --global --add safe.directory /src
          git -C /src log --oneline -1

      - name: Resolve the version
        id: version
        working-directory: /src
        run: |
          set -eu
          v="${{ inputs.version }}"
          if [ -z "$v" ]; then
            case "$REF_NAME" in
              v*) v="$REF_NAME" ;;
              *)  v=$(git describe --tags --abbrev=0 --match 'v[0-9]*') ;;
            esac
          fi
          case "$v" in v*) ;; *) v="v$v" ;; esac

          # v0.0.0 is semantic-release's version floor, not a shipment —
          # see the bootstrap step in release.yml.  Nothing is built for
          # it, and this is a clean skip rather than a failure because a
          # red run against a tag that was never meant to ship is noise.
          if [ "$v" = "v0.0.0" ]; then
            echo "v0.0.0 is the version floor, not a release; nothing to build"
            echo "skip=true" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          # Nor is a prerelease, and this trigger is `v*`, which matches
          # `v0.4.0-beta.1`.  The mildest of the four — assets attach to
          # the prerelease's own Gitea release and no package manager
          # reads them — but four workflows sharing one trigger should
          # share one answer about what a shipment is.
          case "$v" in
            *-*)
              echo "$v is a prerelease; not attaching desktop assets"
              echo "skip=true" >> "$GITHUB_OUTPUT"
              exit 0
              ;;
          esac

          echo "skip=false"   >> "$GITHUB_OUTPUT"
          echo "tag=$v"       >> "$GITHUB_OUTPUT"
          echo "version=${v#v}" >> "$GITHUB_OUTPUT"
          echo "building $v"

      - name: Go toolchain
        if: steps.version.outputs.skip == 'false'
        run: |
          set -eu
          if [ ! -x /cache/tool/go/bin/go ] || ! /cache/tool/go/bin/go version | grep -q "$GO_VERSION"; then
            mkdir -p /cache/tool && rm -rf /cache/tool/go
            curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar -C /cache/tool -xz
          fi
          echo "/cache/tool/go/bin" >> "$GITHUB_PATH"
          /cache/tool/go/bin/go version

      - name: Node toolchain
        if: steps.version.outputs.skip == 'false'
        run: |
          set -eu
          curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
          apt-get install -y -qq --no-install-recommends nodejs
          corepack enable
          node --version

      # `make build-prod` is the production task: -trimpath and -w -s are
      # already in it, so only the version stamp is passed, through the
      # LDFLAGS_EXTRA variable this repo added to build/linux/Taskfile.yml.
      # (`wails3 build` has no -ldflags of its own; that was v2.)
      - name: Build
        if: steps.version.outputs.skip == 'false'
        working-directory: /src
        env:
          TAG: ${{ steps.version.outputs.tag }}
        run: |
          set -eu
          export PATH="/src/scripts/toolbin:$PATH"
          commit=$(git rev-parse --short HEAD)

          go generate ./...
          go tool wails3 task build \
            LDFLAGS_EXTRA="-X 'main.version=${TAG}' -X 'main.commit=${commit}'"

          # Described, never run: main.go has no flag parsing, so any
          # invocation here would try to open a window in a container with
          # no display and hang the job rather than printing a version.
          test -x bin/yellowjacket
          ls -la bin/yellowjacket
          file bin/yellowjacket || true

      # The .desktop file and the icon go in the tarball because without
      # them the binary is a window with no menu entry — the Arch package
      # installs both, and this is the same app for people not using it.
      - name: Package the tarball
        if: steps.version.outputs.skip == 'false'
        working-directory: /src
        env:
          VERSION: ${{ steps.version.outputs.version }}
        run: |
          set -eu
          dir="yellowjacket-${VERSION}-linux-amd64"
          mkdir -p "/tmp/$dir"
          cp bin/yellowjacket "/tmp/$dir/"
          cp packaging/arch/yellowjacket.desktop "/tmp/$dir/"
          cp frontend/src/assets/images/icons/music/compact-disc.svg \
             "/tmp/$dir/yellowjacket.svg"
          tar -C /tmp -czf "/tmp/${dir}.tar.gz" "$dir"
          ls -la "/tmp/${dir}.tar.gz"

      - name: Attach it to the release
        if: steps.version.outputs.skip == 'false'
        working-directory: /src
        env:
          TAG: ${{ steps.version.outputs.tag }}
          VERSION: ${{ steps.version.outputs.version }}
        run: |
          set -eu
          ./scripts/release-asset.sh "$TAG" \
            "/tmp/yellowjacket-${VERSION}-linux-amd64.tar.gz"
