Coursemology/coursemology2

View on GitHub
.circleci/config.yml

Summary

Maintainability
Test Coverage
version: 2.1

orbs:
  codecov: codecov/codecov@3.2.5

executors:
  node:
    docker:
      - image: cimg/node:18.17.0

    working_directory: ~/repo
    resource_class: large

  ruby_with_postgres:
    parameters:
      collects_rails_coverage:
        type: boolean
        default: false

    docker:
      - image: cimg/ruby:3.0.6-browsers
        environment:
          PG_HOST: localhost
          PG_USER: ubuntu
          RAILS_ENV: test
          BUNDLE_APP_CONFIG: ~/repo/.bundle
          DATABASE_URL: "postgres://ubuntu@localhost:5432/coursemology_test"
          COLLECT_COVERAGE: << parameters.collects_rails_coverage >>

      - image: cimg/postgres:14.5
        environment:
          POSTGRES_USER: ubuntu
          POSTGRES_DB: coursemology_test

      - image: cimg/redis:7.2.3

    working_directory: ~/repo
    resource_class: large

commands:
  checkout_with_submodules:
    steps:
      - checkout

      - run:
          name: Checkout submodules
          command: git submodule update --init --recursive

  rehydrate_ruby_deps:
    steps:
      - restore_cache:
          name: Restore Ruby dependencies cache
          keys:
            - v3.0.6-ruby-{{ checksum "Gemfile.lock" }}
            - v3.0.6-ruby-

      - run:
          name: Install Bundler
          command: gem install bundler:2.2.33

      - run:
          name: Install Ruby dependencies
          command: bundle install --jobs=4 --retry=3 --path vendor/bundle --without development:production --deployment

      - save_cache:
          paths:
            - ./vendor/bundle
            - ./.bundle
          key: v3.0.6-ruby-{{ checksum "Gemfile.lock" }}

  rehydrate_node_deps:
    steps:
      - restore_cache:
          name: Restore client Yarn dependencies cache
          keys:
            - v18.17.0-node-{{ checksum "client/yarn.lock" }}-{{ checksum "client/vendor/recorderjs/package.json" }}
            - v18.17.0-node-

      - run:
          name: Install client Yarn dependencies
          working_directory: client
          command: yarn install

      - save_cache:
          paths:
            - ./client/node_modules
            - ./client/vendor/recorderjs/node_modules
          key: v18.17.0-node-{{ checksum "client/yarn.lock" }}-{{ checksum "client/vendor/recorderjs/package.json" }}

  restore_client_cache:
    steps:
      - restore_cache:
          name: Restore client cache
          keys:
            - v1-yarn-build-{{ .Revision }}

  build_and_cache_client:
    steps:
      - restore_client_cache

      - run:
          name: Add env file to client folder
          working_directory: client
          command: |
            touch .env
            echo GOOGLE_RECAPTCHA_SITE_KEY="${GOOGLE_RECAPTCHA_SITE_KEY}" >> .env
            echo ROLLBAR_POST_CLIENT_ITEM_KEY="${ROLLBAR_POST_CLIENT_ITEM_KEY}" >> .env
            echo SUPPORT_EMAIL="${SUPPORT_EMAIL}" >> .env
            echo DEFAULT_LOCALE="${DEFAULT_LOCALE}" >> .env
            echo DEFAULT_TIME_ZONE="${DEFAULT_TIME_ZONE}" >> .env

      - run:
          name: Build client
          working_directory: client
          command: yarn build:test
          environment:
            AVAILABLE_CPUS: 4

      - save_cache:
          paths:
            - ./client/build
          key: v1-yarn-build-{{ .Revision }}

  setup_db:
    steps:
      - run:
          name: Set up test database
          command: bundle exec rake db:setup
          environment:
            COLLECT_COVERAGE: false

  serve_static_site:
    steps:
      - run:
          name: Download dirt-cheap-rocket
          command: curl https://github.com/Coursemology/dirt-cheap-rocket/releases/latest/download/dirt-cheap-rocket.cjs -o dirt-cheap-rocket.cjs -L
      - run:
          name: Serve static site
          command: node dirt-cheap-rocket.cjs
          background: true
          environment:
            DCR_CLIENT_PORT: 3200
            DCR_SERVER_PORT: 7979
            DCR_PUBLIC_PATH: /static
            DCR_ASSETS_DIR: client/build

  serve_rails_server:
    steps:
      - run:
          name: Serve Rails server
          command: bundle exec rails s -p 7979
          background: true

      - run:
          name: Wait for Rails server
          command: curl -s --retry 1000 --retry-delay 1 --retry-connrefused http://lvh.me:7979

  terminate_rails_and_wait_for_coverage_results:
    steps:
      - run:
          name: Terminate Rails server
          command: pkill -SIGTERM -f puma

      - run:
          name: Wait for Rails coverage results
          no_output_timeout: 5m
          command: until [ -f coverage/codecov-result.json ]; do sleep 1; done

  setup_docker_layer_cache:
    steps:
      - setup_remote_docker:
          docker_layer_caching: true

  # Install Ghostscript so `identify` in ImageMagick works with PDF files.
  # To remove PDF security policy for ImageMagick (Ubuntu 20.04), see https://stackoverflow.com/questions/52998331/imagemagick-security-policy-pdf-blocking-conversion
  # This is currently not used as CircleCI would fail to install occasionally.
  install_ghostscript_and_imagemagick:
    steps:
      - run:
          name: Install Ghostscript and ImageMagick
          command: |
            sudo apt update
            sudo apt install imagemagick
            sudo apt install ghostscript
            sudo sed -i '/disable ghostscript format types/,+6d' /etc/ImageMagick-6/policy.xml

