version: 2.1

orbs:
  win: circleci/windows@5.0.0
  node: circleci/node@5.1.0
  slack: circleci/slack@5
  prodsec: snyk/prodsec-orb@1

defaults: &defaults
  resource_class: small
  docker:
    - image: cimg/node:20.19
  working_directory: ~/snyk-docker-plugin

windows_defaults: &windows_defaults
  executor:
    name: win/default
    shell: bash.exe
  parameters:
    node_version:
      type: string
      default: ""
  working_directory: ~/snyk-docker-plugin

slack-fail-notify: &slack-fail-notify
  slack/notify:
    event: fail
    channel: team-container-pipeline-info
    branch_pattern: "main"
    template: basic_fail_1

slack-success-notify: &slack-success-notify
  slack/notify:
    event: pass
    channel: team-container-pipeline-info
    branch_pattern: "main"
    template: basic_success_1

windows_big: &windows_big
  executor:
    name: win/server-2022
    shell: bash.exe
    size: large
    # we've pinned the version because without it, it uses "current" (at the time of writing, "2023.06.1"),
    # which has a broken Docker installation. See https://discuss.circleci.com/t/build-failures-when-running-docker-on-junes-windows-executor/48605
    # TODO: check if it works again with the next release and unpin the version.
    version: "2023.05.1"
  parameters:
    node_version:
      type: string
      default: ""
  working_directory: ~/snyk-docker-plugin

release_defaults: &release_defaults
  resource_class: small
  docker:
    - image: cimg/node:20.19
  working_directory: ~/snyk-docker-plugin

define: &windows_node_version "20.19.1"

commands:
  setup_npm_user:
    steps:
       - run: 
           command: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
  install_deps:
    description: Install dependencies
    steps:
      - checkout
      - restore_cache:
          keys:
            - v2-npm-cache-{{ checksum "package.json" }}
            - v2-npm-cache-
      - setup_npm_user
      - run: npm ci
      - save_cache:
          key: v2-npm-cache-{{ checksum "package.json" }}
          paths:
            - ~/.npm
      - persist_to_workspace:
          root: .
          paths:
            - node_modules/
  checkout_and_merge:
    steps:
      - checkout
      - run:
          name: Checkout main
          command: git checkout origin/main
      - run:
          name: Merge test branch
          command: |
            git config user.name "CircleCI"
            git config user.email "noop"
            git merge --no-edit "$CIRCLE_BRANCH"
      - attach_workspace:
          at: ~/snyk-docker-plugin
  install_node_npm:
    description: Install specific Node version
    parameters:
      node_version:
        type: string
        default: ""
    steps:
      - node/install:
          node-version: << parameters.node_version >>
      - run:
          name: Use currently installed node version
          command: nvm list | awk '/<< parameters.node_version >>/ {print $1}' | xargs nvm use

jobs:
  security-scans:
    <<: *defaults
    steps:
      - checkout
      - install_deps
      - attach_workspace:
          at: ~/snyk-docker-plugin
      - prodsec/security_scans:
          mode: auto
          open-source-additional-arguments: --exclude=test
          iac-scan: disabled
  install:
    <<: *defaults
    steps:
      - install_deps
  lint:
    <<: *defaults
    steps:
      - checkout
      - attach_workspace:
          at: ~/snyk-docker-plugin
      - run: npm run lint
  test_unit:
    <<: *defaults
    resource_class: medium
    steps:
      - checkout
      - attach_workspace:
          at: ~/snyk-docker-plugin
      - run: npm run test:unit > test-unit-logs.txt 2>&1
      - store_artifacts:
          path: test-unit-logs.txt
          destination: test-unit-logs
  test_system:
    <<: *defaults
    steps:
      - checkout
      - setup_remote_docker
      - attach_workspace:
          at: ~/snyk-docker-plugin
      - run:
          command: npm run test:system > test-system-logs.txt 2>&1
          no_output_timeout: 20m
      - store_artifacts:
          path: test-system-logs.txt
          destination: test-system-logs
  test_jest_windows_with_docker:
    <<: *windows_big
    steps:
      - checkout
      - install_node_npm:
          node_version: << parameters.node_version >>
      - setup_npm_user
      - run: npm ci
      - run: docker version
      - run:
          command: npm run test-jest-windows
          no_output_timeout: 20m
  test_jest_windows_no_docker:
    <<: *windows_big
    steps:
      - checkout
      - install_node_npm:
          node_version: << parameters.node_version >>
      - setup_npm_user
      - run: npm ci
        # make docker appear to be broken.
      - run: "function docker() { return 1; }"
      - run:
          command: npm run test-jest-windows
          no_output_timeout: 20m
  build:
    <<: *defaults
    steps:
      - checkout_and_merge
      - setup_npm_user
      - run: npm ci
      - run: npm run build
  build_cli:
    <<: *defaults
    resource_class: medium
    steps:
      - checkout_and_merge
      - run:
          name: Setup NPM credentials
          command: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> ../.npmrc
      - run:
          name: Build Snyk CLI with latest changes
          command: ./.circleci/build-cli.sh
  build_and_test_latest_go_binary:
    <<: *defaults
    resource_class: medium
    steps:
      - setup_remote_docker
      - checkout_and_merge
      - run:
          name: Build a Go binary with latest Go version
          command: ./.circleci/build-go-binary-latest.sh
      - run:
          name: Run Go binaries unit test
          command: npx jest test/unit/go-binaries.spec.ts
  release:
    <<: *release_defaults
    steps:
      - checkout
      - setup_npm_user
      - run: npm ci
      - run: npm run build
      - run:
          name: Release on GitHub
          command: npx semantic-release

