name: Verify package library

on:
  pull_request:
    # by default, this will run on [opened, synchronize, reopened] events
    # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
    # only consider PRs with changes to rust files
    paths:
      - '**.rs'

permissions:
  id-token: write

env:
  NODE_VERSION: 17.0.1
  ANCHOR_VERSION: 0.22.0
  SOLANA_VERSION_STABLE: 1.9.22
  RUST_TOOLCHAIN: stable

jobs:
  dump-context:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Dump GitHub context
        env:
          GITHUB_CONTEXT: ${{ toJson(github) }}
        run: echo "$GITHUB_CONTEXT"

  get-changes-scope:
    # only listen for PRs opened against master
    if: contains(github.base_ref, 'master')
    runs-on: ubuntu-latest
    outputs:
      packages: ${{ steps.filter-out-excluded-pkgs.outputs.result }}
    steps:
      - uses: actions/checkout@v3
      - name: List changed files
        uses: ./.github/actions/list-changed-files
        id: list-changed-files
        with:
          pull-number: ${{ github.event.pull_request.number }}

      # map fetched changed files to package / lang (list)
      - name: Get scope of changed packages
        uses: actions/github-script@v4
        id: map-changed-files-to-pkg
        with:
          script: |
            const files = ${{ steps.list-changed-files.outputs.changed-files }}
            const isRustFile = /.*\.rs$/g;
            const uniqueFilesObj = files
              .filter(f => isRustFile.test(f))
              .reduce((p, file) => {
                const pkgForFile = file.split("/")[0];
                if (!p[pkgForFile]) p[pkgForFile] = 1
                return p
              }, {})
            return Array.from(Object.keys(uniqueFilesObj))

      - name: Exclude packages
        uses: actions/github-script@v4
        id: filter-out-excluded-pkgs
        with:
          # exclude packages either do not have `js` dirs or do not have `api:gen` scripts in their local package.json
          script: |
            const exclude = ['metaplex', 'auction', 'core', 'nft-packs']
            const files = ${{ steps.map-changed-files-to-pkg.outputs.result }}
            const result = files
              .filter(f => !exclude.includes(f))
              .join(" ")
            return result.length > 0 ? result : null

      - name: Print packages to verify
        run: echo "${{ steps.filter-out-excluded-pkgs.outputs.result }}"
        shell: bash

  verify-package-library:
    needs: [get-changes-scope]
    # note: checking for empty string just doesn't work, so we explicitly return and check null in the case that there's nothing to verify
    if: ${{ needs.get-changes-scope.outputs.packages != 'null' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: ./.github/actions/install-linux-build-deps
      - uses: ./.github/actions/install-rust
        with:
          toolchain: ${{ env.RUST_TOOLCHAIN }}
      - uses: ./.github/actions/install-solana
        with:
          solana_version: ${{ env.SOLANA_VERSION_STABLE }}

      - name: Generate library for modified packages
        id: generate-package-lib
        # YARN_ENABLE_IMMUTABLE_INSTALLS=false is needed otherwise, we get this error:
        # `The lockfile would have been modified by this install, which is explicitly forbidden.`
        # Additionally, we aren't committing changes here.
        run: |
          pkgs=${{ needs.get-changes-scope.outputs.packages }}
          pkgs=(${pkgs//\"})
          for pkg in "${pkgs[@]}"; do
            echo ">> changing dir $pkg/js"
            cd "$pkg/js"
            echo ">> setup local pcakage dependencies with yarn"
            YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install
            echo ">> generate library"
            yarn api:gen
            echo ">> git status"
            # ignore any possible yarn.lock changes
            git restore ../../yarn.lock
            git status
            if [[ $(git diff --stat) != '' ]]; then
              echo ">> $pkg needs changes - breaking early"
              echo "::set-output name=failed::true"
              break
            else
              echo "::set-output name=failed::false"
            fi
            echo ">> regress 2 dirs"
            cd ../..
          done

      - uses: actions/github-script@v4
        if: steps.generate-package-lib.outputs.failed == 'true'
        with:
          script: |
            core.setFailed("Found differences when generating libraries. See logs for offending package(s).")
