name: build

on:
  push:
    branches:
      - master
      - stable
    tags:
      - "v*"
  pull_request:

jobs:

# =============================
#       Create release
# =============================

# Create release, but only if it's triggered by tag push.
# On pull requests/commits push, this job will always complete.

  maybe-release:
    runs-on: ubuntu-latest
    steps:
      - name: Clone project
        if: startsWith(github.ref, 'refs/tags/v')
        uses: actions/checkout@v3

      - name: Build changelog
        id: build_changelog
        if: startsWith(github.ref, 'refs/tags/v')
        uses: simplex-chat/release-changelog-builder-action@v5
        with:
          configuration: .github/changelog_conf.json
          failOnError: true
          ignorePreReleases: true
          commitMode: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Create release
        if: startsWith(github.ref, 'refs/tags/v')
        uses: simplex-chat/action-gh-release@v2
        with:
          body: |
            See full changelog [here](https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md).

            Commits:
            ${{ steps.build_changelog.outputs.changelog }}
          prerelease: true
          files: |
            LICENSE
          fail_on_unmatched_files: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# =============================
#        Main build job
# =============================

  build:
    name: "ubuntu-${{ matrix.os }}-${{ matrix.arch }}, GHC: ${{ matrix.ghc }}"
    needs: maybe-release
    env:
      apps: "smp-server xftp-server ntf-server xftp"
    runs-on: ${{ matrix.runner }}
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_HOST_AUTH_METHOD: trust # Allows passwordless access
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          # Maps tcp port 5432 on service container to the host
          - 5432:5432
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: 22.04
            os_underscore: 22_04
            arch: x86-64
            runner: "ubuntu-22.04"
            ghc: "8.10.7"
            should_run: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }}
          - os: 22.04
            os_underscore: 22_04
            arch: x86-64
            runner: "ubuntu-22.04"
            ghc: "9.6.3"
            should_run: true
          - os: 24.04
            os_underscore: 24_04
            arch: x86-64
            runner: "ubuntu-24.04"
            ghc: "9.6.3"
            should_run: true
          - os: 22.04
            os_underscore: 22_04
            arch: aarch64
            runner: "ubuntu-22.04-arm"
            ghc: "9.6.3"
            should_run: true
          - os: 24.04
            os_underscore: 24_04
            arch: aarch64
            runner: "ubuntu-24.04-arm"
            ghc: "9.6.3"
            should_run: true
    steps:
      - name: Clone project
        if: matrix.should_run == true
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        if: matrix.should_run == true
        uses: simplex-chat/docker-setup-buildx-action@v3

      - name: Setup swap
        if: matrix.ghc == '8.10.7' && matrix.should_run == true
        uses: ./.github/actions/swap
        with:
          swap-size-gb: 20

      - name: Install PostgreSQL 15 client tools
        if: matrix.os == '22.04' && matrix.should_run == true
        shell: bash
        run: |
          # Import the repository signing key
          sudo install -d /usr/share/postgresql-common/pgdg
          sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
          # Add the PostgreSQL APT repository
          sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
          # Update repository and install postgresql tools
          sudo apt update
          sudo apt -y install postgresql-client-15

      - name: Build and cache Docker image
        if: matrix.should_run == true
        uses: simplex-chat/docker-build-push-action@v6
        with:
          context: .
          load: true
          file: Dockerfile.build
          tags: build/${{ matrix.os }}:latest
          build-args: |
            TAG=${{ matrix.os }}
            GHC=${{ matrix.ghc }}

      - name: Cache dependencies
        if: matrix.should_run == true
        uses: actions/cache@v4
        with:
          path: |
            ~/.cabal/store
            dist-newstyle
          key: ubuntu-${{ matrix.os }}-${{ matrix.arch }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplexmq.cabal') }}

      - name: Start container
        if: matrix.should_run == true
        shell: bash
        run: |
          docker run -t -d \
            --device /dev/fuse \
            --cap-add SYS_ADMIN \
            --security-opt apparmor:unconfined \
            --name builder \
            -v ~/.cabal:/root/.cabal \
            -v /home/runner/work/_temp:/home/runner/work/_temp \
            -v ${{ github.workspace }}:/project \
            build/${{ matrix.os }}:latest

      - name: Build smp-server (postgresql) and tests
        if: matrix.should_run == true
        shell: docker exec -t builder sh -eu {0}
        run: |
          chmod -fR 777 ~/.cabal ./dist-newstyle || :; git config --global --add safe.directory '*'
          cabal clean
          cabal update
          cabal build --jobs=$(nproc) --enable-tests -fserver_postgres
          mkdir -p /out
          for i in smp-server simplexmq-test; do
            bin=$(find /project/dist-newstyle -name "$i" -type f -executable)
            chmod +x "$bin"
            mv "$bin" /out/
          done
          strip /out/smp-server

      - name: Copy simplexmq-test from container
        if: matrix.should_run == true
        shell: bash
        run: |
          docker cp builder:/out/simplexmq-test .

      - name: Copy smp-server (postgresql) from container and prepare it
        if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
        id: prepare-postgres
        shell: bash
        run: |
          name="smp-server-postgres-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}"
          docker cp builder:/out/smp-server $name

          path="${{ github.workspace }}/$name"
          echo "bin=$path" >> $GITHUB_OUTPUT

          hash="SHA2-256($name)= $(openssl sha256 $path | cut -d' ' -f 2)"
          printf 'hash=%s' "$hash" >> $GITHUB_OUTPUT

      - name: Build everything else (standard)
        if: matrix.should_run == true
        shell: docker exec -t builder sh -eu {0}
        run: |
          cabal build --jobs=$(nproc)
          mkdir -p /out
          for i in ${{ env.apps }}; do
            bin=$(find /project/dist-newstyle -name "$i" -type f -executable)
            strip "$bin"
            chmod +x "$bin"
            mv "$bin" /out/
          done

      - name: Copy binaries from container and prepare them
        id: prepare-regular
        if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
        shell: bash
        run: |
          docker cp builder:/out .

          printf 'bins<<EOF\n' > bins.output
          printf 'hashes<<EOF\n' > hashes.output
          for i in ${{ env.apps }}; do
            name="$i-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}"

            mv ./out/$i ./$name

            path="${{ github.workspace }}/$name"
            hash="SHA2-256($name)= $(openssl sha256 $path | cut -d' ' -f 2)"

            printf '%s\n' "$path" >> bins.output
            printf '%s\n\n' "$hash" >> hashes.output
          done
          printf 'EOF\n' >> bins.output
          printf 'EOF\n' >> hashes.output

          cat bins.output >> "$GITHUB_OUTPUT"
          cat hashes.output >> "$GITHUB_OUTPUT"

      - name: Upload binaries
        if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
        uses: simplex-chat/action-gh-release@v2
        with:
          append_body: true
          prerelease: true
          fail_on_unmatched_files: true
          body: |
            ${{ steps.prepare-regular.outputs.hashes }}
            ${{ steps.prepare-postgres.outputs.hash }}
          files: |
            ${{ steps.prepare-regular.outputs.bins }}
            ${{ steps.prepare-postgres.outputs.bin }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Test
        if: matrix.should_run == true && matrix.arch == 'x86-64'
        timeout-minutes: 120
        shell: bash
        env:
          PGHOST: localhost
        run: |
          i=1
          attempts=1
          ${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3
          while [ "$i" -le "$attempts" ]; do
            if ./simplexmq-test; then
              break
            else
              echo "Attempt $i failed, retrying..."
              i=$((i + 1))
              sleep 1
            fi
          done
          if [ "$i" -gt "$attempts" ]; then
            echo "All "$attempts" attempts failed."
            exit 1
          fi