jobs:
  build_client:
    executor: node

    steps:
      - checkout_with_submodules
      - rehydrate_node_deps
      - build_and_cache_client

  test_playwright:
    executor:
      name: ruby_with_postgres
      collects_rails_coverage: true

    parallelism: 10

    steps:
      - checkout_with_submodules
      - setup_docker_layer_cache

      - rehydrate_ruby_deps
      - restore_client_cache

      - setup_db
      - serve_static_site
      - serve_rails_server

      - run:
          name: Install Playwright dependencies
          working_directory: tests
          command: yarn install; yarn prepare

      - run:
          name: Run Playwright tests
          working_directory: tests
          command: |
            SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npx playwright test --shard=${SHARD}/${CIRCLE_NODE_TOTAL} --reporter=junit
          environment:
            PLAYWRIGHT_JUNIT_OUTPUT_NAME: results.xml

      - run:
          name: Generate code coverage
          working_directory: tests
          command: yarn coverage

      - terminate_rails_and_wait_for_coverage_results

      - run:
          name: Zip test results
          when: always
          working_directory: tests
          command: |
            zip -r test-results.zip test-results

      # - codecov/upload:
      #     upload_name: playwright-client
      #     file: tests/coverage/lcov.info

      - codecov/upload:
          upload_name: playwright-rails
          file: coverage/codecov-result.json

      - store_test_results:
          path: ~/repo/tests/results.xml

      - store_artifacts:
          path: ~/repo/tests/results.xml

      - store_artifacts:
          path: ~/repo/tests/test-results.zip

  test_rspec:
    executor:
      name: ruby_with_postgres
      collects_rails_coverage: true

    parallelism: 30

    steps:
      - checkout_with_submodules
      - setup_docker_layer_cache

      - rehydrate_ruby_deps
      - restore_client_cache

      - setup_db
      - serve_static_site

      - run:
          name: Run RSpec tests
          no_output_timeout: 10m
          command: |
            mkdir ~/rspec
            circleci tests glob "spec/**/*_spec.rb" | circleci tests run --command="xargs bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml" --verbose --split-by=timings

      - store_test_results:
          path: ~/rspec

  factorybot_lint:
    executor: ruby_with_postgres

    steps:
      - checkout
      - rehydrate_ruby_deps
      - setup_db

      - run:
          name: Run FactoryBot lint
          command: bundle exec rake factory_bot:lint

  jslint:
    executor: node

    steps:
      - checkout_with_submodules
      - rehydrate_node_deps

      - run:
          name: Run ESLint and Prettier checks
          working_directory: client
          command: yarn lint

  jstest:
    executor: node

    steps:
      - checkout_with_submodules
      - rehydrate_node_deps

      - run:
          name: Build translations
          working_directory: client
          command: yarn run build:translations

      - run:
          name: Run Jest tests
          working_directory: client
          command: yarn testci

  i18n_en:
    executor: ruby_with_postgres

    steps:
      - checkout
      - rehydrate_ruby_deps
      - setup_db

      - run:
          name: Check for unused translations (English)
          command: bundle exec i18n-tasks unused --locales en

      - run:
          name: Check for missing translations (English)
          command: bundle exec i18n-tasks missing --locales en

  i18n_zh:
    executor: ruby_with_postgres

    steps:
      - checkout
      - rehydrate_ruby_deps
      - setup_db

      - run:
          name: Check for unused translations (Mandarin)
          command: bundle exec i18n-tasks unused --locales zh

      - run:
          name: Check for missing translations (Mandarin)
          command: bundle exec i18n-tasks missing --locales zh

workflows:
  build_and_test_and_lint:
    jobs:
      - jslint
      - jstest
      - build_client
      - i18n_en
      - i18n_zh
      - factorybot_lint
      - test_rspec:
          requires:
            - build_client
            - factorybot_lint
      - test_playwright:
          requires:
            - build_client