version: 2.1

orbs:
  codecov: codecov/codecov@1.1.1

commands:
  create-typeorm-config:
    parameters:
      databases:
        type: string
        default: ""
    steps:
      - when:
          condition:
            equal: [<< parameters.databases >>, ""]
          steps:
            - run:
                name: "Enabling Databases in ORM config"
                command: cp ormconfig.circleci-common.json ./ormconfig.json
      - unless:
          condition:
            equal: [<< parameters.databases >>, ""]
          steps:
            - run:
                name: "Enabling Databases in ORM config"
                command: >
                  cat ormconfig.circleci-common.json \
                    | jq 'map(.skip = if (.name | IN($ARGS.positional[])) then false else true end)' --args << parameters.databases >> \
                    > ormconfig.json
      - run:
          name: Check ORMConfig
          command: cat ormconfig.json

  install-packages:
    parameters:
      cache-key:
        type: string
        default: ""
    steps:
      - restore_cache:
          name: Restore node_modules cache
          key: node_modules-<< parameters.cache-key >>-{{ checksum "package-lock.json" }}
      #      removed this item because lock-verify is deprecated
      #      - run:
      #          name: Verify `package.json` and `package-lock.json` are in sync
      #          command: npx lock-verify
      - run:
          # This uses `npm install` instead of `npm ci`
          # because of https://github.com/npm/cli/issues/558
          name: Install Node Packages
          command: |
            if [ ! -d node_modules ]; then
              npm install
              npm install oracledb
            fi
      - run:
          # This is pretty terrible but OracleDB requires you to grab the binaries OOB
          # from the normal installation, place them in the LD Path
          # also - not super well documented - grab `libaio` as well
          # Because this is technically the same image as the runner we'll snag
          # the libaio1 and place them in the same instantclient directory.
          name: Download Required OracleDB Binaries
          command: |
            if [ ! -d node_modules/oracledb/instantclient_19_8 ]; then
              curl -sf -o node_modules/oracledb/instantclient.zip $BLOB_URL
              unzip -qqo node_modules/oracledb/instantclient.zip -d node_modules/oracledb/
              rm node_modules/oracledb/instantclient.zip

              sudo apt-get update -qq && sudo apt-get -qq -y install libaio1
              (cp /lib/*/libaio.so.* node_modules/oracledb/instantclient_19_8/ ||
                cp /usr/lib/*/libaio.so.* node_modules/oracledb/instantclient_19_8/)
            fi
          environment:
            BLOB_URL: https://download.oracle.com/otn_software/linux/instantclient/19800/instantclient-basiclite-linux.x64-19.8.0.0.0dbru.zip
            DEBIAN_FRONTEND: noninteractive
      - save_cache:
          name: Save node_modules cache
          key: node_modules-{{ checksum "package-lock.json" }}
          paths:
            - node_modules

jobs:
  lint:
    working_directory: ~/typeorm
    docker:
      - image: cimg/node:16.20.0
    steps:
      - checkout
      - install-packages:
          cache-key: node16
      - run: npm run lint

  build:
    working_directory: ~/typeorm
    docker:
      - image: cimg/node:16.20.0
    steps:
      - checkout
      - install-packages:
          cache-key: node16
      - run: npm run compile
      - persist_to_workspace:
          root: ~/typeorm
          paths:
            - build/

  test:
    parameters:
      databases:
        type: string
        default: ""
      node-version:
        type: string
        default: "16"
    working_directory: ~/typeorm
    docker:
      - image: cimg/node:<< parameters.node-version >>
    steps:
      - checkout
      - setup_remote_docker
      - attach_workspace:
          at: ~/typeorm
      - create-typeorm-config:
          databases: << parameters.databases >>
      - run:
          name: Start all Relevant Services
          command: |
            SERVICES=$(
              npx js-yaml ./docker-compose.yml \
                | jq -r '.services | keys | map(select(. | IN($ARGS.positional[]))) | join(" ")' --args << parameters.databases >>
            )

            docker-compose --project-name typeorm --no-ansi up --detach $SERVICES
      - install-packages:
          cache-key: node<< parameters.node-version >>
      - run:
          name: Set up TypeORM Test Runner
          command: |
            docker run \
              --volume /typeorm \
              --name typeorm-code \
              --workdir /typeorm \
              cimg/node:<< parameters.node-version >> \
              /bin/bash -c "sudo chmod 777 /typeorm && sudo chown circleci /typeorm"
            docker cp ./ typeorm-code:/typeorm
      - run:
          name: Wait for Services to be Available
          command: |
            COMMANDS=$(
                cat ormconfig.json \
                  | jq -r '
                    map(select(.skip == false)
                      | select(.host)
                      | select(.port)
                      | "nc -z " + .host + " " + (.port|tostring) + " && echo " + .host + " " + (.port|tostring) + " is up"
                    )
                    | join(" && ")
                  '
            )
            echo "Running '$COMMANDS'"

            docker run \
              --network typeorm_default \
              --tty \
              ubuntu:trusty \
              timeout 60 sh -c "until ($COMMANDS); do echo \"Waiting for Services to be Available ...\"; sleep 5; done"
      - run:
          name: "Wait for OracleDB to be Available"
          command: |
            COMMANDS=$(
                cat ormconfig.json \
                  | jq -r '
                    map(select(.skip == false)
                      | select(.name == "oracle")
                      | "sleep 60"
                    )
                    | join(" && ")
                  '
            )
            if [ ! -z "$COMMANDS" ]; then
              echo "$COMMANDS seconds to wait for oracledb";
              $COMMANDS
            fi

      # Download and cache dependencies
      - run:
          name: "Run Tests with Coverage"

          command: |
            docker run \
              --env npm_config_yes='true' \
              --env LD_LIBRARY_PATH='/typeorm/node_modules/oracledb/instantclient_19_8/:$LD_LIBRARY_PATH' \
              --volumes-from typeorm-code \
              --network typeorm_default \
              --tty \
              --workdir /typeorm \
              --name typeorm-testrunner \
              cimg/node:<< parameters.node-version >> \
              npx nyc npm run test-fast

            docker cp typeorm-testrunner:/typeorm/coverage/ ./
      - run:
          name: Stop all Relevant Services
          command: docker-compose down
      - store_artifacts:
          path: coverage
      - codecov/upload

workflows:
  version: 2
  test:
    jobs:
      - lint
      - build
      - test:
          name: test (mysql mariadb postgres mssql mongodb sqlite better-sqlite3 sqljs) - Node v<< matrix.node-version >>
          requires:
            - lint
            - build
          databases: "mysql mariadb postgres mssql mongodb sqlite better-sqlite3 sqljs"
          matrix:
            parameters:
              node-version:
                - "14.21.3"
                - "16.20.0"
      - test:
          name: test (cockroachdb) - Node v16
          requires:
            - lint
            - build
          databases: "cockroachdb"
          node-version: "16.20.0"
      - test:
          name: test (oracle) - Node v16
          requires:
            - lint
            - build
          databases: "oracle"
          node-version: "16.20.0"
      - test:
          name: test (postgres 12) - Node v16
          requires:
            - lint
            - build
          databases: "postgres-12"
          node-version: "16.20.0"
