name: Anchor

on:
  schedule:
    - cron: "0 0 * * *"
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
        pull-requests: read
    strategy:
      matrix:
        node-version: [20.x]
        solana-version: [1.18.8, beta]
        anchor-version: [0.30.0]
    steps:
      - uses: actions/checkout@v4
      - name: Setup Anchor
        uses: heyAyushh/setup-anchor@v3.10
        with:
          anchor-version: ${{ matrix.anchor-version }}
          solana-cli-version: ${{ matrix.solana-version }}
          node-version: ${{ matrix.node-version }}
      - name: Display versions
        run: |
          solana -V
          solana-keygen new --no-bip39-passphrase
          rustc -V
          anchor -V
          npm i -g pnpm
      # Run only if it's triggered by PR to main,
      # Build the changed programs
      - uses: dorny/paths-filter@v3
        if: github.event_name == 'pull_request'
        id: changes
        with:
          list-files: shell
          filters: |
            anchor:
              - added|modified: '**/anchor/**'
      - name: Build Changed Anchor programs
        if: ${{ steps.changes.outputs.anchor == 'true' }}
        run: |
            changed_files=(${{ steps.changes.outputs.anchor_files }})

            # Read ignored projects from .github/.ghaignore, ignoring empty lines and comments
            ignored_projects=($(grep . .github/.ghaignore | grep -v '^$'))

            # Find anchor projects and remove ignored projects
            ProjectDirs=($(for file in "${changed_files[@]}"; do
              anchor_path=$(dirname "${file}" | grep anchor | sed 's#/anchor/.*#/anchor#g')
              if [[ ! " ${ignored_projects[*]} " =~ " ${anchor_path} " ]]; then
                echo "$anchor_path"
              fi
            done | sort -u))

            # Build anchor projects
            echo "Projects to Build:"
            printf "%s\n" "${ProjectDirs[@]}"
            for projectDir in "${ProjectDirs[@]}"; do
              echo "
              ********
              Building $projectDir
              ********"
              cd $projectDir
              if anchor build; then
                echo "Build succeeded for $projectDir."
                rm -rf target
              else
                failed=true
                failed_builds+=($projectDir)
                echo "Build failed for $projectDir. Continuing with the next program."
              fi
              cd - > /dev/null
            done
            if [ "$failed" = true ]; then
              echo "Programs that failed building:"
              printf "%s\n" "${failed_builds[@]}"
              exit 1
            else
              echo "All programs built successfully."
            fi
        shell: bash
      # Skip Building all Programs if it's a PR to main branch
      - name: Build All Anchor programs
        if: github.event_name != 'pull_request'
        run: |
          # Find all anchor projects and remove ignored projects
          declare -a ProjectDirs=($(find . -type d -name 'anchor' | sed 's|^\./||'| grep -v -f <(grep . .github/.ghaignore | grep -v '^$')))

          echo "Projects to Build:"
          printf "%s\n" "${ProjectDirs[@]}"

          # Build anchor projects
          for projectDir in "${ProjectDirs[@]}"; do
            echo "
            ********
            Building $projectDir
            ********"
            cd $projectDir
            if anchor build; then
              echo "Build succeeded for $projectDir."
              rm -rf target
            else
              failed=true
              failed_builds+=($projectDir)
              echo "Build failed for $projectDir. Continuing with the next program."
            fi
          cd - > /dev/null
          done
          if [ "$failed" = true ]; then
            echo "Programs that failed building:"
            printf "%s\n" "${failed_builds[@]}"
            exit 1
          else
            echo "All programs built successfully."
          fi
        shell: bash

  test:
    runs-on: ubuntu-latest
    permissions:
          pull-requests: read
    strategy:
      matrix:
        node-version: [20.x]
        solana-version: [1.18.8, beta]
        anchor-version: [0.30.0]
    steps:
      - uses: actions/checkout@v4
      - name: Setup Anchor
        uses: heyAyushh/setup-anchor@v3.10
        with:
          anchor-version: ${{ matrix.anchor-version }}
          solana-cli-version: ${{ matrix.solana-version }}
          node-version: ${{ matrix.node-version }}
      - name: Display versions
        run: |
          solana -V
          solana-keygen new --no-bip39-passphrase
          rustc -V
          anchor -V
          npm i -g pnpm
      # Run only if it's triggered by PR to main,
      # Test the changed programs (if any)
      - uses: dorny/paths-filter@v3
        if: github.event_name == 'pull_request'
        id: changes
        with:
          list-files: shell
          filters: |
            anchor:
              - added|modified: '**/anchor/**'
      - name: Test Changed Anchor Programs
        if: ${{ steps.changes.outputs.anchor == 'true' }}
        run: |
          changed_files=(${{ steps.changes.outputs.anchor_files }})
          ProjectDirs=($(for file in "${changed_files[@]}"; do dirname "${file}" | grep anchor | sed 's#/anchor/.*#/anchor#g'; done | grep -v -f <(grep . .github/.ghaignore | grep -v '^$') | sort -u))
          echo "Projects to Test:"
          printf "%s\n" "${ProjectDirs[@]}"
          for projectDir in "${ProjectDirs[@]}"; do
            echo "
            ********
            Testing $projectDir
            ********"
            cd $projectDir
            pnpm install --frozen-lockfile
            if anchor test; then
              echo "Tests succeeded for $projectDir."
              rm -rf target node_modules
            else
              failed=true
              failed_tests+=($projectDir)
              echo "Tests failed for $val. Continuing with the next program."
            fi
          cd - > /dev/null
          done
          if [ "$failed" = true ]; then
            echo "*****************************"
            echo "Programs that failed testing:"
            printf "%s\n" "${failed_tests[@]}"
            exit 1
          else
            echo "All tests passed."
          fi
        shell: bash
      # Skip Testing all Programs if it's a PR to main branch
      - name: Test All Anchor Programs
        if: github.event_name != 'pull_request'
        run: |
          declare -a ProjectDirs=($(find . -type d -name "anchor"| grep -v -f <(grep . .github/.ghaignore | grep -v '^$')))
          echo "Projects to Test:"
          printf "%s\n" "${ProjectDirs[@]}"
          for projectDir in "${ProjectDirs[@]}"; do
            echo "
            ********
            Testing $projectDir
            ********"
            cd $projectDir
            pnpm install --frozen-lockfile
            if anchor test; then
              echo "Tests succeeded for $projectDir."
              rm -rf target node_modules
            else
              failed=true
              failed_tests+=($projectDir)
              echo "Tests failed for $val. Continuing with the next program."
            fi
          cd - > /dev/null
          done
          if [ "$failed" = true ]; then
            echo "*****************************"
            echo "Programs that failed testing:"
            printf "%s\n" "${failed_tests[@]}"
            exit 1
          else
            echo "All tests passed."
          fi
        shell: bash
