name: release
on:
  schedule:
    - cron: "0 4 * * *"
  workflow_dispatch:
    inputs:
      nightly:
        description: Nightly build
        type: boolean
        default: false
      debug:
        description: Debug symbols
        type: boolean
        default: false
      no_tests:
        description: Do not run unit tests
        type: boolean
        default: false
      no_coverage:
        description: Do not run coverage job
        type: boolean
        default: false
      tag:
        description: Optional release tag (normally auto-detected)

jobs:
  get-version:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.get-version.outputs.version }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Get version
        id: get-version
        run: |
          if [[ ${{ github.event_name }} = schedule ]]; then
            version=nightly
          elif [[ "${{ github.event.inputs.nightly }}" = true ]]; then
            version=nightly
          else
            version=`grep -m1 _RELEASE core/init.lua | grep -o "[0-9.]\+[^']*" | tr ' ' '_'`
          fi
          echo "version=$version">> "$GITHUB_OUTPUT"

  build:
    strategy:
      matrix:
        include:
          - os: ubuntu-20.04
          - os: windows-2019
            qt: '6.7'
          - os: macOS-13
            qt: '6.5'
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Checkout textadept-build dependencies
        uses: actions/checkout@v4
        with:
          repository: orbitalquark/textadept-build
          path: textadept-build

      - name: Install Qt (Windows, macOS)
        if: runner.os == 'Windows' || runner.os == 'macOS'
        uses: jurplel/install-qt-action@v3
        with:
          version: ${{ matrix.qt }}
          modules: 'qt5compat'

      - name: Install build dependencies like Qt and GTK (Linux)
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install qtbase5-dev libgtk-3-dev lua5.3 discount --no-install-recommends -y

      - name: Build
        shell: bash
        run: |
          # Move cached dependencies into build/_deps.
          mkdir -p build/_deps && mv textadept-build/* build/_deps && rm -r textadept-build

          # Build.
          type="Release"
          if [[ ${{ github.event_name }} != schedule ]]; then
            if [[ "${{ github.event.inputs.nightly }}" = true ]]; then nightly="-D NIGHTLY=1"; fi
            if [[ "${{ github.event.inputs.debug }}" = true ]]; then type="Debug"; fi
          else
            nightly="-D NIGHTLY=1"
          fi
          if [[ ${{ runner.os }} == Linux ]]; then generate_html="-D GENERATE_HTML=1"; fi
          cmake -S . -B build ${nightly} ${generate_html} -D CMAKE_INSTALL_PREFIX=build/install
          cmake --build build --config ${type} -j
          if [[ ${{ runner.os }} == Linux ]]; then cmake --build build --target html; fi
          cmake --install build --config ${type}
          cmake --build build --target archive
        env:
          VCINSTALLDIR: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: artifacts-${{ matrix.os }}
          path: |
            build/*.tgz
            build/*.zip

  modules:
    runs-on: ubuntu-latest
    needs: get-version
    steps:
      - name: Package modules
        run: |
          mkdir textadept-modules
          modules="debugger export file_diff format lsp lua_repl open_file_mode scratch spellcheck"
          for module in $modules; do
            gh_name="`echo -n $module | sed -e 's/_/-/g;'`"
            gh_prefix="https://github.com/orbitalquark/textadept-$gh_name"
            wget $gh_prefix/releases/download/latest/$module.zip
            unzip -d textadept-modules $module.zip
          done
          zip -r textadept_${{ needs.get-version.outputs.version }}.modules.zip textadept-modules

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: artifacts-modules
          path: '*.zip'

  release:
    runs-on: ubuntu-latest
    needs: [get-version, build, modules]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          merge-multiple: true

      - name: Add HTML documentation
        run: |
          # Extract HTML documentation from Linux.
          version=${{ needs.get-version.outputs.version }}
          tar xzf textadept_$version.linux.tgz --wildcards 'textadept/docs/*.html'

          # Insert it into Windows archive.
          zip -r textadept_$version.win.zip textadept/docs/*.html

          # Install it into macOS archive.
          mkdir -p Textadept.app/Contents/Resources/docs
          cp textadept/docs/*.html Textadept.app/Contents/Resources/docs
          zip -r textadept_$version.macOS.zip Textadept.app/Contents/Resources/docs/*.html
        continue-on-error: true

      - name: Tag
        if: github.ref_name == github.event.repository.default_branch
        run: |
          git tag textadept_${{ needs.get-version.outputs.version }}
          git push -f origin textadept_${{ needs.get-version.outputs.version }}

      - name: Create release log
        run: |
          echo -n "Textadept " > log.md
          echo -n "${{ needs.get-version.outputs.version }} " | tr '_' ' ' >> log.md
          echo \(`date +"%d %b %Y"`\) >> log.md
          if [[ ${{ needs.get-version.outputs.version }} = nightly ]]; then exit 0; fi
          prefix="https://orbitalquark.github.io/textadept"
          echoing=0
          while read line; do
            if [[ $line == \#\#\#* ]]; then
              if [[ $echoing -eq 0 ]]; then
                echoing=1
              else
                exit 0
              fi
            elif [[ $echoing -eq 1 ]]; then
              echo "$line" | sed "s,\(manual\|api\)\.html,$prefix/\0,;"
            fi
          done < docs/changelog.md >> log.md

      - name: Upload release log
        uses: actions/upload-artifact@v4
        with:
          name: artifacts-release-log
          path: log.md

      - name: Create release
        if: github.ref_name == github.event.repository.default_branch
        uses: ncipollo/release-action@v1
        with:
          name: ${{ needs.get-version.outputs.version }}
          tag: textadept_${{ needs.get-version.outputs.version }}
          prerelease: |
            ${{ needs.get-version.outputs.version == 'nightly' ||
              contains(needs.get-version.outputs.version, 'alpha') ||
              contains(needs.get-version.outputs.version, 'beta') }}
          allowUpdates: true
          bodyFile: log.md
          artifacts: textadept_*
          token: ${{ secrets.GITHUB_TOKEN }}

  test:
    if: ${{ ! inputs.no_tests }}
    needs: [build, modules]
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        exe: [textadept/textadept, textadept/textadept-curses]
        include:
          - os: ubuntu-latest
            exe: textadept/textadept-gtk
          - os: macOS-latest
            exe: Textadept.app/Contents/MacOS/textadept_osx
          #- os: macOS-latest
          #  exe: Textadept.app/Contents/MacOS/textadept-curses
      fail-fast: false
    runs-on: ${{ matrix.os }}
    steps:
      - name: Install Qt, Gtk, xterm, matchbox-window-manager, and module dependencies (Linux)
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install qtbase5-dev libgtk-3-dev xterm matchbox-window-manager \
            gdb lua5.4 lua-socket clang-format clangd hunspell-en-us --no-install-recommends -y

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          merge-multiple: true

      - name: Unpack Textadept (Windows)
        if: runner.os == 'Windows'
        shell: bash
        run: unzip textadept_*.win.zip

      - name: Unpack Textadept (macOS)
        if: runner.os == 'macOS'
        run: unzip textadept_*.macOS.zip

      - name: Unpack Textadept (Linux)
        if: runner.os == 'Linux'
        run: tar xzf textadept_*.linux.tgz

      - name: Unpack modules (Windows, Linux)
        if: runner.os != 'macOS'
        run: |
          unzip textadept_*.modules.zip
          mv textadept-modules/* textadept/modules

      - name: Unpack modules (macOS)
        if: runner.os == 'macOS'
        run: |
          unzip textadept_*modules.zip
          mv textadept-modules/* Textadept.app/Contents/Resources/modules

      - name: Run unit tests (Windows)
        if: runner.os == 'Windows'
        shell: cmd
        run: start "" /d ${{ github.workspace }} /wait ${{ matrix.exe }}.exe -t
        timeout-minutes: 2

      - name: Run unit tests (macOS)
        if: runner.os == 'macOS'
        run: ${{ matrix.exe }} -t
        #run: |
        #  echo "cd ${{ github.workspace }}" > run.sh
        #  echo "${{ matrix.exe }} -t" >> run.sh
        #  open -W -a Terminal run.sh # TODO: never exits since Terminal.app does not quit
        timeout-minutes: 2

      - name: Run unit tests (Linux)
        if: runner.os == 'Linux'
        run: xvfb-run -a xterm -e ${{ matrix.exe }} -t
        env:
          LANG: en_US.UTF-8
        timeout-minutes: 2

      - name: Read test output
        if: success() || failure()
        run: |
          cat test.log
          grep -q '^0 failed' test.log

  smoke-test-bsd:
    if: ${{ ! inputs.no_tests }}
    runs-on: ubuntu-latest
    env:
      TEXTADEPT_HOME: ${{ github.workspace }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Checkout textadept-build dependencies
        uses: actions/checkout@v4
        with:
          repository: orbitalquark/textadept-build
          path: textadept-build

      - name: Install dependencies
        uses: cross-platform-actions/action@v0.25.0
        with:
          operating_system: freebsd
          version: '14.1'
          shell: bash
          environment_variables: TEXTADEPT_HOME
          sync_files: false
          shutdown_vm: false
          run: sudo pkg install -y cmake ninja qt5 ncurses

      - name: Build
        uses: cross-platform-actions/action@v0.25.0
        with:
          operating_system: freebsd
          version: '14.1'
          shell: bash
          environment_variables: TEXTADEPT_HOME
          sync_files: runner-to-vm
          shutdown_vm: false
          run: |
            # Move cached dependencies into build/_deps.
            mkdir -p build/_deps && mv textadept-build/* build/_deps && rm -r textadept-build

            # Build.
            cmake -S . -B build -D NIGHTLY=1 -G Ninja
            cmake --build build

            # Copy lexers.
            cp -r build/_deps/scintillua-src/lexers .

      - name: Test
        uses: cross-platform-actions/action@v0.25.0
        with:
          operating_system: freebsd
          version: '14.1'
          shell: bash
          environment_variables: TEXTADEPT_HOME
          sync_files: false
          shutdown_vm: false
          run: build/textadept-curses -t
        timeout-minutes: 1

      - name: Read test output
        if: success() || failure()
        uses: cross-platform-actions/action@v0.25.0
        with:
          operating_system: freebsd
          version: '14.1'
          shell: bash
          environment_variables: TEXTADEPT_HOME
          sync_files: false
          run: |
            cat test.log
            grep -q '^0 failed' test.log

  coverage:
    if: ${{ ! inputs.no_coverage }}
    needs: modules
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Checkout textadept-build dependencies
        uses: actions/checkout@v4
        with:
          repository: orbitalquark/textadept-build
          path: textadept-build

      - name: Checkout LuaCov
        uses: actions/checkout@v4
        with:
          repository: lunarmodules/luacov
          path: luacov

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install qtbase5-dev libgtk-3-dev xterm matchbox-window-manager lua5.4 gcovr \
            gdb lua-socket clang-format clangd hunspell-en-us --no-install-recommends -y

          patch -d luacov -p1 < src/luacov.patch
          sudo mkdir -p /usr/share/lua/5.4
          sudo cp -r luacov/src/luacov* /usr/share/lua/5.4
          sudo cp luacov/src/bin/luacov /usr/bin
          rm -r luacov

      - name: Build for coverage
        run: |
          # Move cached dependencies into build/_deps.
          mkdir -p build/_deps && mv textadept-build/* build/_deps && rm -r textadept-build

          # Build.
          cmake -S . -B build -D NIGHTLY=1 -D PROFILE=1
          cmake --build build --config Debug -j

          # Copy lexers.
          cp -r build/_deps/scintillua-src/lexers .

      - name: Download modules
        uses: actions/download-artifact@v4
        with:
          name: artifacts-modules

      - name: Unpack modules
        run: |
          unzip textadept_*.modules.zip
          mv textadept-modules/* modules

      - name: Run tests
        run: |
          xvfb-run -a build/textadept -t -T || true
          xvfb-run -a build/textadept-gtk -f -t -T || true
          xvfb-run -a xterm -e build/textadept-curses -t -T
        env:
          TEXTADEPT_HOME: ${{ github.workspace }}
          LANG: en_US.UTF-8
        timeout-minutes: 5

      - name: Read test output
        if: success() || failure()
        run: cat test.log

      - name: Process coverage reports
        run: lua scripts/gen_cov.lua github . > coverage.md

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: artifacts-coverage
          path: coverage.md

  update-release-log:
    if: ${{ ! inputs.no_coverage }}
    needs: [release, coverage]
    runs-on: ubuntu-latest
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          merge-multiple: true

      - name: Append coverage table
        run: cat coverage.md >> log.md

      - name: Update release log with coverage info
        if: github.ref_name == github.event.repository.default_branch
        uses: ncipollo/release-action@v1
        with:
          name: nightly
          tag: textadept_nightly
          prerelease: true
          allowUpdates: true
          bodyFile: log.md
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload updated release log
        uses: actions/upload-artifact@v4
        with:
          name: artifacts-update-release-log
          path: log.md
