name: Release
on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:

jobs:
  create-release:
    permissions:
      contents: write
    runs-on: ubuntu-latest
    outputs:
      release_id: ${{ steps.create-release.outputs.result }}
      package_version: ${{ steps.get-version.outputs.version }}
    steps:
      - uses: actions/checkout@v5
      - name: setup node
        uses: actions/setup-node@v5
        with:
          node-version: lts/*
      - name: cache npm
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
          restore-keys: ${{ runner.os }}-npm-
      - name: get version
        id: get-version
        run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
      - name: extract changelog
        id: changelog
        run: |
          VERSION="${{ steps.get-version.outputs.version }}"
          # Extract the block between ## [VERSION] and the next ## heading
          BODY=$(awk "/^## \[$VERSION\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md)
          # Store multiline output
          EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
          echo "body<<$EOF" >> $GITHUB_OUTPUT
          echo "$BODY" >> $GITHUB_OUTPUT
          echo "$EOF" >> $GITHUB_OUTPUT
      - name: create release
        id: create-release
        uses: actions/github-script@v7
        env:
          PACKAGE_VERSION: ${{ steps.get-version.outputs.version }}
          CHANGELOG_BODY: ${{ steps.changelog.outputs.body }}
        with:
          script: |
            const tag = `app-v${process.env.PACKAGE_VERSION}`;
            const body = process.env.CHANGELOG_BODY || 'See the assets to download this version and install.';
            try {
              const { data } = await github.rest.repos.getReleaseByTag({
                owner: context.repo.owner,
                repo: context.repo.repo,
                tag,
              });
              await github.rest.repos.updateRelease({
                owner: context.repo.owner,
                repo: context.repo.repo,
                release_id: data.id,
                body,
              });
              return data.id;
            } catch (e) {
              if (e.status !== 404) throw e;
            }
            const { data } = await github.rest.repos.createRelease({
              owner: context.repo.owner,
              repo: context.repo.repo,
              tag_name: tag,
              name: `Psysonic v${process.env.PACKAGE_VERSION}`,
              body,
              draft: true,
              prerelease: false
            });
            return data.id;

  build-macos-windows:
    needs: create-release
    permissions:
      contents: write
    env:
      TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
      TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
    strategy:
      fail-fast: false
      matrix:
        settings:
          - platform: 'macos-latest'
            args: '--target aarch64-apple-darwin'
          - platform: 'macos-latest'
            args: '--target x86_64-apple-darwin'
          - platform: 'windows-latest'
            args: '--bundles nsis'
    runs-on: ${{ matrix.settings.platform }}
    steps:
      - uses: actions/checkout@v5
      - name: setup node
        uses: actions/setup-node@v5
        with:
          node-version: lts/*
      - name: cache npm
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
          restore-keys: ${{ runner.os }}-npm-
      - name: install Rust stable
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.settings.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
      - name: cache cargo
        uses: Swatinem/rust-cache@v2
        with:
          workspaces: src-tauri
      - name: install npm dependencies
        run: npm install
      - uses: tauri-apps/tauri-action@v0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VITE_LASTFM_API_KEY: ${{ secrets.VITE_LASTFM_API_KEY }}
          VITE_LASTFM_API_SECRET: ${{ secrets.VITE_LASTFM_API_SECRET }}
        with:
          releaseId: ${{ needs.create-release.outputs.release_id }}
          args: ${{ matrix.settings.args }}

      - name: sign and upload Windows NSIS updater bundle
        if: matrix.settings.platform == 'windows-latest'
        shell: pwsh
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VERSION: ${{ needs.create-release.outputs.package_version }}
        run: |
          $dir = "src-tauri/target/release/bundle/nsis"
          $exe = (Get-ChildItem $dir -Filter "*.exe")[0].FullName
          $zip = $exe -replace '\.exe$', '.nsis.zip'
          Compress-Archive -Path $exe -DestinationPath $zip -Force
          npx tauri signer sign --private-key "$env:TAURI_SIGNING_PRIVATE_KEY" --password "$env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD" $zip
          gh release upload "app-v$env:VERSION" $zip "$zip.sig" --clobber

      - name: sign and upload macOS bundle signature
        if: matrix.settings.platform == 'macos-latest'
        shell: bash
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VERSION: ${{ needs.create-release.outputs.package_version }}
        run: |
          if [[ "${{ matrix.settings.args }}" == *"aarch64"* ]]; then
            BUNDLE="src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Psysonic.app.tar.gz"
            SIG_NAME="Psysonic_aarch64.app.tar.gz.sig"
          else
            BUNDLE="src-tauri/target/x86_64-apple-darwin/release/bundle/macos/Psysonic.app.tar.gz"
            SIG_NAME="Psysonic_x64.app.tar.gz.sig"
          fi
          npx tauri signer sign --private-key "$TAURI_SIGNING_PRIVATE_KEY" --password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" "$BUNDLE"
          cp "${BUNDLE}.sig" "$SIG_NAME"
          gh release upload "app-v${VERSION}" "$SIG_NAME" --clobber

  build-linux:
    needs: create-release
    permissions:
      contents: write
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v5

      - name: install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf \
            libasound2-dev

      - name: setup node
        uses: actions/setup-node@v5
        with:
          node-version: lts/*

      - name: cache npm
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
          restore-keys: ${{ runner.os }}-npm-

      - name: install Rust stable
        uses: dtolnay/rust-toolchain@stable

      - name: cache cargo
        uses: Swatinem/rust-cache@v2
        with:
          workspaces: src-tauri

      - name: install npm dependencies
        run: npm install

      - name: build
        env:
          VITE_LASTFM_API_KEY: ${{ secrets.VITE_LASTFM_API_KEY }}
          VITE_LASTFM_API_SECRET: ${{ secrets.VITE_LASTFM_API_SECRET }}
        run: npm run tauri:build -- --bundles deb,rpm

      - name: upload Linux artifacts
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          VERSION=${{ needs.create-release.outputs.package_version }}
          find src-tauri/target/release/bundle \
            \( -name "*.deb" -o -name "*.rpm" \) \
            | xargs gh release upload "app-v${VERSION}" --clobber

  generate-manifest:
    needs: [create-release, build-macos-windows]
    permissions:
      contents: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: setup node
        uses: actions/setup-node@v5
        with:
          node-version: lts/*
      - name: cache npm
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
          restore-keys: ${{ runner.os }}-npm-
      - name: generate latest.json
        env:
          VERSION: ${{ needs.create-release.outputs.package_version }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: node scripts/generate-update-manifest.js
      - name: upload latest.json
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          VERSION=${{ needs.create-release.outputs.package_version }}
          gh release upload "app-v${VERSION}" latest.json --clobber
