name: Build and Release
on:
  push:
    tags:
      - "v*.*.*"

env:
  RETENTION_INTERMEDIATE_ASSETS: 1
  RETENTION_RELEASE_ASSETS: 30
    
jobs:
  read_metadata:
    name: Read metadata
    runs-on: ubuntu-latest
    outputs:
      formatted_version: ${{ steps.format_metadata.outputs.formatted_version }}
      formatted_build_time: ${{ steps.format_metadata.outputs.formatted_build_time }}
      changelog: ${{ steps.read_changelog.outputs.changelog }}
      version: ${{ steps.format_metadata.outputs.version }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Read and format metadata
        id: format_metadata
        run: |
          # Read the first two lines of the metadata file:
          version=$(sed -n '1p' metadata.txt)
          build_time=$(sed -n '2p' metadata.txt)

          # Format the version:
          formatted_version="v${version}"

          # Format the build time according to RFC 3339:
          formatted_build_time=$(date -d "${build_time}" --utc +'%Y-%m-%dT%H:%M:%SZ')

          # Log the formatted metadata:
          echo "Formatted version: '${formatted_version}'"
          echo "Formatted build time: '${formatted_build_time}'"

          # Set the outputs:
          echo "formatted_version=${formatted_version}" >> "$GITHUB_OUTPUT"
          echo "FORMATTED_VERSION=${formatted_version}" >> $GITHUB_ENV
          echo "formatted_build_time=${formatted_build_time}" >> "$GITHUB_OUTPUT"
          echo "version=${version}" >> "$GITHUB_OUTPUT"
          
      - name: Check tag vs. metadata version
        run: |
          # Ensure, that the tag matches the version in the metadata file:
          if [ "${GITHUB_REF}" != "refs/tags/${FORMATTED_VERSION}" ]; then
              echo "Tag '${GITHUB_REF}' does not match the version in the metadata file '${FORMATTED_VERSION}'"
              exit 1
          else
              echo "Tag '${GITHUB_REF}' matches the version in the metadata file '${FORMATTED_VERSION}'"
          fi

      - name: Read changelog
        id: read_changelog
        run: |
          # Ensure, that the matching changelog file for the current version exists:
          if [ ! -f "app/MindWork AI Studio/wwwroot/changelog/${FORMATTED_VERSION}.md" ]; then
              echo "Changelog file 'app/MindWork AI Studio/wwwroot/changelog/${FORMATTED_VERSION}.md' not found"
              exit 1
          else
              echo "Changelog file '${FORMATTED_VERSION}.md' found"
          fi
          
          # Read the changelog file:
          changelog=$(cat "app/MindWork AI Studio/wwwroot/changelog/${FORMATTED_VERSION}.md")

          # Set the output:
          echo "changelog<<EOOOF" >> "$GITHUB_OUTPUT"
          echo "${changelog}" >> "$GITHUB_OUTPUT"
          echo "EOOOF" >> "$GITHUB_OUTPUT"

  build_main:
    name: Build app (${{ matrix.dotnet_runtime }})
    needs: read_metadata
    
    strategy:
      fail-fast: true
      matrix:
        include:
          - platform: 'macos-latest' # for ARM-based macOS (M1 and above)
            rust_target: 'aarch64-apple-darwin'
            dotnet_runtime: 'osx-arm64'
            dotnet_name_postfix: '-aarch64-apple-darwin'
            tauri_bundle: 'dmg updater'

          - platform: 'macos-latest' # for Intel-based macOS
            rust_target: 'x86_64-apple-darwin'
            dotnet_runtime: 'osx-x64'
            dotnet_name_postfix: '-x86_64-apple-darwin'
            tauri_bundle: 'dmg updater'

          - platform: 'ubuntu-22.04' # for x86-based Linux
            rust_target: 'x86_64-unknown-linux-gnu'
            dotnet_runtime: 'linux-x64'
            dotnet_name_postfix: '-x86_64-unknown-linux-gnu'
            tauri_bundle: 'appimage deb updater'

          - platform: 'windows-latest' # for x86-based Windows
            rust_target: 'x86_64-pc-windows-msvc'
            dotnet_runtime: 'win-x64'
            dotnet_name_postfix: '-x86_64-pc-windows-msvc.exe'
            tauri_bundle: 'nsis updater'

          - platform: 'windows-latest' # for ARM-based Windows
            rust_target: 'aarch64-pc-windows-msvc'
            dotnet_runtime: 'win-arm64'
            dotnet_name_postfix: '-aarch64-pc-windows-msvc.exe'
            tauri_bundle: 'nsis updater'
    
    runs-on: ${{ matrix.platform }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          lfs: false
          
      - name: Read and format metadata (Unix)
        if: matrix.platform != 'windows-latest'
        run: |
          # Read the lines of the metadata file:
          app_version=$(sed -n '1p' metadata.txt)
          build_time=$(sed -n '2p' metadata.txt)
          build_number=$(sed -n '3p' metadata.txt)
          
          # Next line is the .NET SDK version.
          # The format is '8.0.205 (commit 3e1383b780)'.
          # We extract only the version number, though:
          dotnet_sdk_version=$(sed -n '4p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
          
          # Next line is the .NET runtime version.
          # The format is '8.0.5 (commit 087e15321b)'.
          # We extract only the version number, though:
          dotnet_runtime_version=$(sed -n '5p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
          
          # Next line is the Rust version.
          # The format is '1.78.0 (commit 9b00956e5)'.
          # We extract only the version number, though:
          rust_version=$(sed -n '6p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
          
          # Next line is the MudBlazor version:
          mud_blazor_version=$(sed -n '7p' metadata.txt)
          
          # Next line is the Tauri version:
          tauri_version=$(sed -n '8p' metadata.txt)

          # Format the app version:
          formatted_app_version="v${app_version}"
          
          # Write the metadata to the environment:
          echo "APP_VERSION=${app_version}" >> $GITHUB_ENV
          echo "FORMATTED_APP_VERSION=${formatted_app_version}" >> $GITHUB_ENV
          echo "BUILD_TIME=${build_time}" >> $GITHUB_ENV
          echo "BUILD_NUMBER=${build_number}" >> $GITHUB_ENV
          echo "DOTNET_SDK_VERSION=${dotnet_sdk_version}" >> $GITHUB_ENV
          echo "DOTNET_RUNTIME_VERSION=${dotnet_runtime_version}" >> $GITHUB_ENV
          echo "RUST_VERSION=${rust_version}" >> $GITHUB_ENV
          echo "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $GITHUB_ENV
          echo "TAURI_VERSION=${tauri_version}" >> $GITHUB_ENV

          # Log the metadata:
          echo "App version: '${formatted_app_version}'"
          echo "Build time: '${build_time}'"
          echo "Build number: '${build_number}'"
          echo ".NET SDK version: '${dotnet_sdk_version}'"
          echo ".NET runtime version: '${dotnet_runtime_version}'"
          echo "Rust version: '${rust_version}'"
          echo "MudBlazor version: '${mud_blazor_version}'"
          echo "Tauri version: '${tauri_version}'"
          
      - name: Read and format metadata (Windows)
        if: matrix.platform == 'windows-latest'
        run: |
          # Read the lines of the metadata file:
          $metadata = Get-Content metadata.txt
          
          $app_version = $metadata[0]
          $build_time = $metadata[1]
          $build_number = $metadata[2]
          
          # Next line is the .NET SDK version.
          # The format is '8.0.205 (commit 3e1383b780)'.
          # We extract only the version number, though:
          $dotnet_sdk_version = ($metadata[3] -match '(\d+\.\d+\.\d+)') | Out-Null; $dotnet_sdk_version = $matches[1]
          
          # Next line is the .NET runtime version.
          # The format is '8.0.5 (commit 087e15321b)'.
          # We extract only the version number, though:
          $dotnet_runtime_version = ($metadata[4] -match '(\d+\.\d+\.\d+)') | Out-Null; $dotnet_runtime_version = $matches[1]
          
          # Next line is the Rust version.
          # The format is '1.78.0 (commit 9b00956e5)'.
          # We extract only the version number, though:
          $rust_version = ($metadata[5] -match '(\d+\.\d+\.\d+)') | Out-Null; $rust_version = $matches[1]
          
          $mud_blazor_version = $metadata[6]
          $tauri_version = $metadata[7]

          # Format the app version:
          $formatted_app_version = "v${app_version}"
          
          # Write the metadata to the environment:
          Write-Output "APP_VERSION=${app_version}" >> $env:GITHUB_ENV
          Write-Output "FORMATTED_APP_VERSION=${formatted_app_version}" >> $env:GITHUB_ENV
          Write-Output "BUILD_TIME=${build_time}" >> $env:GITHUB_ENV
          Write-Output "BUILD_NUMBER=${build_number}" >> $env:GITHUB_ENV
          Write-Output "DOTNET_SDK_VERSION=${dotnet_sdk_version}" >> $env:GITHUB_ENV
          Write-Output "DOTNET_RUNTIME_VERSION=${dotnet_runtime_version}" >> $env:GITHUB_ENV
          Write-Output "RUST_VERSION=${rust_version}" >> $env:GITHUB_ENV
          Write-Output "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $env:GITHUB_ENV
          
          # Log the metadata:
          Write-Output "App version: '${formatted_app_version}'"
          Write-Output "Build time: '${build_time}'"
          Write-Output "Build number: '${build_number}'"
          Write-Output ".NET SDK version: '${dotnet_sdk_version}'"
          Write-Output ".NET runtime version: '${dotnet_runtime_version}'"
          Write-Output "Rust version: '${rust_version}'"
          Write-Output "MudBlazor version: '${mud_blazor_version}'"
          Write-Output "Tauri version: '${tauri_version}'"

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_SDK_VERSION }}
          cache: true
          cache-dependency-path: 'app/MindWork AI Studio/packages.lock.json'
      
      - name: Build .NET project
        run: |
          cd "app/MindWork AI Studio"
          dotnet publish --configuration release --runtime ${{ matrix.dotnet_runtime }} --disable-build-servers --force --output ../../publish/dotnet
          
      - name: Move & rename .NET artifact (Unix)
        if: matrix.platform != 'windows-latest'
        run: |
          mkdir -p "app/MindWork AI Studio/bin/dist"
          cd publish/dotnet
          mv mindworkAIStudio "../../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer${{ matrix.dotnet_name_postfix }}"
          
      - name: Move & rename .NET artifact (Windows)
        if: matrix.platform == 'windows-latest'
        run: |
          New-Item -ItemType Directory -Path "app\MindWork AI Studio\bin\dist" -Force
          cd publish/dotnet
          mv mindworkAIStudio.exe "../../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer${{ matrix.dotnet_name_postfix }}"
          
      - name: Cache Rust
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/bin
            ~/.cargo/git/db/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.rustup/toolchains
            runtime/target
            
          key: target-${{ matrix.dotnet_runtime }}-rust-${{ env.RUST_VERSION }}
            
      - name: Setup Rust (stable)
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ env.RUST_VERSION }}
          targets: ${{ matrix.rust_target }}
      
      - name: Setup dependencies (Ubuntu-specific, x86)
        if: matrix.platform == 'ubuntu-22.04' && contains(matrix.rust_target, 'x86_64')
        run: |
          sudo apt-get update
          sudo apt-get install -y libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
          
      - name: Setup Tauri (Unix)
        if: matrix.platform != 'windows-latest'
        run: |
          if ! cargo tauri --version > /dev/null 2>&1; then
            cargo install --version 1.6.2 tauri-cli
          else
            echo "Tauri is already installed"
          fi
          
      - name: Setup Tauri (Windows)
        if: matrix.platform == 'windows-latest'
        run: |
          if (-not (cargo tauri --version 2>$null)) {
            cargo install --version 1.6.2 tauri-cli
          } else {
            Write-Output "Tauri is already installed"
          }
      
      - name: Build Tauri project (Unix)
        if: matrix.platform != 'windows-latest'
        env:
          PRIVATE_PUBLISH_KEY: ${{ secrets.PRIVATE_PUBLISH_KEY }}
          PRIVATE_PUBLISH_KEY_PASSWORD: ${{ secrets.PRIVATE_PUBLISH_KEY_PASSWORD }}
        run: |
          cd runtime
          export TAURI_PRIVATE_KEY="$PRIVATE_PUBLISH_KEY"
          export TAURI_KEY_PASSWORD="$PRIVATE_PUBLISH_KEY_PASSWORD"
          cargo tauri build --target ${{ matrix.rust_target }} --bundles ${{ matrix.tauri_bundle }}
          
      - name: Build Tauri project (Windows)
        if: matrix.platform == 'windows-latest'
        env:
          PRIVATE_PUBLISH_KEY: ${{ secrets.PRIVATE_PUBLISH_KEY }}
          PRIVATE_PUBLISH_KEY_PASSWORD: ${{ secrets.PRIVATE_PUBLISH_KEY_PASSWORD }}
        run: |
          cd runtime
          $env:TAURI_PRIVATE_KEY="$env:PRIVATE_PUBLISH_KEY"
          $env:TAURI_KEY_PASSWORD="$env:PRIVATE_PUBLISH_KEY_PASSWORD"
          cargo tauri build --target ${{ matrix.rust_target }} --bundles ${{ matrix.tauri_bundle }}

      - name: Upload artifact (macOS)
        if: startsWith(matrix.platform, 'macos')
        uses: actions/upload-artifact@v4
        with:
          name: MindWork AI Studio (macOS ${{ matrix.dotnet_runtime }})
          path: |
            runtime/target/${{ matrix.rust_target }}/release/bundle/dmg/MindWork AI Studio_*.dmg
            runtime/target/${{ matrix.rust_target }}/release/bundle/macos/MindWork AI Studio.app.tar.gz*
          if-no-files-found: error
          retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
          
      - name: Upload artifact (Windows - MSI)
        if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'msi')
        uses: actions/upload-artifact@v4
        with:
          name: MindWork AI Studio (Windows - MSI ${{ matrix.dotnet_runtime }})
          path: |
            runtime/target/${{ matrix.rust_target }}/release/bundle/msi/MindWork AI Studio_*.msi
            runtime/target/${{ matrix.rust_target }}/release/bundle/msi/MindWork AI Studio*msi.zip*
          if-no-files-found: error
          retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
          
      - name: Upload artifact (Windows - NSIS)
        if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'nsis')
        uses: actions/upload-artifact@v4
        with:
          name: MindWork AI Studio (Windows - NSIS ${{ matrix.dotnet_runtime }})
          path: |
            runtime/target/${{ matrix.rust_target }}/release/bundle/nsis/MindWork AI Studio_*.exe
            runtime/target/${{ matrix.rust_target }}/release/bundle/nsis/MindWork AI Studio*nsis.zip*
          if-no-files-found: error
          retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
          
      - name: Upload artifact (Linux - Debian Package)
        if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'deb')
        uses: actions/upload-artifact@v4
        with:
          name: MindWork AI Studio (Linux - deb ${{ matrix.dotnet_runtime }})
          path: |
            runtime/target/${{ matrix.rust_target }}/release/bundle/deb/mind-work-ai-studio_*.deb
          if-no-files-found: error
          retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
          
      - name: Upload artifact (Linux - AppImage)
        if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'appimage')
        uses: actions/upload-artifact@v4
        with:
          name: MindWork AI Studio (Linux - AppImage ${{ matrix.dotnet_runtime }})
          path: |
            runtime/target/${{ matrix.rust_target }}/release/bundle/appimage/mind-work-ai-studio_*.AppImage
            runtime/target/${{ matrix.rust_target }}/release/bundle/appimage/mind-work-ai-studio*AppImage.tar.gz*
          if-no-files-found: error
          retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
          
  build_linux_arm64:
    name: Build app (linux-arm64)
    runs-on: ubuntu-22.04
    needs: read_metadata
    env:
      SKIP: false # allows disabling this long-running job temporarily
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          lfs: false
          
      - name: Read and format metadata
        if: ${{ env.SKIP != 'true' }}
        id: metadata
        run: |
          # Read the lines of the metadata file:
          app_version=$(sed -n '1p' metadata.txt)
          build_time=$(sed -n '2p' metadata.txt)
          build_number=$(sed -n '3p' metadata.txt)

          # Next line is the .NET SDK version.
          # The format is '8.0.205 (commit 3e1383b780)'.
          # We extract only the version number, though:
          dotnet_sdk_version=$(sed -n '4p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')

          # Next line is the .NET runtime version.
          # The format is '8.0.5 (commit 087e15321b)'.
          # We extract only the version number, though:
          dotnet_runtime_version=$(sed -n '5p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')

          # Next line is the Rust version.
          # The format is '1.78.0 (commit 9b00956e5)'.
          # We extract only the version number, though:
          rust_version=$(sed -n '6p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')

          # Next line is the MudBlazor version:
          mud_blazor_version=$(sed -n '7p' metadata.txt)

          # Next line is the Tauri version:
          tauri_version=$(sed -n '8p' metadata.txt)

          # Format the app version:
          formatted_app_version="v${app_version}"

          # Write the metadata to the environment:
          echo "APP_VERSION=${app_version}" >> $GITHUB_ENV
          echo "FORMATTED_APP_VERSION=${formatted_app_version}" >> $GITHUB_ENV
          echo "BUILD_TIME=${build_time}" >> $GITHUB_ENV
          echo "BUILD_NUMBER=${build_number}" >> $GITHUB_ENV
          echo "DOTNET_SDK_VERSION=${dotnet_sdk_version}" >> $GITHUB_ENV
          echo "DOTNET_RUNTIME_VERSION=${dotnet_runtime_version}" >> $GITHUB_ENV
          echo "RUST_VERSION=${rust_version}" >> $GITHUB_ENV
          echo "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $GITHUB_ENV
          echo "TAURI_VERSION=${tauri_version}" >> $GITHUB_ENV

          # Log the metadata:
          echo "App version: '${formatted_app_version}'"
          echo "Build time: '${build_time}'"
          echo "Build number: '${build_number}'"
          echo ".NET SDK version: '${dotnet_sdk_version}'"
          echo ".NET runtime version: '${dotnet_runtime_version}'"
          echo "Rust version: '${rust_version}'"
          echo "MudBlazor version: '${mud_blazor_version}'"
          echo "Tauri version: '${tauri_version}'"
        
      - name: Setup .NET
        if: ${{ env.SKIP != 'true' }}
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_SDK_VERSI }}
          cache: true
          cache-dependency-path: 'app/MindWork AI Studio/packages.lock.json'
          
      - name: Build .NET project
        if: ${{ env.SKIP != 'true' }}
        run: |
          cd "app/MindWork AI Studio"
          dotnet publish --configuration release --runtime linux-arm64 --disable-build-servers --force --output ../../publish/dotnet
          
      - name: Move & rename the .NET artifact
        if: ${{ env.SKIP != 'true' }}
        run: |
          mkdir -p "app/MindWork AI Studio/bin/dist"
          cd publish/dotnet
          mv mindworkAIStudio "../../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer-aarch64-unknown-linux-gnu"
          
      - name: Cache linux arm64 runner image
        if: ${{ env.SKIP != 'true' }}
        uses: actions/cache@v4
        id: linux_arm_cache
        with:
          path: ${{ runner.temp }}/linux_arm_qemu_cache.img
          key: target-linux-arm64-rust-${{ env.RUST_VERSION }}
            
      - name: Build linux arm runner image
        uses: pguyot/arm-runner-action@v2
        id: build-linux-arm-runner
        if: ${{ steps.linux_arm_cache.outputs.cache-hit != 'true' && env.SKIP != 'true' }}
        env:
          RUST_VERSION: ${{ env.RUST_VERSION }}
          TAURI_VERSION: ${{ env.TAURI_VERSION }}
          
        with:
          base_image: dietpi:rpi_armv8_bullseye
          cpu: cortex-a53
          image_additional_mb: 6000 # ~ 6GB
          optimize_image: false
          shell: /bin/bash
          commands: |
            # Rust complains (rightly) that $HOME doesn't match eid home:
            export HOME=/root
            
            # Workaround to CI worker being stuck on Updating crates.io index:
            export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
            
            # Update and upgrade the system:
            apt-get update --yes --allow-releaseinfo-change
            apt-get upgrade --yes
            apt-get autoremove --yes
            apt-get install curl wget --yes
            
            # Install Rust:
            curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y
            source "$HOME/.cargo/env"
            rustup toolchain install $RUST_VERSION
            
            # Install build tools and tauri-cli requirements:
            apt-get install --yes libwebkit2gtk-4.0-dev build-essential libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
            
            # Setup Tauri:
            cargo install --version 1.6.2 tauri-cli
            
      - name: Add the built runner image to the cache
        if: ${{ steps.linux_arm_cache.outputs.cache-hit != 'true' && env.SKIP != 'true' }}
        run: |
          mv ${{ steps.build-linux-arm-runner.outputs.image }} ${{ runner.temp }}/linux_arm_qemu_cache.img
            
      - name: Build Tauri project
        if: ${{ env.SKIP != 'true' }}
        uses: pguyot/arm-runner-action@v2
        id: build-linux-arm
        
        with:
          base_image: file://${{ runner.temp }}/linux_arm_qemu_cache.img
          cpu: cortex-a53
          optimize_image: false
          copy_artifact_path: runtime
          copy_artifact_dest: result
          bind_mount_repository: true
          
          #
          # We do not need to set the PRIVATE_PUBLISH_KEY and PRIVATE_PUBLISH_KEY_PASSWORD here,
          # because we cannot produce the AppImage on arm64. Only the AppImage supports the automatic
          # update feature. The Debian package does not support this feature.
          #
          #PRIVATE_PUBLISH_KEY: ${{ secrets.PRIVATE_PUBLISH_KEY }}
          #PRIVATE_PUBLISH_KEY_PASSWORD: ${{ secrets.PRIVATE_PUBLISH_KEY_PASSWORD }}
          #
          
          shell: /bin/bash
          commands: |
            export HOME=/root
            export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
            source "$HOME/.cargo/env"
            cd runtime
            
            # Try to restore the Rust cache from previous build:
            mkdir -p /rust-cache/target
            rm -fr target
            cp -Rp /rust-cache/target target
            
            cargo tauri build --target aarch64-unknown-linux-gnu --bundles deb
            
            # Save the built libraries for the next job:
            rm -fr /rust-cache/target
            cp -Rp target /rust-cache
          
      - name: Update the runner image to cache the Rust runtime build
        if: ${{ env.SKIP != 'true' }}
        run: |
          mv ${{ steps.build-linux-arm.outputs.image }} $RUNNER_TEMP/linux_arm_qemu_cache.img
      
      - name: Upload artifact (Linux - Debian Package)
        if: ${{ env.SKIP != 'true' }}
        uses: actions/upload-artifact@v4
        with:
          name: MindWork AI Studio (Linux - deb linux-arm64)
          path: |
            result/target/aarch64-unknown-linux-gnu/release/bundle/deb/mind-work-ai-studio_*.deb
          if-no-files-found: warn
          retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
        
  create_release:
    name: Prepare & create release
    runs-on: ubuntu-latest
    needs: [build_main, read_metadata, build_linux_arm64]
    steps:
      - name: Create artifact directory
        run: mkdir -p $GITHUB_WORKSPACE/artifacts
        
      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: ${{ github.workspace }}/artifacts
          merge-multiple: false
          
      - name: Display structure of previously artifacts
        run: ls -Rlhat $GITHUB_WORKSPACE/artifacts
        
      - name: Prepare release assets
        env:
          VERSION: ${{ needs.read_metadata.outputs.version }}
          
        run: |
          RELEASE_DIR="$GITHUB_WORKSPACE/release/assets"
          
          # Ensure the release directory exists:
          mkdir -p "$RELEASE_DIR"
          
          # Find and process files in the artifacts directory:
          find "$GITHUB_WORKSPACE/artifacts" -type f | while read -r FILE; do
          
            if [[ "$FILE" == *"osx-x64"* && "$FILE" == *".tar.gz" ]]; then
              TARGET_NAME="MindWork AI Studio_x64.app.tar.gz"
            elif [[ "$FILE" == *"osx-x64"* && "$FILE" == *".tar.gz.sig" ]]; then
              TARGET_NAME="MindWork AI Studio_x64.app.tar.gz.sig"
            elif [[ "$FILE" == *"osx-arm64"* && "$FILE" == *".tar.gz" ]]; then
              TARGET_NAME="MindWork AI Studio_aarch64.app.tar.gz"
            elif [[ "$FILE" == *"osx-arm64"* && "$FILE" == *".tar.gz.sig" ]]; then
              TARGET_NAME="MindWork AI Studio_aarch64.app.tar.gz.sig"
            else
              TARGET_NAME="$(basename "$FILE")"
              TARGET_NAME=$(echo "$TARGET_NAME" | sed "s/_${VERSION}//")
            fi
          
            cp "$FILE" "${RELEASE_DIR}/${TARGET_NAME}"
          done
          
          # Display the structure of the release directory:
          ls -Rlhat $GITHUB_WORKSPACE/release/assets
          
      - name: Create .update directory
        run: mkdir -p $GITHUB_WORKSPACE/.updates
          
      - name: Build platform JSON for latest.json file
        env:
          FORMATTED_VERSION: ${{ needs.read_metadata.outputs.formatted_version }}
        
        run: |
          # Here, we create the JSON object:
          platforms_json=$(jq -n '{}')
          
          # Display the structure of the release directory:
          ls -Rlhat $GITHUB_WORKSPACE/release/assets
          
          # Iterate over all signature files:
          while IFS= read -r -d '' sig_file; do
          
            echo "Processing signature file '$sig_file':"
          
            #
            # Next, we determine the "update platform".
            # We store the result in the $platform variable.
            # 
            # We derive the platform from the signature file name:
            # - platform=darwin-aarch64 when path contains 'aarch64-apple-darwin'
            # - platform=darwin-x86_64 when path contains 'x86_64-apple-darwin'
            # - platform=linux-x86_64 when path contains 'x86_64-unknown-linux-'
            # - platform=windows-x86_64 when path contains 'x86_64-pc-windows-'
            # - platform=windows-aarch64 when path contains 'aarch64-pc-windows-'
            #
            if [[ "$sig_file" == *"aarch64.app"* ]]; then
              platform="darwin-aarch64"
            elif [[ "$sig_file" == *"x64.app"* ]]; then
              platform="darwin-x86_64"
            elif [[ "$sig_file" == *"amd64.AppImage"* ]]; then
              platform="linux-x86_64"
            elif [[ "$sig_file" == *"x64-setup.nsis"* ]]; then
              platform="windows-x86_64"
            elif [[ "$sig_file" == *"arm64-setup.nsis"* ]]; then
              platform="windows-aarch64"
            else
              echo "Platform not recognized: '$sig_file'"
              exit 1
            fi
          
            # Read the signature:
            signature=$(cat "$sig_file")
        
            # Extract the artifact name and create the URL:
            artifact_name=$(basename "$sig_file" .sig)
            
            # Replaces spaces by '.':
            encoded_artifact_name=$(echo "$artifact_name" | sed 's/ /./g')
      
            # Create the URL:
            url="https://github.com/MindWorkAI/AI-Studio/releases/download/$FORMATTED_VERSION/$encoded_artifact_name"
        
            # Build the JSON object:
            platforms_json=$(echo "$platforms_json" | jq --arg platform "$platform" --arg signature "$signature" --arg url "$url" '.[$platform] = {"signature": $signature, "url": $url}')
            
          done < <(find $GITHUB_WORKSPACE/release/assets -type f -name '*.sig' -print0)
          
          # Write the JSON object to a temporary file:
          echo "$platforms_json" > $GITHUB_WORKSPACE/.updates/platforms.json

      - name: Create latest.json
        env:
          FORMATTED_VERSION: ${{ needs.read_metadata.outputs.formatted_version }}
          FORMATTED_BUILD_TIME: ${{ needs.read_metadata.outputs.formatted_build_time }}
          CHANGELOG: ${{ needs.read_metadata.outputs.changelog }}
          
        run: |          
          # Read the platforms JSON, which was created in the previous step:
          platforms=$(cat $GITHUB_WORKSPACE/.updates/platforms.json)
          
          # Replace newlines in changelog with \n
          changelog=$(echo "$CHANGELOG" | awk '{printf "%s\\n", $0}')
          
          # Escape double quotes in changelog:
          changelog=$(echo "$changelog" | sed 's/"/\\"/g')
          
          # Create the latest.json file:
          cat <<EOOOF > $GITHUB_WORKSPACE/release/assets/latest.json
          {
            "version": "$FORMATTED_VERSION",
            "notes": "$changelog",
            "pub_date": "$FORMATTED_BUILD_TIME",
            "platforms": $platforms
          }
          EOOOF

      - name: Show all release assets
        run: ls -Rlhat $GITHUB_WORKSPACE/release/assets

      - name: Display the content of latest.json
        run: cat $GITHUB_WORKSPACE/release/assets/latest.json
        
      - name: Upload release assets
        uses: actions/upload-artifact@v4
        env:
          FORMATTED_VERSION: ${{ needs.read_metadata.outputs.formatted_version }}
          
        with:
          name: MindWork AI Studio ${{ env.FORMATTED_VERSION }} Release
          path: release/assets/
          if-no-files-found: error
          retention-days: ${{ env.RETENTION_RELEASE_ASSETS }}
          
  publish_release:
      name: Publish release
      runs-on: ubuntu-latest
      needs: [read_metadata, create_release]
      
      permissions:
        contents: write
      
      env:
        FORMATTED_VERSION: ${{ needs.read_metadata.outputs.formatted_version }}
        CHANGELOG: ${{ needs.read_metadata.outputs.changelog }}
      
      steps:
          - name: Create release folder
            run: mkdir -p release/assets
          
          - name: Download release assets
            uses: actions/download-artifact@v4  
            with:
                name: MindWork AI Studio ${{ env.FORMATTED_VERSION }} Release
                path: release/assets
                
          - name: Display the content of the release folder
            run: ls -Rlhat release/assets
            
          - name: Scan for threats
            id: virus_total
            uses: crazy-max/ghaction-virustotal@v4
            with:
              vt_api_key: ${{ secrets.VIRUS_TOTAL_KEY }}
              files: release/assets/*
              request_rate: 4
              vt_monitor: false
              github_token: ${{ secrets.GITHUB_TOKEN }}
              update_release_body: true
              
          - name: Append scan results to changelog
            run: |
              changelog=$(cat <<'EOOOOOOOF'
              ${{ env.CHANGELOG }}
              EOOOOOOOF
              )
              
              links="${{ steps.virus_total.outputs.analysis }}"

              # Create a temporary file for the changelog:
              temp_changelog=$(mktemp)

              # Add the new Markdown section:
              echo -e "$changelog" > $temp_changelog
              echo -e "\n\n## Virus scans" >> $temp_changelog
              
              # Split the analysis output by comma:
              IFS=',' read -ra analysis_array <<< "$links"

              # Append each file and link to the changelog:
              for item in "${analysis_array[@]}"; do
                
                # Get the part before the first '=':
                filename="${item%%=*}"
                filename=$(echo $filename | xargs)
              
                # Extract the base name of the file
                base_filename=$(basename "$filename")
              
                # Ignore the latest.json file:
                if [[ "$base_filename" == "latest.json" ]]; then
                  continue
                fi
              
                # Get the part after the first '=':
                link="${item#*=}"
                link=$(echo $link | xargs)
              
                # Append this scan result to the changelog:
                echo "- [$base_filename]($link)" >> $temp_changelog
              done
              
              # Export the modified changelog (using HEREDOC syntax for multi-line support):
              echo "CHANGELOG<<EOOOF" >> $GITHUB_ENV
              cat $temp_changelog >> $GITHUB_ENV
              echo "EOOOF" >> $GITHUB_ENV
            
          - name: Create release
            uses: softprops/action-gh-release@v2
            with:
              prerelease: true
              draft: false
              make_latest: true
              body: ${{ env.CHANGELOG }}
              name: "Release ${{ env.FORMATTED_VERSION }}"
              fail_on_unmatched_files: true
              files: |
                release/assets/*