variables:
  NODE_IMAGE: node:12-alpine
  NODE_E2E_IMAGE: circleci/node:12-buster-browsers
  DEPLOY_IMAGE: docksal/ssh-agent:1.3
  # To cache both npm modules and Cypress binary we use environment variables
  # to point at the folders we can list as paths in "cache" job settings
  YARN_CACHE_FOLDER: "$CI_PROJECT_DIR/cache/yarn"
  CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/cache/cypress"

stages:
  - prepare
  - test
  - build
  - deploy

# GitLab don't support bash syntax in the "variables" definitions,
# so we use a custom step to define all necessary environment variables
.defineVars: &defineVars |-
  export VERSION="${CI_COMMIT_TAG:-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}}"

.installSentry: &installSentry |-
  apk add curl
  curl -sL https://sentry.io/get-cli/ | bash

.setupSSH: &setupSSH |-
  mkdir ~/.ssh
  echo -e "Host *\n    StrictHostKeyChecking no\n" > ~/.ssh/config
  eval $(ssh-agent -s)
  echo "$SSH_DEPLOY_KEY" | tr -d '\r' | ssh-add -

###################
# Steps to extend #
###################

.yarnCache:
  cache:
    key: yarn-cache
    paths:
      - node_modules
      - packages/*/node_modules
      - tests-e2e/node_modules # Keep this node_modules because it's one of workspaces
    policy: pull

.yarnE2ECache:
  cache:
    key: yarn-e2e-cache
    paths:
      - cache/cypress
      - node_modules
      - packages/*/node_modules
      - tests-e2e/node_modules
    policy: pull

#################
# Prepare stage #
#################

Yarn:
  stage: prepare
  image: $NODE_IMAGE
  extends:
    - .yarnCache
  cache:
    policy: pull-push
  variables:
    CYPRESS_INSTALL_BINARY: 0 # Don't install binary to increase caching performance between jobs
  before_script:
    - apk add git
  script:
    - yarn install --frozen-lockfile

Yarn E2E:
  stage: prepare
  image: $NODE_E2E_IMAGE
  extends:
    - .yarnE2ECache
  cache:
    policy: pull-push
  script:
    - yarn install --frozen-lockfile
  only:
    refs:
      - master

#################
# Testing stage #
#################

Lint:
  stage: test
  image: $NODE_IMAGE
  needs:
    - Yarn
  extends:
    - .yarnCache
  script:
    - yarn lint:check

TypeScript:
  stage: test
  image: $NODE_IMAGE
  needs:
    - Yarn
  extends:
    - .yarnCache
  script:
    - yarn ts:check

Jest:
  stage: test
  image: $NODE_IMAGE
  needs:
    - Yarn
  extends:
    - .yarnCache
  script:
    - yarn test

Crowdin:
  stage: test
  image: $NODE_IMAGE
  needs:
    - Yarn
  extends:
    - .yarnCache
  script:
    - yarn i18n:extract
    - yarn i18n:push
    - yarn i18n:pull
  artifacts:
      name: "i18n for $CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA"
      paths:
        - packages/app/i18n/*.json
        - packages/app/i18n/index.js
      expire_in: 1 week

Cypress:
  stage: test
  image: $NODE_E2E_IMAGE
  needs:
    - Yarn E2E
  extends:
    - .yarnE2ECache
  variables:
    # Sentry's DSN is provided for build step from repository configuration,
    # but it's not necessary to send any logs reports during E2E testing
    SENTRY_DSN: ""
  script:
    - yarn start &
    - yarn wait-on http://localhost:8080
    - >
      yarn --cwd tests-e2e test:ci
      --record
      --browser chrome
      --parallel
  parallel: 4
  timeout: 20m
  allow_failure: true
  only:
    refs:
      - master

###############
# Build stage #
###############

Build:
  stage: build
  image: $NODE_IMAGE
  needs:
    - Lint
    - TypeScript
    - Jest
    - Crowdin
  extends:
    - .yarnCache
  before_script:
    - *defineVars
  script:
    - yarn build
    # Remove unneeded files
    - rm -rf build/*.css.map
    # Move all source maps to its own directory
    - mkdir -p source-maps
    - cd build
    - find . -name '*.js.map' | cpio -pdm ../source-maps/
    - rm -rf *.js.map
    - find . -name '*.js' | cpio -pdm ../source-maps/
    - cd ..
  artifacts:
    name: "Production build for $CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA"
    paths:
      - build
      - source-maps
    expire_in: 1 week

Storybook:
  stage: build
  image: $NODE_IMAGE
  needs:
    - Yarn
    - Crowdin
  extends:
    - .yarnCache
  script:
    - yarn sb:build --quiet
  artifacts:
    name: "Storybook for $CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA"
    paths:
      - storybook-static
    expire_in: 1 day

################
# Deploy stage #
################

.deployJob:
  stage: deploy
  image: $DEPLOY_IMAGE
  needs:
    - Build
  before_script:
    - *defineVars
    - *installSentry
    - *setupSSH
    - apk add rsync
  script:
    # Send release to the Sentry
    - sentry-cli releases new $VERSION
    - sentry-cli releases set-commits --commit "elyby/accounts-frontend@${CI_COMMIT_SHA}" $VERSION
    - sentry-cli releases files $VERSION upload-sourcemaps source-maps
    - rsync -v -a -r --delete-after -e "ssh -J deploy@ely.by:4534 -p 722" build/ "root@${VM_HOST_NAME}:${VM_DEPLOY_PATH}"
    # Finalize Sentry's release
    - sentry-cli releases deploys $VERSION new -e $CI_ENVIRONMENT_NAME
    - sentry-cli releases finalize $VERSION

Deploy dev:
  extends:
    - .deployJob
  environment:
    name: Development
  variables:
    VM_HOST_NAME: playground.ely.local
    VM_DEPLOY_PATH: /srv/dev.account.ely.by/frontend
  only:
    refs:
      - master

Deploy prod:
  extends:
    - .deployJob
  stage: deploy
  environment:
    name: Production
  variables:
    VM_HOST_NAME: accounts.ely.local
    VM_DEPLOY_PATH: /srv/frontend
  rules:
    - if: '$CI_COMMIT_BRANCH != "master"'
      when: never
    - if: '$CI_COMMIT_MESSAGE =~ /\[deploy\]/'
      when: on_success
    - when: manual
