# ----------------------------------------------------------------------------
# GitHub Actions workflow to build and test PasDoc.
#
# Uses GitHub-hosted runners.
#
# See https://docs.github.com/en/actions for docs.
# ----------------------------------------------------------------------------

name: Build
on: [push, pull_request]

env:
  # To which GitHub release tag should we upload artifacts.
  # Can be "snapshot" or "vX.Y.Z" (latter when we make stable release).
  #release_tag: snapshot
  release_tag: v1.0.4

defaults:
  run:
    shell: bash

jobs:
  build:
    name: Build and Test
    strategy:
      matrix:
        operating-system: [
          windows-latest,
          ubuntu-latest,
          macos-15-intel,
          macos-latest
        ]
        include:
        - operating-system: windows-latest
          pasdoc_make_target: win64
        - operating-system: ubuntu-latest
          pasdoc_make_target: linux-x86_64
        - operating-system: macos-15-intel
          pasdoc_make_target: darwin-x86_64
        - operating-system: macos-latest
          pasdoc_make_target: darwin-aarch64
    runs-on: ${{ matrix.operating-system }}
    steps:
      - name: Checkout PasDoc
        uses: actions/checkout@v7

      # We use https://github.com/castle-engine/castle-build-ci/
      # to get FPC + Lazarus on all platforms.
      #
      # Reasons:
      # - Useful on macOS. This works on latest macOS GH-hosted runners.
      #   Alternatives don't work (lazarus is removed from HomeBrew on latest mac,
      #   gcarreno/setup-lazarus does not support latest macos-latest or macos-15-intel).
      # - Does not put ancient cp.exe and other tools from FPC on $PATH for
      #   execution.
      # - It is simpler to use the same on all platforms.
      - name: "Install FPC+Lazarus (using castle-build-ci)"
        run: |
          cd /tmp/ # temporary directory, to be separate from the current project files
          git clone https://github.com/castle-engine/castle-build-ci.git \
            --depth=1 --single-branch --branch=master
          castle-build-ci/install_dependencies
          castle-build-ci/setup_castle_engine --install-fpc=true --install-lazarus=true --install-castle-engine=false

      - name: Install Windows Prerequisites
        if: ${{ runner.os == 'Windows' }}
        run: choco install zip php
      - name: Install macOS Prerequisites
        if: ${{ runner.os == 'macOS' }}
        run: brew install php

      # Without this hack, GitHub Actions Windows runner
      # tries to use WSL bash when our source/component/lang/Makefile calls
      # ./convert_encoding.sh .
      # See https://github.com/orgs/community/discussions/25038 .
      - name: Fix bash on Windows
        if: ${{ runner.os == 'Windows' }}
        run: echo "C:/Program Files/Git/bin" >> $GITHUB_PATH

      - name: Clean And Build All (non-Windows)
        run: make clean-build-all

      - name: Fail If Rebuild Made Any Changes To Versioned Files
        # TODO: Abort Windows checking, seems to have differences due to line endings,
        # although we have eol=auto and files generated should have native line endings.
        if: ${{ runner.os != 'Windows' }}
        run: |
          if [[ -n $(git status --porcelain) ]]; then
            echo "Error: Rebuild made changes to versioned files. Please commit these changes."
            git status
            exit 1
          fi
      - name: Test
        run: make tests-no-build

      # Test HTML validity using
      # https://github.com/marketplace/actions/html5-validator
      # that in turn uses
      # https://github.com/svenkreiss/html5validator
      # https://github.com/validator/validator
      #
      # TODO: Commented out as validation fails for now, with stuff we should indeed fix in PasDoc:
      #
      # "file:/github/workspace/tests/testcases_output/html/ok_warning_note/index.html":22.283-22.286: error: No "p" element in scope but a "p" end tag seen.
      # "file:/github/workspace/tests/testcases_output/html/ok_warning_note/AllUnits.html":22.283-22.286: error: No "p" element in scope but a "p" end tag seen.
      # "file:/github/workspace/tests/testcases_output/html/ok_warning_note/ok_warning_note.html":19.283-19.286: error: No "p" element in scope but a "p" end tag seen.
      # "file:/github/workspace/tests/testcases_output/html/ok_parse_impl/ok_parse_impl.html":117.22-117.43: error: Duplicate ID "Overloaded".
      # "file:/github/workspace/tests/testcases_output/html/ok_parse_impl/ok_parse_impl.TClass.html":70.22-70.39: error: Duplicate ID "Create".
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllFunctions.html":50.22-50.68: error: Bad value "ok_operator_test.html#<" for attribute "href" on element "a": Illegal character in fragment: "<" is not allowed.
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllFunctions.html":50.69-50.70: error: Bad character "<" after "<". Probable cause: Unescaped "<". Try escaping it as "&lt;".
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllFunctions.html":55.22-55.69: error: Bad value "ok_operator_test.html#<=" for attribute "href" on element "a": Illegal character in fragment: "<" is not allowed.
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllFunctions.html":55.70-55.71: error: Bad character "=" after "<". Probable cause: Unescaped "<". Try escaping it as "&lt;".
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllFunctions.html":65.22-65.68: error: Bad value "ok_operator_test.html#>" for attribute "href" on element "a": Illegal character in fragment: ">" is not allowed.
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllFunctions.html":70.22-70.69: error: Bad value "ok_operator_test.html#>=" for attribute "href" on element "a": Illegal character in fragment: ">" is not allowed.
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/ok_operator_test.TDelphiRec.html":83.22-83.41: error: Duplicate ID "Implicit".
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllIdentifiers.html":50.22-50.68: error: Bad value "ok_operator_test.html#<" for attribute "href" on element "a": Illegal character in fragment: "<" is not allowed.
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllIdentifiers.html":50.69-50.70: error: Bad character "<" after "<". Probable cause: Unescaped "<". Try escaping it as "&lt;".
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllIdentifiers.html":55.22-55.69: error: Bad value "ok_operator_test.html#<=" for attribute "href" on element "a": Illegal character in fragment: "<" is not allowed.
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllIdentifiers.html":55.70-55.71: error: Bad character "=" after "<". Probable cause: Unescaped "<". Try escaping it as "&lt;".
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllIdentifiers.html":65.22-65.68: error: Bad value "ok_operator_test.html#>" for attribute "href" on element "a": Illegal character in fragment: ">" is not allowed.
      # "file:/github/workspace/tests/testcases_output/html/ok_operator_test/AllIdentifiers.html":70.22-70.69: error: Bad value "ok_operator_test.html#>=" for attribute "href" on element "a": Illegal character in fragment: ">" is not allowed.
      #
      # - name: HTML5Validator
      #   uses: Cyb3r-Jak3/html5validator-action@master
      #   with:
      #     root: tests/testcases_output/html/

      - name: "Package"
        run: make dist-${{ matrix.pasdoc_make_target }}

      - name: Archive Artifacts
        uses: actions/upload-artifact@v7
        with:
          name: build ${{ matrix.operating-system }} lazarus
          path: |
            *.zip
            *.tar.gz
          if-no-files-found: error

  test-html-help-workshop:
    name: Test htmlhelp output (HTML Help Workshop)
    runs-on: windows-latest
    steps:
      - name: Checkout PasDoc
        uses: actions/checkout@v7

      - name: "Install FPC+Lazarus (using castle-build-ci)"
        run: |
          cd /tmp/ # temporary directory, to be separate from the current project files
          git clone https://github.com/castle-engine/castle-build-ci.git \
            --depth=1 --single-branch --branch=master
          castle-build-ci/install_dependencies
          castle-build-ci/setup_castle_engine --install-fpc=true --install-lazarus=true --install-castle-engine=false

      - name: Build PasDoc
        run: make

      - name: Test HTML Help Generation (Prepare)
        run: |
          choco install html-help-workshop graphviz

          # put hhc on $PATH
          echo "c:/Program Files (x86)/HTML Help Workshop/" >> $GITHUB_PATH

          # put pasdoc on $PATH
          # Note that GITHUB_WORKSPACE is native path on Windows,
          # it's OK to pass it to GITHUB_PATH even when we use bash from Msys
          # (env variable will be converted to Unix-style for bash).
          echo "${GITHUB_WORKSPACE}/bin/" >> $GITHUB_PATH

      - name: Test HTML Help Generation (Generate CHM)
        run: |
          cd source/autodoc/
          make clean htmlhelp

  test-html-help-chmcmd:
    name: Test htmlhelp output (chmcmd)
    runs-on: ubuntu-latest
    steps:
      - name: Checkout PasDoc
        uses: actions/checkout@v7

      - name: "Install FPC+Lazarus (using castle-build-ci)"
        run: |
          cd /tmp/ # temporary directory, to be separate from the current project files
          git clone https://github.com/castle-engine/castle-build-ci.git \
            --depth=1 --single-branch --branch=master
          castle-build-ci/install_dependencies
          castle-build-ci/setup_castle_engine --install-fpc=true --install-lazarus=true --install-castle-engine=false

      - name: Install Graphviz (for autodoc)
        run: sudo apt-get install graphviz

      - name: Build PasDoc and put it on $PATH
        run: |
          make
          echo "${GITHUB_WORKSPACE}/bin/" >> $GITHUB_PATH

      - name: Test HTML Help Generation (Generate CHM)
        run: |
          cd source/autodoc/
          make clean htmlhelp-chmcmd

  # Build for Raspberry Pi using https://github.com/marketplace/actions/arm-runner .
  # See https://github.com/michaliskambi/test-arm-runner-for-cge/ .
  build-rpi:
    name: Build for Raspberry Pi (Linux/Arm)
    runs-on: ubuntu-latest
    strategy:
      matrix:
        arch: [armv7l, aarch64]
        include:
        - arch: armv7l
          cpu: cortex-a7
          base_image: raspios_lite:latest
        - arch: aarch64
          cpu: cortex-a53
          base_image: raspios_lite_arm64:latest
    steps:
      - uses: actions/checkout@v7
      - uses: pguyot/arm-runner-action@v2
        with:
          base_image: ${{ matrix.base_image }}
          cpu: ${{ matrix.cpu }}
          shell: /bin/bash -eo pipefail
          image_additional_mb: 4096
          # Avoids the need for copy_artifact_path later.
          bind_mount_repository: true
          commands: |
            # Show system info
            uname -a

            # Install FPC and other Linux dependencies
            sudo apt-get update
            sudo apt-get --no-install-recommends -y install libgl-dev fpc lazarus-ide lcl-utils lcl-gtk2 lcl lcl-units git make sed zip php-cli

            # Build and test
            make
            make build-tools
            make tests-no-build

            # Make release
            FPC_CPU=`fpc -iTP`
            make dist-linux-"${FPC_CPU}"

      - name: Archive Artifacts
        uses: actions/upload-artifact@v7
        with:
          name: build raspberry-pi ${{ matrix.arch }}
          path: |
            *.zip
            *.tar.gz
          if-no-files-found: error

  delphi-test:
    name: Test Building and Running with Delphi
    strategy:
      matrix:
        delphi_runner_version: [delphi_10, delphi_12]
        include:
        - delphi_runner_version: delphi_10
          delphi_directory: "C:/Program Files (x86)/Embarcadero/Studio/19.0/"
        - delphi_runner_version: delphi_12
          delphi_directory: "C:/Program Files (x86)/Embarcadero/Studio/23.0/"
    runs-on: ${{ matrix.delphi_runner_version }}
    steps:
      - name: Checkout PasDoc
        uses: actions/checkout@v7
      - name: Build Using Delphi
        shell: cmd
        # Note: It seems that only the last command in the "run" section
        # is considered for error code with cmd shell. So keep it always msbuild.
        run: |
          call "${{ matrix.delphi_directory }}/bin/rsvars.bat"
          msbuild source/console/pasdoc.dproj
      # Note: This runs on self-hosted runners with limited permissions,
      # "choco install ..." will just fail if package is not already installed.
      # Still it's useful to run -- to make sure it is installed.
      - name: Install Windows Prerequisites
        run: choco install php xsltproc
      - name: Test Using Delphi
        run: make tests-no-build

  release:
    name: Release
    runs-on: ubuntu-latest
    # Only upload release if all builds and tests succeeded.
    needs: [build,
      build-rpi,
      delphi-test,
      test-html-help-workshop,
      test-html-help-chmcmd
    ]
    steps:
    - name: Download packaged releases
      uses: actions/download-artifact@v8
      with:
        merge-multiple: true
    - name: List downloaded files
      run: ls -R
    - name: GH CLI status
      env:
        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: gh auth status
    # Releases files in the GitHub release.
    - name: Release Artifacts
      if: ${{ github.ref == 'refs/heads/master' }}
      run: gh release --repo ${{ github.repository }} upload ${{ env.release_tag }} --clobber *.zip *.tar.gz
      env:
        GH_TOKEN: ${{ github.token }}

  update-release-tag:
    name: Update Release Tag (make release tag point to the build commit on master branch)
    runs-on: ubuntu-latest
    needs: [release]
    steps:
    - uses: actions/checkout@v7
    - name: Update Release Tag
      if: ${{ github.ref == 'refs/heads/master' }}
      run: |
        # --force allows to overwrite previous tag
        git tag --force ${{ env.release_tag }}
        # --force allows to push with overwritten tag
        git push --force origin ${{ env.release_tag }}
