name: frontend

on:
  push:
    branches:
      - master
  pull_request:

# Cancel in progress workflows on pull_requests.
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

# hack for https://github.com/actions/cache/issues/810#issuecomment-1222550359
env:
  SEGMENT_DOWNLOAD_TIMEOUT_MINS: 3

jobs:
  files-changed:
    name: detect what files changed
    runs-on: ubuntu-20.04
    timeout-minutes: 3
    # Map a step output to a job output
    outputs:
      eslint_config: ${{ steps.changes.outputs.eslint_config }}
      frontend: ${{ steps.changes.outputs.frontend_all }}
      frontend_components_modified_lintable: ${{ steps.changes.outputs.frontend_components_modified_lintable }}
      frontend_components_modified_lintable_files: ${{ steps.changes.outputs.frontend_components_modified_lintable_files }}
      frontend_modified_lintable_files: ${{ steps.changes.outputs.frontend_modified_lintable_files }}
      yarn_lockfile: ${{ steps.changes.outputs.yarn_lockfile }}
    steps:
      - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8  # v3.1.0

      - name: Check for frontend file changes
        uses: getsentry/paths-filter@4512585405083f25c027a35db413c2b3b9006d50  # v2.11.1
        id: changes
        with:
          token: ${{ github.token }}
          filters: .github/file-filters.yml
          list-files: shell

  typescript-and-lint:
    if: needs.files-changed.outputs.frontend == 'true'
    needs: files-changed
    name: typescript and lint
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8  # v3.1.0

      - name: Internal github app token
        id: token
        uses: getsentry/action-github-app-token@97c9e23528286821f97fba885c1b1123284b29cc  # v2.0.0
        continue-on-error: true
        with:
          app_id: ${{ secrets.SENTRY_INTERNAL_APP_ID }}
          private_key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}

      - uses: getsentry/action-setup-volta@54775a59c41065f54ecc76d1dd5f2cdc7a1550cb  # v1.1.0

      - name: Install dependencies
        id: dependencies
        run: yarn install --frozen-lockfile

      # Setup custom tsc matcher, see https://github.com/actions/setup-node/issues/97
      - name: setup matchers
        run: |
          echo "::remove-matcher owner=masters::"
          echo "::add-matcher::.github/tsc.json"
          echo "::add-matcher::.github/eslint-stylish.json"

      - name: eslint logic
        id: eslint
        if: (github.ref == 'refs/heads/master' || needs.files-changed.outputs.eslint_config == 'true' || needs.files-changed.outputs.yarn_lockfile == 'true')
        run: echo "all-files=true" >> "$GITHUB_OUTPUT"

      # Lint entire frontend if:
      # - this is on main branch
      # - eslint configuration in repo has changed
      # - yarn lockfile has changed (i.e. we bump our eslint config)
      - name: eslint
        if: steps.eslint.outputs.all-files == 'true'
        env:
          # Run relax config on main branch (and stricter config for changed files)
          SENTRY_ESLINT_RELAXED: 1
        run: |
          yarn lint
          yarn lint:css

      # Otherwise... only lint modified files
      # Note `eslint --fix` will not fail when it auto fixes files
      - name: eslint (changed files only)
        if: steps.eslint.outputs.all-files != 'true'
        run: |
          yarn eslint --fix ${{ needs.files-changed.outputs.frontend_modified_lintable_files }}

      - name: stylelint (changed files only)
        if: github.ref != 'refs/heads/master' && needs.files-changed.outputs.frontend_components_modified_lintable == 'true'
        run: |
          yarn stylelint ${{ needs.files-changed.outputs.frontend_components_modified_lintable_files }}

      # Check (and error) for dirty working tree for forks
      # Reason being we need a different token to auto commit changes and
      # forks do not have access to said token
      - name: Check for dirty git working tree (forks)
        if: steps.token.outcome != 'success' && github.ref != 'refs/heads/master'
        run: |
          git diff --quiet || (echo '::error ::lint produced file changes, run linter locally and try again' && exit 1)

      # If working tree is dirty, commit and update if we have a token
      - name: Commit any eslint fixed files
        if: steps.token.outcome == 'success' && github.ref != 'refs/heads/master'
        uses: getsentry/action-github-commit@1761f891f036c3efc813b2ba963b121120c1587a  # main
        with:
          github-token: ${{ steps.token.outputs.token }}

      - name: tsc
        id: tsc
        if: steps.dependencies.outcome == 'success'
        run: |
          set -o pipefail
          yarn tsc -p config/tsconfig.ci.json --diagnostics --generateTrace /tmp/trace | tee /tmp/typescript-monitor.log

      - name: monitor-tsc
        continue-on-error: true
        if: steps.tsc.outcome == 'success'
        env:
          GITHUB_PR_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
          GITHUB_PR_REF: ${{ github.event.pull_request.head.ref || github.ref }}
        run: yarn run ts-node .github/workflows/scripts/monitor-typescript.ts

      - name: storybook
        if: github.ref != 'refs/heads/master'
        env:
          STORYBOOK_BUILD: 1
        run: |
          yarn storybook-build

  webpack:
    if: github.ref == 'refs/heads/master' || needs.files-changed.outputs.frontend == 'true'
    needs: files-changed
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8  # v3.1.0

      - uses: getsentry/action-setup-volta@54775a59c41065f54ecc76d1dd5f2cdc7a1550cb  # v1.1.0

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - uses: getsentry/size-limit-action@3f9e584f47175f7f2ac742569ac16b7a8c05ad82  # v4
        env:
          SENTRY_INSTRUMENTATION: 1
          SENTRY_WEBPACK_WEBHOOK_SECRET: ${{ secrets.SENTRY_WEBPACK_WEBHOOK_SECRET }}
        with:
          main_branch: master
          skip_step: install
          build_script: build
          windows_verbatim_arguments: false
          github_token: ${{ secrets.GITHUB_TOKEN }}

  # This check runs once all dependant jobs have passed
  # It symbolizes that all required Frontend checks have succesfully passed (Or skipped)
  # This check is the only required Github check
  frontend-required-check:
    needs: [typescript-and-lint, webpack]
    name: Frontend
    # This is necessary since a failed/skipped dependent job would cause this job to be skipped
    if: always()
    runs-on: ubuntu-20.04
    steps:
      # If any jobs we depend on fail, we will fail since this is a required check
      # NOTE: A timeout is considered a failure
      - name: Check for failures
        if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
        run: |
          echo "One of the dependent jobs have failed. You may need to re-run it." && exit 1