workflows:
  version: 2
  test_and_release:
    when:
      # do not run on a pipeline schedule
      not:
        equal: [scheduled_pipeline, << pipeline.trigger_source >>]
    jobs:
      - prodsec/secrets-scan:
          name: Scan repository for secrets
          context:
            - snyk-bot-slack
          channel: snyk-vuln-alerts-container
          filters:
            branches:
              ignore: main
      - install:
          name: Install
          context:
            - nodejs-install
      - lint:
          name: Lint
          context:
            - nodejs-install
            - snyk-bot-slack
          requires:
            - Install
          post-steps:
            - *slack-fail-notify
      - build:
          name: Build
          context:
            - nodejs-install
            - snyk-bot-slack
          requires:
            - Lint
          post-steps:
            - *slack-fail-notify
      - security-scans:
          name: Security Scans
          context: infrasec_container
          post-steps:
            - *slack-fail-notify
      - test_unit:
          name: Unit Test
          context:
            - nodejs-install
            - snyk-bot-slack
          requires:
            - Build
          post-steps:
            - *slack-fail-notify
      - test_system:
          name: System Test
          context:
            - nodejs-install
            - snyk-bot-slack
          requires:
            - Build
          post-steps:
            - *slack-fail-notify
      - test_jest_windows_with_docker:
          name: Test Jest Windows with Docker
          context:
            - nodejs-install
            - snyk-bot-slack
          node_version: *windows_node_version
          requires:
            - Build
          post-steps:
            - *slack-fail-notify
      - test_jest_windows_no_docker:
          name: Test Jest Windows no Docker
          context:
            - nodejs-install
            - snyk-bot-slack
          node_version: *windows_node_version
          requires:
            - Build
          post-steps:
            - *slack-fail-notify
      - build_cli:
          name: Build CLI with changes
          context:
            - nodejs-install
            - snyk-bot-slack
          requires:
            - Build
          post-steps:
            - *slack-fail-notify
      - release:
          name: Release to GitHub
          context:
            - nodejs-lib-release
            - snyk-bot-slack
          filters:
            branches:
              only:
                - main
          requires:
            - Lint
            - Build
            - Security Scans
            - Unit Test
            - System Test
            - Test Jest Windows with Docker
            - Test Jest Windows no Docker
          post-steps:
            - *slack-fail-notify
            - *slack-success-notify
  go_regression_test:
    when:
      and:
        - equal: [scheduled_pipeline, << pipeline.trigger_source >>]
        - equal: ["Build and test Go binaries", << pipeline.schedule.name >>]
    jobs:
      - install:
          name: Install
          context:
            - nodejs-install
          post-steps:
            - *slack-fail-notify
      - build_and_test_latest_go_binary:
          name: Build Go binary
          context:
            - nodejs-install
          requires:
            - Install
          post-steps:
            - *slack-fail-notify
