name: Build
on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      - main
jobs:
  # Run the full Tilt build and wait for it to converge
  tilt:
    # in the future, we may want to run cheap lints, tests, and builds before firing up the expensive tilt test.
    # But for now, we'll kick-off everything at once
    # needs: [go-lint-and-tests, node, algorand, ethereum, terra, rust-lint-and-tests]
    runs-on: tilt-kube-public

    # Cancel previous builds on the same branch/ref. Full runs are expensive
    # and capacity is limited, so we want to avoid running multiple builds
    # in parallel even if it means skipping CI runs on permanent branches
    # (unfortunately, we can't differentiate between temporary and permanent
    # refs without duplicating the entire logic).
    concurrency:
      group: ${{ github.workflow }}-tilt-${{ github.ref }}
      cancel-in-progress: true

    steps:
      - name: Clear repository
        run: |
          rm -rf $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
      - uses: actions/checkout@v2
      - name: Expand for link to Tilt dashboard (only available during build)
        run: >
          echo "Tilt progress dashboard: https://$DASHBOARD_URL"
      - run: |
          kubectl config set-context ci --namespace=$DEPLOY_NS
          kubectl config use-context ci

      - run: tilt ci -- --ci --namespace=$DEPLOY_NS --num=2
        timeout-minutes: 60

      # Clean up k8s resources
      - run: kubectl delete --namespace=$DEPLOY_NS service,statefulset,configmap,pod,job --all
        if: always()

  # Verify whether the Makefile builds the node (no dependencies other than Go)
  node:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: "1.19.3"
      - run: make node

  algorand:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v3
        with:
          python-version: "3.10"
      - run: pip install -r algorand/requirements.txt
      - run: cd algorand && make test

  ethereum:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "16"
      - run: cd ethereum && ../scripts/install-foundry
      - run: cd ethereum && PATH=$PATH:$HOME/.foundry/bin/ make test

  ethereum-upgrade:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "16"
      - run: cd clients/js && make install
      - run: cd ethereum && make test-upgrade

  solana:
    runs-on: ubuntu-20.04
    env:
      RUSTFLAGS: -Dwarnings
      EMITTER_ADDRESS: CiByUvEcx7w2HA4VHcPCBUAFQ73Won9kB36zW9VjirSr
      BRIDGE_ADDRESS: Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o

    steps:
      - uses: actions/checkout@v3
      - name: Get rust toolchain version
        id: toolchain
        run: |
          RUST_VERSION="$(awk '/channel =/ { print substr($3, 2, length($3)-2) }' solana/rust-toolchain)"
          echo "::set-output name=version::${RUST_VERSION}"

      - name: Get solana version
        id: solana
        run: |
          SOLANA_VERSION="$(awk '/solana-program =/ { print substr($3, 3, length($3)-3) }' solana/bridge/program/Cargo.toml)"
          echo "::set-output name=version::${SOLANA_VERSION}"

      - name: Cache rust toolchain
        uses: actions/cache@v3
        env:
          cache-name: solana-toolchain
        with:
          path: |
            ~/.cargo/bin
            ~/.rustup
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ steps.toolchain.outputs.version }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-

      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
        with:
          toolchain: ${{ steps.toolchain.outputs.version }}
          components: "clippy,rustfmt"

      - name: Cache rust packages / build cache
        uses: actions/cache@v3
        env:
          cache-name: solana-rust-packages
        with:
          path: |
            ~/.cargo/bin
            ~/.cargo/registry
            ~/.cargo/git/db
            solana/target
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('solana/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-

      - name: Run `cargo fmt`
        run: cargo fmt --check --all --manifest-path solana/Cargo.toml

      - name: Run `cargo check`
        run: cargo check --workspace --tests --manifest-path solana/Cargo.toml
          --features "nft-bridge/instructions token-bridge/instructions wormhole-bridge-solana/instructions"

      - name: Run `cargo clippy`
        run: cargo clippy --workspace --tests --manifest-path solana/Cargo.toml
          --features "nft-bridge/instructions token-bridge/instructions wormhole-bridge-solana/instructions"

      - name: Cache solana tools
        id: cache-solana
        uses: actions/cache@v3
        env:
          cache-name: solana-tools
        with:
          path: |
            ~/.local/share/solana/install/
            ~/.cache/solana/
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ steps.solana.outputs.version }}

      - if: ${{ steps.cache-solana.outputs.cache-hit != 'true' }}
        name: Install solana tools
        env:
          SOLANA_VERSION: ${{ steps.solana.outputs.version }}
        run: |
          sh -c "$(curl -sSfL https://release.solana.com/v${SOLANA_VERSION}/install)"
          ~/.local/share/solana/install/active_release/bin/sdk/bpf/scripts/install.sh

      - name: Run unit tests
        env:
          RUST_BACKTRACE: "1"
        run: |
          cd solana
          export BPF_OUT_DIR="$(pwd)/target/deploy"
          export PATH="${HOME}/.local/share/solana/install/active_release/bin:${PATH}"

          mkdir -p "${BPF_OUT_DIR}"
          cp modules/token_bridge/token-metadata/spl_token_metadata.so "${BPF_OUT_DIR}"

          BPF_PACKAGES=(
            bridge/program/Cargo.toml
            modules/token_bridge/program/Cargo.toml
            modules/nft_bridge/program/Cargo.toml
          )
          for p in "${BPF_PACKAGES[@]}"; do
            cargo build-bpf --manifest-path "${p}"
          done

          cargo test --workspace --features "nft-bridge/instructions token-bridge/instructions wormhole-bridge-solana/instructions"

  aptos:
    name: Aptos
    runs-on: ubuntu-20.04
    defaults:
      run:
        shell: bash
        working-directory: ./aptos
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Run tests via docker
        run: make test-docker

  sui:
    name: Sui
    runs-on: ubuntu-20.04
    defaults:
      run:
        shell: bash
        working-directory: ./sui
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Run tests via docker
        run: make test-docker


  terra:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "16"
      - run: cd terra && make test
  terra-2:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "16"
      - run: cd cosmwasm/deployment/terra2 && make test
  cosmwasm:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "16"
      - run: cd cosmwasm && make test

  cli:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: "16"
      - run: cd clients/js && make test

  # Verify wormchain tests
  wormchain:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: "1.19.3"
      - name: make proto -B && make test
        run: |
          cd wormchain
          make proto -B
          git diff --name-only --exit-code && echo "✅ Generated proto matches committed proto" || (echo "❌ Generated proto differs from committed proto, run \`make proto -B\` and commit the result" >&2 && exit 1)
          make test
      - name: deploy-test
        run: cd wormchain && make contracts-deploy-test

  # Verify go sdk unit tests
  sdk_vaa:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: "1.19.3"
      - run: cd sdk/vaa && go test

  # Run Go linters, Go tests and other outside-of-Tilt things.
  lint-and-tests:
    # The linter is slow enough that we want to run it on the self-hosted runner
    runs-on: tilt-kube-public
    concurrency:
      group: ${{ github.workflow }}-lint-${{ github.ref }}
      cancel-in-progress: true
    steps:
      - name: Clear repository
        run: |
          rm -rf $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: "1.19.3"
      - name: Install formatter
        run: go install golang.org/x/tools/cmd/goimports@latest
      - name: Formatting checks
        run: ./scripts/lint.sh -l -g format
      - name: Install linters
        run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.46.2
      - name: Run linters
        run: make generate && ./scripts/lint.sh -g lint

      # The go-ethereum and celo-blockchain packages both implement secp256k1 using the exact same header, but that causes duplicate symbols.
      - name: Run golang tests
        run: cd node && go test -v -race -ldflags '-extldflags "-Wl,--allow-multiple-definition" ' ./...
      - name: Ensure generated proto matches
        run: |
          rm -rf node/pkg/proto
          docker build --target go-export -f Dockerfile.proto -o type=local,dest=node .
          git diff --name-only --exit-code && echo "✅ Generated proto matches committed proto" || (echo "❌ Generated proto differs from committed proto, run \`rm -rf node/pkg/proto && docker build --target go-export -f Dockerfile.proto -o type=local,dest=node .\` and commit the result" >&2 && exit 1)

  # Run Rust lints and tests
  rust-lint-and-tests:
    runs-on: ubuntu-20.04
    env:
      RUSTFLAGS: -Dwarnings
    strategy:
      matrix:
        manifest:
          - cosmwasm/Cargo.toml
          - terra/Cargo.toml
          - sdk/rust/Cargo.toml
    steps:
      - name: Check out source
        uses: actions/checkout@v3

      - name: Install rust toolchain
        uses: dtolnay/rust-toolchain@55c7845fad90d0ae8b2e83715cb900e5e861e8cb
        with:
          toolchain: stable
          components: "clippy,rustfmt"

      - name: Create cache key
        id: cachekey
        env:
          MANIFEST: ${{ matrix.manifest }}
        run: |
          LOCKFILE="$(dirname "${MANIFEST}")/Cargo.lock"
          NAME="${MANIFEST%%/*}"
          HASH="$(sha256sum "${LOCKFILE}" | awk '{ print $1 }')"
          echo "::set-output name=name::${NAME}"
          echo "::set-output name=hash::${HASH}"

      - name: Cache rust packages
        uses: actions/cache@v3
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-build-${{ steps.cachekey.outputs.name }}-${{ steps.cachekey.outputs.hash }}
          restore-keys: |
            ${{ runner.os }}-build-${{ matrix.manifest }}-

      - name: Run `rustfmt`
        env:
          MANIFEST: ${{ matrix.manifest }}
        # In its infinite wisdom, `cargo fmt --all` will also format path-based dependencies so
        # instead we have to manually format each ".rs" file.
        run: find "$(dirname "${MANIFEST}")" -name '*.rs' -exec rustfmt --check {} +

      - name: Run `cargo clippy`
        run: cargo clippy --workspace --tests --locked --manifest-path ${{ matrix.manifest }}

      - name: Run unit tests
        run: cargo test --workspace --locked --manifest-path ${{ matrix.manifest }}

  docker:
    runs-on: ubuntu-latest
    steps:
      - name: Check out source
        uses: actions/checkout@v2
      - run: chmod 755 ./scripts/check-docker-pin.sh
      - run: ./scripts/check-docker-pin.sh
  npm-packages:
    runs-on: ubuntu-latest
    steps:
      - name: Check out source
        uses: actions/checkout@v2
      - run: chmod 755 ./scripts/check-npm-package-scopes.sh
      - run: ./scripts/check-npm-package-scopes.sh
