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@bec0adb2924f8c19a9f84c18c83d375caf02ca38 # main with: github-token: ${{ steps.token.outputs.token }} - name: tsc id: tsc if: steps.dependencies.outcome == 'success' run: yarn tsc -p config/tsconfig.ci.json frontend-browser-tests: if: needs.files-changed.outputs.frontend == 'true' needs: files-changed name: Browser tests # If you change the runs-on image, you must also change the runner in jest-balance.yml # so that the balancer runs in the same environment as the tests. runs-on: ubuntu-20.04 timeout-minutes: 30 strategy: # This helps not having to run multiple jobs because one fails, thus, reducing resource usage # and reducing the risk that one of many runs would turn red again (read: intermittent tests) fail-fast: false matrix: # XXX: When updating this, make sure you also update CI_NODE_TOTAL. instance: [0, 1, 2, 3] env: VISUAL_HTML_ENABLE: 1 steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 name: Checkout sentry with: # Avoid codecov error message related to SHA resolution: # https://github.com/codecov/codecov-bash/blob/7100762afbc822b91806a6574658129fe0d23a7d/codecov#L891 fetch-depth: '2' - uses: getsentry/action-setup-volta@54775a59c41065f54ecc76d1dd5f2cdc7a1550cb # v1.1.0 - name: node_modules cache uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # v3.0.11 id: nodemodulescache with: path: node_modules key: ${{ runner.os }}-node-modules-${{ hashFiles('yarn.lock', 'api-docs/yarn.lock') }} - name: Install Javascript Dependencies if: steps.nodemodulescache.outputs.cache-hit != 'true' run: yarn install --frozen-lockfile - name: Build CSS run: NODE_ENV=production yarn build-css - name: jest env: GITHUB_PR_SHA: ${{ github.event.pull_request.head.sha || github.sha }} GITHUB_PR_REF: ${{ github.event.pull_request.head.ref || github.ref }} # XXX: CI_NODE_TOTAL must be hardcoded to the length of strategy.matrix.instance. # Otherwise, if there are other things in the matrix, using strategy.job-total # wouldn't be correct. CI_NODE_TOTAL: 4 CI_NODE_INDEX: ${{ matrix.instance }} run: | SENTRY_PROFILER_LOGGING_MODE=eager JEST_TESTS=$(yarn -s jest --listTests --json) yarn test-ci --forceExit - name: Save HTML artifacts uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1 with: retention-days: 14 name: jest-html path: .artifacts/visual-snapshots/jest - name: Create Images from HTML uses: getsentry/action-html-to-image@dc153dae538e6e1138f77156d8e62e3b2b897f41 # main with: base-path: .artifacts/visual-snapshots/jest css-path: src/sentry/static/sentry/dist/entrypoints/sentry.css - name: Save snapshots uses: getsentry/action-visual-snapshot@ea663f1be8b73c86b18081d714423ce52352e934 with: artifact-name: frontend-snapshots save-only: true snapshot-path: .artifacts/visual-snapshots # We only upload coverage data for FE changes since it conflicts with # codecov's carry forward functionality. # Upload coverage data even if running the tests step fails since # it reduces large coverage fluctuations. - name: Handle artifacts uses: ./.github/actions/artifacts if: ${{ always() && needs.files-changed.outputs.frontend_all == 'true' }} with: files: .artifacts/coverage/* type: frontend frontend-visual-diff: needs: [frontend-browser-tests] name: Visual diff runs-on: ubuntu-20.04 timeout-minutes: 20 if: github.event.pull_request.head.repo.full_name == 'getsentry/sentry' steps: - name: Diff snapshots uses: getsentry/action-visual-snapshot@ea663f1be8b73c86b18081d714423ce52352e934 with: action-name: 'Visual Snapshot: Frontend' artifact-name: 'frontend-snapshots' base-branch: 'master' api-token: ${{ secrets.VISUAL_SNAPSHOT_SECRET }} gcs-bucket: 'sentry-visual-snapshots' gcp-service-account-key: ${{ secrets.SNAPSHOT_GOOGLE_SERVICE_ACCOUNT_KEY }} # 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, frontend-browser-tests] 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