New upstream version 13.5.5

This commit is contained in:
Pirate Praveen 2021-01-03 14:25:43 +05:30
parent 03a7b5db89
commit 30798b500b
6091 changed files with 237405 additions and 85396 deletions

2
.gitignore vendored
View file

@ -96,4 +96,4 @@ apollo.config.js
/tmp/matching_foss_tests.txt /tmp/matching_foss_tests.txt
/tmp/matching_tests.txt /tmp/matching_tests.txt
ee/changelogs/unreleased-ee ee/changelogs/unreleased-ee
/sitespeed-result

View file

@ -17,7 +17,7 @@ stages:
# in cases where jobs require Docker-in-Docker, the job # in cases where jobs require Docker-in-Docker, the job
# definition must be extended with `.use-docker-in-docker` # definition must be extended with `.use-docker-in-docker`
default: default:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
tags: tags:
- gitlab-org - gitlab-org
# All jobs are interruptible by default # All jobs are interruptible by default

View file

@ -184,7 +184,7 @@ Dangerfile @gl-quality/eng-prod
/lib/gitlab/auth/ldap/ @dblessing @mkozono /lib/gitlab/auth/ldap/ @dblessing @mkozono
[Templates] [Templates]
/lib/gitlab/ci/templates/ @nolith @dosuken123 /lib/gitlab/ci/templates/ @nolith @shinya.maeda
/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah /lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
/lib/gitlab/ci/templates/Security/ @plafoucriere @gonzoyumo @twoodham @sethgitlab /lib/gitlab/ci/templates/Security/ @plafoucriere @gonzoyumo @twoodham @sethgitlab
@ -213,10 +213,10 @@ Dangerfile @gl-quality/eng-prod
/ee/spec/lib/gitlab/code_owners/ @reprazent @kerrizor @garyh /ee/spec/lib/gitlab/code_owners/ @reprazent @kerrizor @garyh
/doc/user/project/code_owners.md @reprazent @kerrizor @garyh /doc/user/project/code_owners.md @reprazent @kerrizor @garyh
[Telemetry] [Product Analytics]
/ee/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry/engineers /ee/lib/gitlab/usage_data_counters/ @gitlab-org/growth/product_analytics/engineers
/ee/lib/ee/gitlab/usage_data.rb @gitlab-org/growth/telemetry/engineers /ee/lib/ee/gitlab/usage_data.rb @gitlab-org/growth/product_analytics/engineers
/lib/gitlab/grafana_embed_usage_data.rb @gitlab-org/growth/telemetry/engineers /lib/gitlab/grafana_embed_usage_data.rb @gitlab-org/growth/product_analytics/engineers
/lib/gitlab/usage_data.rb @gitlab-org/growth/telemetry/engineers /lib/gitlab/usage_data.rb @gitlab-org/growth/product_analytics/engineers
/lib/gitlab/cycle_analytics/usage_data.rb @gitlab-org/growth/telemetry/engineers /lib/gitlab/cycle_analytics/usage_data.rb @gitlab-org/growth/product_analytics/engineers
/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry/engineers /lib/gitlab/usage_data_counters/ @gitlab-org/growth/product_analytics/engineers

View file

@ -28,6 +28,8 @@
# Help pages are excluded from scan as they are static pages. # Help pages are excluded from scan as they are static pages.
# profile/two_factor_auth is excluded from scan to prevent 2FA from being turned on from user profile, which will reduce coverage. # profile/two_factor_auth is excluded from scan to prevent 2FA from being turned on from user profile, which will reduce coverage.
- 'export DAST_AUTH_EXCLUDE_URLS="${DAST_WEBSITE}/help/.*,${DAST_WEBSITE}/profile/two_factor_auth,${DAST_WEBSITE}/users/sign_out"' - 'export DAST_AUTH_EXCLUDE_URLS="${DAST_WEBSITE}/help/.*,${DAST_WEBSITE}/profile/two_factor_auth,${DAST_WEBSITE}/users/sign_out"'
# Exclude the automatically generated monitoring project from being tested due to https://gitlab.com/gitlab-org/gitlab/-/issues/260362
- 'DAST_AUTH_EXCLUDE_URLS="${DAST_AUTH_EXCLUDE_URLS},https://.*\.gitlab-review\.app/gitlab-instance-(administrators-)?[a-zA-Z0-9]{8}/.*"'
- enable_rule () { read all_rules; rule=$1; echo $all_rules | sed -r "s/(,)?$rule(,)?/\1-1\2/" ; } - enable_rule () { read all_rules; rule=$1; echo $all_rules | sed -r "s/(,)?$rule(,)?/\1-1\2/" ; }
# Sort ids in DAST_RULES ascendingly, which is required when using DAST_RULES as argument to enable_rule # Sort ids in DAST_RULES ascendingly, which is required when using DAST_RULES as argument to enable_rule
- 'DAST_RULES=$(echo $DAST_RULES | tr "," "\n" | sort -n | paste -sd ",")' - 'DAST_RULES=$(echo $DAST_RULES | tr "," "\n" | sort -n | paste -sd ",")'

View file

@ -14,14 +14,17 @@
SIZE: 0 # number of external projects to fork, requires network connection SIZE: 0 # number of external projects to fork, requires network connection
# SEED_NESTED_GROUPS: "false" # requires network connection # SEED_NESTED_GROUPS: "false" # requires network connection
.run-dev-fixtures-script: &run-dev-fixtures-script
- run_timed_command "scripts/gitaly-test-build"
- run_timed_command "scripts/gitaly-test-spawn"
- run_timed_command "RAILS_ENV=test bundle exec rake db:seed_fu"
run-dev-fixtures: run-dev-fixtures:
extends: extends:
- .run-dev-fixtures - .run-dev-fixtures
- .dev-fixtures:rules:ee-and-foss - .dev-fixtures:rules:ee-and-foss
script: script:
- run_timed_command "scripts/gitaly-test-build" - *run-dev-fixtures-script
- run_timed_command "scripts/gitaly-test-spawn"
- run_timed_command "RAILS_ENV=test bundle exec rake db:seed_fu"
run-dev-fixtures-ee: run-dev-fixtures-ee:
extends: extends:
@ -29,7 +32,5 @@ run-dev-fixtures-ee:
- .dev-fixtures:rules:ee-only - .dev-fixtures:rules:ee-only
- .use-pg11-ee - .use-pg11-ee
script: script:
- run_timed_command "scripts/gitaly-test-build"
- run_timed_command "scripts/gitaly-test-spawn"
- cp ee/db/fixtures/development/* $FIXTURE_PATH - cp ee/db/fixtures/development/* $FIXTURE_PATH
- run_timed_command "RAILS_ENV=test bundle exec rake db:seed_fu" - *run-dev-fixtures-script

View file

@ -4,7 +4,7 @@
- .docs:rules:review-docs - .docs:rules:review-docs
image: ruby:2.6-alpine image: ruby:2.6-alpine
stage: review stage: review
dependencies: [] needs: []
variables: variables:
# We're cloning the repo instead of downloading the script for now # We're cloning the repo instead of downloading the script for now
# because some repos are private and CI_JOB_TOKEN cannot access files. # because some repos are private and CI_JOB_TOKEN cannot access files.
@ -42,7 +42,7 @@ docs lint:
extends: extends:
- .default-retry - .default-retry
- .docs:rules:docs-lint - .docs:rules:docs-lint
image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint:vale-2.3.4-markdownlint-0.23.2" image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint:ruby-2.7.2-alpine-3.12-vale-2.4.3-markdownlint-0.24.0"
stage: test stage: test
needs: [] needs: []
script: script:

View file

@ -7,19 +7,21 @@
# we override the max_old_space_size to prevent OOM errors # we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584 NODE_OPTIONS: --max_old_space_size=3584
.yarn-install: &yarn-install
- source scripts/utils.sh
- run_timed_command "retry yarn install --frozen-lockfile"
.compile-assets-base: .compile-assets-base:
extends: extends:
- .frontend-base - .frontend-base
- .assets-compile-cache - .assets-compile-cache
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-git-2.28-lfs-2.9-node-12.x-yarn-1.21-graphicsmagick-1.3.34 image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-git-2.28-lfs-2.9-node-12.18-yarn-1.22-graphicsmagick-1.3.34
variables: variables:
WEBPACK_VENDOR_DLL: "true" WEBPACK_VENDOR_DLL: "true"
stage: prepare stage: prepare
script: script:
- node --version - *yarn-install
- run_timed_command "retry yarn install --frozen-lockfile" - run_timed_command "bin/rake gitlab:assets:compile"
- free -m
- run_timed_command "bin/rake gitlab:assets:compile > assets-compile.log 2>&1"
- run_timed_command "scripts/clean-old-cached-assets" - run_timed_command "scripts/clean-old-cached-assets"
compile-production-assets: compile-production-assets:
@ -34,7 +36,6 @@ compile-production-assets:
name: webpack-report name: webpack-report
expire_in: 31d expire_in: 31d
paths: paths:
- assets-compile.log
# These assets are used in multiple locations: # These assets are used in multiple locations:
# - in `build-assets-image` job to create assets image for packaging systems # - in `build-assets-image` job to create assets image for packaging systems
# - GitLab UI for integration tests: https://gitlab.com/gitlab-org/gitlab-ui/-/blob/e88493b3c855aea30bf60baee692a64606b0eb1e/.storybook/preview-head.pug#L1 # - GitLab UI for integration tests: https://gitlab.com/gitlab-org/gitlab-ui/-/blob/e88493b3c855aea30bf60baee692a64606b0eb1e/.storybook/preview-head.pug#L1
@ -51,7 +52,6 @@ compile-test-assets:
artifacts: artifacts:
expire_in: 7d expire_in: 7d
paths: paths:
- assets-compile.log
- public/assets/ - public/assets/
- node_modules/@gitlab/svgs/dist/icons.json # app/helpers/icons_helper.rb uses this file - node_modules/@gitlab/svgs/dist/icons.json # app/helpers/icons_helper.rb uses this file
when: always when: always
@ -87,8 +87,7 @@ update-yarn-cache:
- .shared:rules:update-cache - .shared:rules:update-cache
stage: prepare stage: prepare
script: script:
- source scripts/utils.sh - *yarn-install
- run_timed_command "retry yarn install --frozen-lockfile"
cache: cache:
policy: push policy: push
@ -139,14 +138,14 @@ eslint-as-if-foss:
- .as-if-foss - .as-if-foss
needs: [] needs: []
script: script:
- run_timed_command "retry yarn install --frozen-lockfile" - *yarn-install
- yarn run eslint - run_timed_command "yarn run eslint"
.karma-base: .karma-base:
extends: .frontend-test-base extends: .frontend-test-base
script: script:
- export BABEL_ENV=coverage CHROME_LOG_FILE=chrome_debug.log - export BABEL_ENV=coverage CHROME_LOG_FILE=chrome_debug.log
- run_timed_command "retry yarn install --frozen-lockfile" - *yarn-install
- run_timed_command "yarn karma" - run_timed_command "yarn karma"
karma: karma:
@ -177,7 +176,7 @@ karma-as-if-foss:
.jest-base: .jest-base:
extends: .frontend-test-base extends: .frontend-test-base
script: script:
- run_timed_command "retry yarn install --frozen-lockfile" - *yarn-install
- run_timed_command "yarn jest --ci --coverage --testSequencer ./scripts/frontend/parallel_ci_sequencer.js" - run_timed_command "yarn jest --ci --coverage --testSequencer ./scripts/frontend/parallel_ci_sequencer.js"
jest: jest:
@ -202,7 +201,7 @@ jest-integration:
- .frontend-test-base - .frontend-test-base
- .frontend:rules:default-frontend-jobs - .frontend:rules:default-frontend-jobs
script: script:
- run_timed_command "retry yarn install --frozen-lockfile" - *yarn-install
- run_timed_command "yarn jest:integration --ci" - run_timed_command "yarn jest:integration --ci"
needs: ["frontend-fixtures"] needs: ["frontend-fixtures"]
@ -222,8 +221,7 @@ coverage-frontend:
needs: ["jest"] needs: ["jest"]
stage: post-test stage: post-test
before_script: before_script:
- source scripts/utils.sh - *yarn-install
- run_timed_command "retry yarn install --frozen-lockfile"
script: script:
- run_timed_command "yarn node scripts/frontend/merge_coverage_frontend.js" - run_timed_command "yarn node scripts/frontend/merge_coverage_frontend.js"
coverage: '/^Statements\s*:\s*?(\d+(?:\.\d+)?)%/' coverage: '/^Statements\s*:\s*?(\d+(?:\.\d+)?)%/'
@ -243,9 +241,8 @@ coverage-frontend:
stage: test stage: test
dependencies: [] dependencies: []
script: script:
- source scripts/utils.sh - *yarn-install
- run_timed_command "yarn install --frozen-lockfile" - run_timed_command "retry yarn run webpack-prod"
- run_timed_command "yarn run webpack-prod"
qa-frontend-node:10: qa-frontend-node:10:
extends: .qa-frontend-node extends: .qa-frontend-node
@ -268,8 +265,7 @@ webpack-dev-server:
WEBPACK_MEMORY_TEST: "true" WEBPACK_MEMORY_TEST: "true"
WEBPACK_VENDOR_DLL: "true" WEBPACK_VENDOR_DLL: "true"
script: script:
- source scripts/utils.sh - *yarn-install
- run_timed_command "retry yarn install --frozen-lockfile"
- run_timed_command "retry yarn webpack-vendor" - run_timed_command "retry yarn webpack-vendor"
- run_timed_command "node --expose-gc node_modules/.bin/webpack-dev-server --config config/webpack.config.js" - run_timed_command "node --expose-gc node_modules/.bin/webpack-dev-server --config config/webpack.config.js"
artifacts: artifacts:

View file

@ -34,6 +34,13 @@
- tmp/rubocop_cache/ - tmp/rubocop_cache/
policy: pull policy: pull
.coverage-cache:
cache:
key: "coverage-cache-v1"
paths:
- vendor/ruby/
policy: pull
.qa-cache: .qa-cache:
cache: cache:
key: "qa-v1" key: "qa-v1"
@ -64,7 +71,7 @@
policy: pull policy: pull
.use-pg11: .use-pg11:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
services: services:
- name: postgres:11.6 - name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -73,7 +80,7 @@
POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_HOST_AUTH_METHOD: trust
.use-pg12: .use-pg12:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-12-graphicsmagick-1.3.34" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
services: services:
- name: postgres:12 - name: postgres:12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -82,22 +89,24 @@
POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_HOST_AUTH_METHOD: trust
.use-pg11-ee: .use-pg11-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-11-graphicsmagick-1.3.34"
services: services:
- name: postgres:11.6 - name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:4.0-alpine - name: redis:4.0-alpine
- name: elasticsearch:6.4.2 - name: elasticsearch:7.9.2
command: ["elasticsearch", "-E", "discovery.type=single-node"]
variables: variables:
POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_HOST_AUTH_METHOD: trust
.use-pg12-ee: .use-pg12-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-12-graphicsmagick-1.3.34" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-85-node-12.18-yarn-1.22-postgresql-12-graphicsmagick-1.3.34"
services: services:
- name: postgres:12 - name: postgres:12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:4.0-alpine - name: redis:4.0-alpine
- name: elasticsearch:6.4.2 - name: elasticsearch:7.9.2
command: ["elasticsearch", "-E", "discovery.type=single-node"]
variables: variables:
POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_HOST_AUTH_METHOD: trust

View file

@ -3,6 +3,8 @@
stage: notify stage: notify
dependencies: [] dependencies: []
cache: {} cache: {}
variables:
MERGE_REQUEST_URL: ${CI_MERGE_REQUEST_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}
before_script: before_script:
- apk update && apk add git curl bash - apk update && apk add git curl bash
@ -16,8 +18,19 @@ notify-update-gitaly:
variables: variables:
NOTIFY_CHANNEL: g_create_gitaly NOTIFY_CHANNEL: g_create_gitaly
GITALY_UPDATE_BRANCH: release-tools/update-gitaly GITALY_UPDATE_BRANCH: release-tools/update-gitaly
MERGE_REQUEST_URL: ${CI_MERGE_REQUEST_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}
script: script:
- echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}" - echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
- echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}" - echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
- scripts/slack ${NOTIFY_CHANNEL} "☠️ \`${GITALY_UPDATE_BRANCH}\` failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing - scripts/slack ${NOTIFY_CHANNEL} "☠️ \`${GITALY_UPDATE_BRANCH}\` failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab QA Bot"
notify-security-pipeline:
extends:
- .notify-slack
- .delivery:rules:security-pipeline-merge-result-failure
variables:
NOTIFY_CHANNEL: f_upcoming_release
script:
- echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
- echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
# <!subteam^S0127FU8PDE> mentions the `@release-managers` group
- scripts/slack ${NOTIFY_CHANNEL} "<!subteam^S0127FU8PDE> ☠️ Pipeline for merged result failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab Release Tools Bot"

View file

@ -6,14 +6,23 @@
- .default-before_script - .default-before_script
- .rails-cache - .rails-cache
.base-script: &base-script
# Only install knapsack after bundle install! Otherwise oddly some native
# gems could not be found under some circumstance. No idea why, hours wasted.
- run_timed_command "gem install knapsack --no-document"
- run_timed_command "scripts/gitaly-test-build"
- run_timed_command "scripts/gitaly-test-spawn"
- source ./scripts/rspec_helpers.sh
.rspec-base: .rspec-base:
extends: .rails-job-base extends: .rails-job-base
stage: test stage: test
variables:
RUBY_GC_MALLOC_LIMIT: 67108864
RUBY_GC_MALLOC_LIMIT_MAX: 134217728
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"] needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"]
script: script:
- run_timed_command "scripts/gitaly-test-build" - *base-script
- run_timed_command "scripts/gitaly-test-spawn"
- source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration" - rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration"
artifacts: artifacts:
expire_in: 31d expire_in: 31d
@ -25,6 +34,7 @@
- rspec_profiling/ - rspec_profiling/
- tmp/capybara/ - tmp/capybara/
- tmp/memory_test/ - tmp/memory_test/
- tmp/feature_flags/
- log/*.log - log/*.log
reports: reports:
junit: junit_rspec.xml junit: junit_rspec.xml
@ -32,9 +42,7 @@
.rspec-base-migration: .rspec-base-migration:
extends: .rails:rules:ee-and-foss-migration extends: .rails:rules:ee-and-foss-migration
script: script:
- run_timed_command "scripts/gitaly-test-build" - *base-script
- run_timed_command "scripts/gitaly-test-spawn"
- source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag level:migration" - rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag level:migration"
.rspec-base-pg11: .rspec-base-pg11:
@ -67,9 +75,7 @@
.rspec-ee-base-geo: .rspec-ee-base-geo:
extends: .rspec-base extends: .rspec-base
script: script:
- run_timed_command "scripts/gitaly-test-build" - *base-script
- run_timed_command "scripts/gitaly-test-spawn"
- source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag geo" - rspec_paralellized_job "--tag ~quarantine --tag geo"
.rspec-ee-base-geo-pg11: .rspec-ee-base-geo-pg11:
@ -160,6 +166,25 @@ update-rails-cache:
cache: cache:
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up. policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
.coverage-base:
extends:
- .default-retry
- .default-before_script
- .coverage-cache
variables:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
update-coverage-cache:
extends:
- .coverage-base
- .shared:rules:update-cache
stage: prepare
script:
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --without default development test production puma unicorn kerberos metrics omnibus ed25519"
cache:
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
.static-analysis-base: .static-analysis-base:
extends: extends:
- .default-retry - .default-retry
@ -178,7 +203,7 @@ update-static-analysis-cache:
script: script:
- rm -rf ./node_modules # We remove node_modules because there's no mechanism to remove stall entries. - rm -rf ./node_modules # We remove node_modules because there's no mechanism to remove stall entries.
- run_timed_command "retry yarn install --frozen-lockfile" - run_timed_command "retry yarn install --frozen-lockfile"
- bundle exec rubocop --parallel # For the moment we only cache `vendor/ruby/`, `node_modules/`, and `tmp/rubocop_cache` so we don't need to run all the tasks, - run_timed_command "bundle exec rubocop --parallel" # For the moment we only cache `vendor/ruby/`, `node_modules/`, and `tmp/rubocop_cache` so we don't need to run all the tasks,
cache: cache:
# We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up but RuboCop has a mechanism # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up but RuboCop has a mechanism
# for keeping only the N latest cache files, so we take advantage of it with `pull-push` and removing `node_modules` at the start of the job. # for keeping only the N latest cache files, so we take advantage of it with `pull-push` and removing `node_modules` at the start of the job.
@ -287,8 +312,7 @@ gitlab:setup:
# db/fixtures/development/04_project.rb thanks to SIZE=1 below # db/fixtures/development/04_project.rb thanks to SIZE=1 below
- git clone https://gitlab.com/gitlab-org/gitlab-test.git - git clone https://gitlab.com/gitlab-org/gitlab-test.git
/home/git/repositories/gitlab-org/gitlab-test.git /home/git/repositories/gitlab-org/gitlab-test.git
- run_timed_command "scripts/gitaly-test-build" - *base-script
- run_timed_command "scripts/gitaly-test-spawn"
- force=yes SIZE=1 FIXTURE_PATH="db/fixtures/development" bundle exec rake gitlab:setup - force=yes SIZE=1 FIXTURE_PATH="db/fixtures/development" bundle exec rake gitlab:setup
artifacts: artifacts:
when: on_failure when: on_failure
@ -313,7 +337,7 @@ db:backup_and_restore:
rspec:coverage: rspec:coverage:
extends: extends:
- .rails-job-base - .coverage-base
- .rails:rules:rspec-coverage - .rails:rules:rspec-coverage
stage: post-test stage: post-test
# We cannot use needs since it would mean needing 84 jobs (since most are parallelized) # We cannot use needs since it would mean needing 84 jobs (since most are parallelized)
@ -333,11 +357,10 @@ rspec:coverage:
- rspec-ee system pg11 geo - rspec-ee system pg11 geo
- memory-static - memory-static
- memory-on-boot - memory-on-boot
variables:
SETUP_DB: "false"
script: script:
- bundle exec scripts/merge-simplecov - run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --without default development test production puma unicorn kerberos metrics omnibus ed25519"
- bundle exec scripts/gather-test-memory-data - run_timed_command "bundle exec scripts/merge-simplecov"
- run_timed_command "bundle exec scripts/gather-test-memory-data"
coverage: '/LOC \((\d+\.\d+%)\) covered.$/' coverage: '/LOC \((\d+\.\d+%)\) covered.$/'
artifacts: artifacts:
name: coverage name: coverage
@ -348,6 +371,32 @@ rspec:coverage:
- tmp/memory_test/ - tmp/memory_test/
reports: reports:
cobertura: coverage/coverage.xml cobertura: coverage/coverage.xml
rspec:feature-flags:
extends:
- .coverage-base
- .rails:rules:rspec-feature-flags
stage: post-test
# We cannot use needs since it would mean needing 84 jobs (since most are parallelized)
# so we use `dependencies` here.
dependencies:
- setup-test-env
- rspec migration pg11
- rspec unit pg11
- rspec integration pg11
- rspec system pg11
- rspec-ee migration pg11
- rspec-ee unit pg11
- rspec-ee integration pg11
- rspec-ee system pg11
- rspec-ee unit pg11 geo
- rspec-ee integration pg11 geo
- rspec-ee system pg11 geo
- memory-static
- memory-on-boot
script:
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --without default development test production puma unicorn kerberos metrics omnibus ed25519"
- run_timed_command "bundle exec scripts/used-feature-flags"
# EE/FOSS: default refs (MRs, master, schedules) jobs # # EE/FOSS: default refs (MRs, master, schedules) jobs #
####################################################### #######################################################
@ -512,9 +561,7 @@ rspec fail-fast:
stage: test stage: test
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"] needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"]
script: script:
- run_timed_command "scripts/gitaly-test-build" - *base-script
- run_timed_command "scripts/gitaly-test-spawn"
- source scripts/rspec_helpers.sh
- rspec_fail_fast tmp/matching_tests.txt "--tag ~quarantine" - rspec_fail_fast tmp/matching_tests.txt "--tag ~quarantine"
artifacts: artifacts:
expire_in: 7d expire_in: 7d
@ -527,9 +574,7 @@ rspec foss-impact:
- .rails:rules:rspec-foss-impact - .rails:rules:rspec-foss-impact
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets as-if-foss", "detect-tests as-if-foss"] needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets as-if-foss", "detect-tests as-if-foss"]
script: script:
- run_timed_command "scripts/gitaly-test-build" - *base-script
- run_timed_command "scripts/gitaly-test-spawn"
- source scripts/rspec_helpers.sh
- rspec_matched_foss_tests tmp/matching_foss_tests.txt "--tag ~quarantine" - rspec_matched_foss_tests tmp/matching_foss_tests.txt "--tag ~quarantine"
artifacts: artifacts:
expire_in: 7d expire_in: 7d

View file

@ -151,3 +151,20 @@ dependency_scanning:
reports: reports:
dependency_scanning: gl-dependency-scanning-report.json dependency_scanning: gl-dependency-scanning-report.json
expire_in: 1 week # GitLab-specific expire_in: 1 week # GitLab-specific
license_scanning:
extends:
- .default-retry
- .reports:rules:license_scanning
stage: test
image:
name: "registry.gitlab.com/gitlab-org/security-products/analyzers/license-finder:3"
entrypoint: [""]
needs: []
script:
- /run.sh analyze .
artifacts:
reports:
license_scanning: gl-license-scanning-report.json
expire_in: 1 week # GitLab-specific
dependencies: []

View file

@ -16,6 +16,11 @@ review-cleanup:
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb - ruby -rrubygems scripts/review_apps/automated_cleanup.rb
- gcp_cleanup - gcp_cleanup
.base-before_script: &base-before_script
- source ./scripts/utils.sh
- source ./scripts/review_apps/review-apps.sh
- install_api_client_dependencies_with_apk
review-build-cng: review-build-cng:
extends: extends:
- .default-retry - .default-retry
@ -23,7 +28,7 @@ review-build-cng:
image: ruby:2.6-alpine image: ruby:2.6-alpine
stage: review-prepare stage: review-prepare
before_script: before_script:
- source scripts/utils.sh - source ./scripts/utils.sh
- install_api_client_dependencies_with_apk - install_api_client_dependencies_with_apk
- install_gitlab_gem - install_gitlab_gem
needs: needs:
@ -62,9 +67,7 @@ review-deploy:
- export GITALY_VERSION=$(<GITALY_SERVER_VERSION) - export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
- export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION) - export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION)
- echo "${CI_ENVIRONMENT_URL}" > environment_url.txt - echo "${CI_ENVIRONMENT_URL}" > environment_url.txt
- source ./scripts/utils.sh - *base-before_script
- install_api_client_dependencies_with_apk
- source scripts/review_apps/review-apps.sh
script: script:
- check_kube_domain - check_kube_domain
- ensure_namespace - ensure_namespace
@ -72,7 +75,7 @@ review-deploy:
- download_chart - download_chart
- date - date
- deploy || (display_deployment_debug && exit 1) - deploy || (display_deployment_debug && exit 1)
- disable_sign_ups - disable_sign_ups || (delete_release && exit 1)
# When the job is manual, review-qa-smoke is also manual and we don't want people # When the job is manual, review-qa-smoke is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them. # to have to manually start the jobs in sequence, so we do it for them.
- '[ -z $CI_JOB_MANUAL ] || play_job "review-qa-smoke"' - '[ -z $CI_JOB_MANUAL ] || play_job "review-qa-smoke"'
@ -81,10 +84,9 @@ review-deploy:
# Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan. # Run seed-dast-test-data.sh only when DAST_RUN is set to true. This is to pupulate review app with data for DAST scan.
# Set DAST_RUN to true when jobs are manually scheduled. # Set DAST_RUN to true when jobs are manually scheduled.
- if [ "$DAST_RUN" == "true" ]; then source scripts/review_apps/seed-dast-test-data.sh; TRACE=1 trigger_proj_user_creation; fi - if [ "$DAST_RUN" == "true" ]; then source scripts/review_apps/seed-dast-test-data.sh; TRACE=1 trigger_proj_user_creation; fi
artifacts: artifacts:
paths: [environment_url.txt] paths: [environment_url.txt]
expire_in: 2 days expire_in: 7 days
when: always when: always
.review-stop-base: .review-stop-base:
@ -98,9 +100,7 @@ review-deploy:
# See https://gitlab.com/gitlab-org/gitlab/issues/191273 # See https://gitlab.com/gitlab-org/gitlab/issues/191273
GIT_DEPTH: 1 GIT_DEPTH: 1
before_script: before_script:
- apk add --update openssl - *base-before_script
- source ./scripts/utils.sh
- source ./scripts/review_apps/review-apps.sh
review-stop-failed-deployment: review-stop-failed-deployment:
extends: extends:
@ -143,8 +143,7 @@ review-stop:
- export CI_ENVIRONMENT_URL="$(cat environment_url.txt)" - export CI_ENVIRONMENT_URL="$(cat environment_url.txt)"
- echo "${CI_ENVIRONMENT_URL}" - echo "${CI_ENVIRONMENT_URL}"
- echo "${QA_IMAGE}" - echo "${QA_IMAGE}"
- source scripts/utils.sh - *base-before_script
- install_api_client_dependencies_with_apk
- gem install gitlab-qa --no-document ${GITLAB_QA_VERSION:+ --version ${GITLAB_QA_VERSION}} - gem install gitlab-qa --no-document ${GITLAB_QA_VERSION:+ --version ${GITLAB_QA_VERSION}}
artifacts: artifacts:
paths: paths:
@ -174,7 +173,7 @@ review-performance:
- .default-retry - .default-retry
- .review:rules:review-performance - .review:rules:review-performance
image: image:
name: sitespeedio/sitespeed.io:6.3.1 name: sitespeedio/sitespeed.io
entrypoint: [""] entrypoint: [""]
stage: qa stage: qa
# This is needed so that manual jobs with needs don't block the pipeline. # This is needed so that manual jobs with needs don't block the pipeline.
@ -232,6 +231,6 @@ danger-review:
stage: test stage: test
needs: [] needs: []
script: script:
- source scripts/utils.sh - source ./scripts/utils.sh
- retry yarn install --frozen-lockfile - run_timed_command "retry yarn install --frozen-lockfile"
- danger --fail-on-errors=true --verbose - danger --fail-on-errors=true --verbose

View file

@ -73,6 +73,12 @@
.if-rspec-fail-fast-skipped: &if-rspec-fail-fast-skipped .if-rspec-fail-fast-skipped: &if-rspec-fail-fast-skipped
if: '$CI_MERGE_REQUEST_TITLE =~ /SKIP RSPEC FAIL-FAST/' if: '$CI_MERGE_REQUEST_TITLE =~ /SKIP RSPEC FAIL-FAST/'
# For Security merge requests, the gitlab-release-tools-bot triggers a new
# pipeline for the "Pipelines for merged results" feature. If the pipeline
# fails, we notify release managers.
.if-security-pipeline-merge-result: &if-security-pipeline-merge-result
if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH && $CI_PROJECT_NAMESPACE == "gitlab-org/security" && $GITLAB_USER_LOGIN == "gitlab-release-tools-bot"'
#################### ####################
# Changes patterns # # Changes patterns #
#################### ####################
@ -132,7 +138,10 @@
.db-patterns: &db-patterns .db-patterns: &db-patterns
- "{,ee/}{,spec/}{db,migrations}/**/*" - "{,ee/}{,spec/}{db,migrations}/**/*"
- "{,ee/}{,spec/}lib/{,ee/}gitlab/database/**/*"
- "{,ee/}{,spec/}lib/{,ee/}gitlab/database{,_spec}.rb"
- "{,ee/}{,spec/}lib/{,ee/}gitlab/background_migration/**/*" - "{,ee/}{,spec/}lib/{,ee/}gitlab/background_migration/**/*"
- "{,ee/}{,spec/}lib/{,ee/}gitlab/background_migration{,_spec}.rb"
- "config/prometheus/common_metrics.yml" # Used by Gitlab::DatabaseImporters::CommonMetrics::Importer - "config/prometheus/common_metrics.yml" # Used by Gitlab::DatabaseImporters::CommonMetrics::Importer
- "{,ee/}app/models/project_statistics.rb" # Used to calculate sizes in migration specs - "{,ee/}app/models/project_statistics.rb" # Used to calculate sizes in migration specs
@ -282,6 +291,14 @@
when: manual when: manual
allow_failure: true allow_failure: true
##################
# Delivery rules #
##################
.delivery:rules:security-pipeline-merge-result-failure:
rules:
- <<: *if-security-pipeline-merge-result
when: on_failure
###################### ######################
# Dev fixtures rules # # Dev fixtures rules #
###################### ######################
@ -336,6 +353,7 @@
.frontend:rules:compile-test-assets: .frontend:rules:compile-test-assets:
rules: rules:
- changes: *code-backstage-qa-patterns - changes: *code-backstage-qa-patterns
- <<: *if-merge-request-title-run-all-rspec
.frontend:rules:compile-test-assets-as-if-foss: .frontend:rules:compile-test-assets-as-if-foss:
rules: rules:
@ -483,6 +501,7 @@
rules: rules:
- <<: *if-default-refs - <<: *if-default-refs
changes: *code-backstage-qa-patterns changes: *code-backstage-qa-patterns
- <<: *if-merge-request-title-run-all-rspec
.rails:rules:ee-only-migration: .rails:rules:ee-only-migration:
rules: rules:
@ -628,6 +647,13 @@
- <<: *if-master-schedule-2-hourly - <<: *if-master-schedule-2-hourly
- <<: *if-merge-request-title-run-all-rspec - <<: *if-merge-request-title-run-all-rspec
.rails:rules:rspec-feature-flags:
rules:
- <<: *if-not-ee
when: never
- <<: *if-master-schedule-2-hourly
- <<: *if-merge-request-title-run-all-rspec
.rails:rules:master-schedule-nightly--code-backstage: .rails:rules:master-schedule-nightly--code-backstage:
rules: rules:
- <<: *if-master-schedule-nightly - <<: *if-master-schedule-nightly
@ -702,6 +728,14 @@
- <<: *if-master-schedule-nightly - <<: *if-master-schedule-nightly
allow_failure: true allow_failure: true
.reports:rules:license_scanning:
rules:
- if: '$LICENSE_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\blicense_scanning\b/'
when: never
- <<: *if-default-refs
changes: *code-backstage-qa-patterns
allow_failure: true
################ ################
# Review rules # # Review rules #
################ ################
@ -859,6 +893,7 @@
- <<: *if-default-refs - <<: *if-default-refs
changes: *code-backstage-patterns changes: *code-backstage-patterns
when: on_success when: on_success
- <<: *if-merge-request-title-run-all-rspec
.test-metadata:rules:update-tests-metadata: .test-metadata:rules:update-tests-metadata:
rules: rules:

View file

@ -38,6 +38,6 @@ update-tests-metadata:
- rspec-ee integration pg11 geo - rspec-ee integration pg11 geo
- rspec-ee system pg11 geo - rspec-ee system pg11 geo
script: script:
- retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document - run_timed_command "retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document"
- source scripts/rspec_helpers.sh - source ./scripts/rspec_helpers.sh
- update_tests_metadata - update_tests_metadata

View file

@ -30,14 +30,14 @@ If applicable, any groups/projects that are happy to have this feature turned on
## Roll Out Steps ## Roll Out Steps
- [ ] Enable on staging - [ ] Enable on staging (`/chatops run feature set feature_name true --staging`)
- [ ] Test on staging - [ ] Test on staging
- [ ] Ensure that documentation has been updated - [ ] Ensure that documentation has been updated
- [ ] Enable on GitLab.com for individual groups/projects listed above and verify behaviour - [ ] Enable on GitLab.com for individual groups/projects listed above and verify behaviour (`/chatops run feature set --project=gitlab-org/gitlab feature_name true`)
- [ ] Coordinate a time to enable the flag with `#production` and `#g_delivery` on slack. - [ ] Coordinate a time to enable the flag with `#production` and `#g_delivery` on slack.
- [ ] Announce on the issue an estimated time this will be enabled on GitLab.com - [ ] Announce on the issue an estimated time this will be enabled on GitLab.com
- [ ] Enable on GitLab.com by running chatops command in `#production` - [ ] Enable on GitLab.com by running chatops command in `#production` (`/chatops run feature set feature_name true`)
- [ ] Cross post chatops slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel - [ ] Cross post chatops Slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel
- [ ] Announce on the issue that the flag has been enabled - [ ] Announce on the issue that the flag has been enabled
- [ ] Remove feature flag and add changelog entry - [ ] Remove feature flag and add changelog entry
- [ ] After the flag removal is deployed, [clean up the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel - [ ] After the flag removal is deployed, [clean up the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel

View file

@ -1,6 +1,6 @@
<!-- The first section "Release notes" is required if you want to have your release post blog MR auto generated. Currently in BETA, details on the **release post item generator** can be found in the handbook: https://about.gitlab.com/handbook/marketing/blog/release-posts/#release-post-item-generator and this video: https://www.youtube.com/watch?v=rfn9ebgTwKg. The next four sections: "Problem to solve", "Intended users", "User experience goal", and "Proposal", are strongly recommended in your first draft, while the rest of the sections can be filled out during the problem validation or breakdown phase. However, keep in mind that providing complete and relevant information early helps our product team validate the problem and start working on a solution. --> <!-- The first section "Release notes" is required if you want to have your release post blog MR auto generated. Currently in BETA, details on the **release post item generator** can be found in the handbook: https://about.gitlab.com/handbook/marketing/blog/release-posts/#release-post-item-generator and this video: https://www.youtube.com/watch?v=rfn9ebgTwKg. The next four sections: "Problem to solve", "Intended users", "User experience goal", and "Proposal", are strongly recommended in your first draft, while the rest of the sections can be filled out during the problem validation or breakdown phase. However, keep in mind that providing complete and relevant information early helps our product team validate the problem and start working on a solution. -->
### Release notes ### Release notes
<!-- What is the problem and solution you're proposing? This content sets the overall vision for the feature and serves as the release notes that will populate in various places, including the [release post blog](https://about.gitlab.com/releases/categories/releases/) and [Gitlab project releases](https://gitlab.com/gitlab-org/gitlab/-/releases). " --> <!-- What is the problem and solution you're proposing? This content sets the overall vision for the feature and serves as the release notes that will populate in various places, including the [release post blog](https://about.gitlab.com/releases/categories/releases/) and [Gitlab project releases](https://gitlab.com/gitlab-org/gitlab/-/releases). " -->
@ -22,19 +22,19 @@ Personas are described at https://about.gitlab.com/handbook/marketing/product-ma
* [Devon (DevOps Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#devon-devops-engineer) * [Devon (DevOps Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#devon-devops-engineer)
* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sidney-systems-administrator) * [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sidney-systems-administrator)
* [Sam (Security Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sam-security-analyst) * [Sam (Security Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sam-security-analyst)
* [Rachel (Release Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#rachel-release-manager) * [Rachel (Release Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#rachel-release-manager)
* [Alex (Security Operations Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#alex-security-operations-engineer) * [Alex (Security Operations Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#alex-security-operations-engineer)
* [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#simone-software-engineer-in-test) * [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#simone-software-engineer-in-test)
* [Allison (Application Ops)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#allison-application-ops) * [Allison (Application Ops)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#allison-application-ops)
* [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#priyanka-platform-engineer) * [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#priyanka-platform-engineer)
* [Dana (Data Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#dana-data-analyst) * [Dana (Data Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#dana-data-analyst)
--> -->
### User experience goal ### User experience goal
<!-- What is the single user experience workflow this problem addresses? <!-- What is the single user experience workflow this problem addresses?
For example, "The user should be able to use the UI/API/.gitlab-ci.yml with GitLab to <perform a specific task>" For example, "The user should be able to use the UI/API/.gitlab-ci.yml with GitLab to <perform a specific task>"
https://about.gitlab.com/handbook/engineering/ux/ux-research-training/user-story-mapping/ --> https://about.gitlab.com/handbook/engineering/ux/ux-research-training/user-story-mapping/ -->
### Proposal ### Proposal
@ -52,7 +52,7 @@ Consider adding checkboxes and expectations of users with certain levels of memb
* [ ] Add expected impact to members with no access (0) * [ ] Add expected impact to members with no access (0)
* [ ] Add expected impact to Guest (10) members * [ ] Add expected impact to Guest (10) members
* [ ] Add expected impact to Reporter (20) members * [ ] Add expected impact to Reporter (20) members
* [ ] Add expected impact to Developer (30) members * [ ] Add expected impact to Developer (30) members
* [ ] Add expected impact to Maintainer (40) members * [ ] Add expected impact to Maintainer (40) members
* [ ] Add expected impact to Owner (50) members --> * [ ] Add expected impact to Owner (50) members -->
@ -78,7 +78,11 @@ See the test engineering planning process and reach out to your counterpart Soft
### What does success look like, and how can we measure that? ### What does success look like, and how can we measure that?
<!-- Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this. --> <!--
Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this.
Create tracking issue using the the Snowplow event tracking template. See https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Snowplow%20event%20tracking.md
-->
### What is the type of buyer? ### What is the type of buyer?

View file

@ -0,0 +1,62 @@
<!--
Implementation issues are used break-up a large piece of work into small, discrete tasks that can
move independently through the build workflow steps. They're typically used to populate a Feature
Epic. Once created, an implementation issue is usually refined in order to populate and review the
implementation plan and weight.
Example workflow: https://about.gitlab.com/handbook/engineering/development/threat-management/planning/diagram.html#plan
-->
## Why are we doing this work
<!--
A brief explanation of the why, not the what or how. Assume the reader doesn't know the
background and won't have time to dig-up information from comment threads.
-->
## Relevant links
<!--
Information that the developer might need to refer to when implementing the issue.
- [Design Issue](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>)
- [Design 1](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png)
- [Design 2](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png)
- [Similar implementation](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/<id>)
-->
## Non-functional requirements
<!--
Add details for required items and delete others.
-->
- [ ] Documentation:
- [ ] Feature flag:
- [ ] Performance:
- [ ] Testing:
## Implementation plan
<!--
Steps and the parts of the code that will need to get updated. The plan can also
call-out responsibilities for other team members or teams.
-->
- [ ] ~frontend Step 1
- [ ] @person Step 1a
- [ ] ~frontend Step 2
<!--
Workflow and other relevant labels
~"group::" ~"Category:" ~"GitLab Ultimate"
-->
/label ~"workflow::refinement"
<!--
Other settings you might want to include when creating the issue.
/milestone %"Next 1-3 releases"
/assign @
/epic &
-->

View file

@ -16,7 +16,7 @@ Please add information here about why you're planning on migrating. Include any
<!-- Please complete as many items in this list as possible. If you're not sure yet, add "TBD" (To be Decided) or "Unknown" --> <!-- Please complete as many items in this list as possible. If you're not sure yet, add "TBD" (To be Decided) or "Unknown" -->
* **Timeline.** - * **Timeline.** -
* **Product.** - GitLab Gold/Ultimate or Commnunity Edition * **Product.** - GitLab Gold/Ultimate or Community Edition
* **Project's License.** What kind of OSI-approved license does your project use? * **Project's License.** What kind of OSI-approved license does your project use?
## Current Tooling and Replacements ## Current Tooling and Replacements
@ -64,4 +64,4 @@ Here is an example of what this list might look like once populated: https://git
------ ------
/label ~"Open Source" ~movingtogitlab /label ~"Open Source" ~movingtogitlab
/cc @nuritzi /cc @nuritzi

View file

@ -31,7 +31,7 @@ Your Security Implementation Issue should have `4` merge requests associated:
## Blog post ## Blog post
Dev: {https://dev.gitlab.org/gitlab/www-gitlab-com/merge_requests/ link}<br/> Security: {https://gitlab.com/gitlab-org/security/www-gitlab-com/merge_requests/ link}<br/>
GitLab.com: {https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ link} GitLab.com: {https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ link}
## Email notification ## Email notification

View file

@ -0,0 +1,42 @@
<!--
* Use this issue template for creating requests to track snowplow events
* Snowplow events can be both Frontend (javascript) or Backend (Ruby)
* Snowplow is currently not used for self-hosted instances of GitLab - Self-hosted still rely on usage ping for product analytics - Snowplow is used for GitLab SaaS
* You do not need to create an issue to track generic front-end events, such as All page views, sessions, link clicks, some button clicks, etc.
* What you should capture are specific events with defined business logic. For example, when a user creates an incident by escalating an existing alert, or when a user creates and pushes up a new Node package to the NPM registry.
* For more details read https://about.gitlab.com/handbook/business-ops/data-team/programs/data-for-product-managers/
-->
<!--
We generally recommend events be tracked using a [structured event](https://docs.snowplowanalytics.com/docs/understanding-tracking-design/out-of-the-box-vs-custom-events-and-entities/#structured-events) which has 5 properties you can use. There may be instances where structured events are not sufficient. You may want to track an event where the property changes frequently or is general something very unique. In those cases, use a [self-describing event](https://docs.snowplowanalytics.com/docs/understanding-tracking-design/out-of-the-box-vs-custom-events-and-entities/#self-describing-events)
-->
## Structured Snowplow events to track
* Category: The page or backend area of the application. Unless infeasible, please use the Rails page attribute by default in the frontend, and namespace + classname on the backend. If you're not sure what it is, work with your engineering manager to figure it out.
* Action: A string that is used to define the user action. The first word should always describe the action or aspect: clicks should be `click`, activations should be `activate`, creations should be `create`, etc. Use underscores to describe what was acted on; for example, activating a form field would be `activate_form_input`. An interface action like clicking on a dropdown would be `click_dropdown`, while a behavior like creating a project record from the backend would be `create_project`
* Label: Optional. The specific element, or object that's being acted on. This is either the label of the element (e.g. a tab labeled 'Create from template' may be `create_from_template`) or a unique identifier if no text is available (e.g. closing the Groups dropdown in the top navbar might be `groups_dropdown_close`), or it could be the name or title attribute of a record being created.
* Property: Optional. Any additional property of the element, or object being acted on.
* Value: Optional, numeric. Describes a numeric value or something directly related to the event. This could be the value of an input (e.g. `10` when clicking `internal` visibility)
| Category | Action | Label | Property | Feature Issue | Additional Information |
| ------ | ------ | ------ | ------ | ------ | ------ |
| cell | cell | cell | cell | cell | cell |
| cell | cell | cell | cell | cell | cell |
<!--
Snowplow event tracking starts with instrumentation and completed after a chart is created in Sisense.
Use this checklist to ensure all steps are completed
-->
## Snowplow event tracking checklist
* [ ] Engineering complete work and deploy changes to GitLab SaaS
* [ ] Verify the new Snowplow events are listed in the [Snowplow Event Exploration](https://app.periscopedata.com/app/gitlab/539181/Snowplow-Event-Exploration---last-30-days) dashboard
* [ ] Create chart(s) to track your event(s) in the relevant dashboard
* [ ] Use the [Chart Snowplow Actions](https://app.periscopedata.com/app/gitlab/snippet/Chart-Snowplow-Actions/5546da87ae2c4a3fbc98415c88b3eedd/edit) SQL snippet to quickly visualize usage. See [example](https://app.periscopedata.com/app/gitlab/737489/Health-Group-Dashboard?widget=9797112&udv=0)
<!-- Label reminders - you should have one of each of the following labels if you can figure out the correct ones -->
/label ~devops:: ~group: ~Category:
/label ~"snowplow tracking events"

View file

@ -1,5 +1,5 @@
## Actionable Insights ## Actionable Insights
Actionable insights always have a follow-up action that needs to take place as a result of the research observation or data, and a clear recommendation or action associated with it. An actionable insight both defines the insight and clearly calls out the next step. These insights are tracked over time. Actionable insights always have a follow-up action that needs to take place as a result of the research observation or data, and a clear recommendation or action associated with it. An actionable insight both defines the insight and clearly calls out the next step. These insights are tracked over time and at the group level.
#### Link #### Link
@ -10,6 +10,10 @@ Actionable insights always have a follow-up action that needs to take place as a
- [ ] Assign this issue to the appropriate Product Manager, Product Designer, or UX Researcher - [ ] Assign this issue to the appropriate Product Manager, Product Designer, or UX Researcher
#### Group label
- [ ] Add the appropriate `Group` (such as `~"group::source code"`) label to the issue. This is done to identify and track actionable insights at the group level.
#### Description #### Description
- [ ] Provide some brief details on the actionable insight and the action to take - [ ] Provide some brief details on the actionable insight and the action to take

View file

@ -15,18 +15,16 @@ Closes
## Moving docs to a new location? ## Moving docs to a new location?
Read the guidelines: Read the guidelines:
https://docs.gitlab.com/ee/development/documentation/index.html#changing-document-location https://docs.gitlab.com/ee/development/documentation/index.html#move-or-rename-a-page
- [ ] Make sure the old link is not removed and has its contents replaced with - [ ] Make sure the old link is not removed and has its contents replaced with
a link to the new location. a link to the new location.
- [ ] Make sure internal links pointing to the document in question are not broken. - [ ] Make sure internal links pointing to the document in question are not broken.
- [ ] Search and replace any links referring to old docs in GitLab Rails app, - [ ] Search and replace any links referring to old docs in GitLab Rails app,
specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories. specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/documentation/index.html#redirections-for-pages-with-disqus-comments) - [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
to the new document if there are any Disqus comments on the old document thread. to the new document if there are any Disqus comments on the old document thread.
- [ ] Update the link in `features.yml` (if applicable) - [ ] Update the link in `features.yml` (if applicable)
- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE - [ ] Assign one of the technical writers for review.
with the changes as well (https://docs.gitlab.com/ce/development/documentation/index.html#cherry-picking-from-ce-to-ee).
- [ ] Ping one of the technical writers for review.
/label ~documentation /label ~documentation

View file

@ -48,10 +48,13 @@ All reviewers can help ensure accuracy, clarity, completeness, and adherence to
- [ ] Technical writer review. If not requested for this MR, must be scheduled post-merge. To request for this MR, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages). - [ ] Technical writer review. If not requested for this MR, must be scheduled post-merge. To request for this MR, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages).
- [ ] Ensure docs metadata are present and up-to-date. - [ ] Ensure docs metadata are present and up-to-date.
- [ ] Ensure ~"Technical Writing" and ~"documentation" are added. - [ ] Ensure ~"Technical Writing" and ~"documentation" are added.
- [ ] Add the corresponding `docs::` scoped label. - [ ] Add the corresponding `docs::` [scoped label](https://gitlab.com/groups/gitlab-org/-/labels?utf8=%E2%9C%93&subscribed=&search=docs%3A%3A).
- [ ] If working on UI text, add the corresponding `UI Text` [scoped label](https://gitlab.com/groups/gitlab-org/-/labels?utf8=%E2%9C%93&subscribed=&search=ui+text).
- [ ] Add ~"tw::doing" when starting work on the MR. - [ ] Add ~"tw::doing" when starting work on the MR.
- [ ] Add ~"tw::finished" if Technical Writing team work on the MR is complete but it remains open. - [ ] Add ~"tw::finished" if Technical Writing team work on the MR is complete but it remains open.
For more information about labels, see [Technical Writing workflows - Labels](https://about.gitlab.com/handbook/engineering/ux/technical-writing/workflow/#labels).
**3. Maintainer** **3. Maintainer**
1. [ ] Review by assigned maintainer, who can always request/require the above reviews. Maintainer's review can occur before or after a technical writer review. 1. [ ] Review by assigned maintainer, who can always request/require the above reviews. Maintainer's review can occur before or after a technical writer review.

View file

@ -22,6 +22,6 @@ Please describe the proposal and add a link to the source (for example, http://w
- [ ] In the relevant Slack channels (e.g. `#development`, `#backend`, `#frontend`) - [ ] In the relevant Slack channels (e.g. `#development`, `#backend`, `#frontend`)
- [ ] (Optional depending on the impact of the change) In the Engineering Week in Review - [ ] (Optional depending on the impact of the change) In the Engineering Week in Review
/label ~"Engineering Productivity" ~"Style decision" ~"development guidelines" ~"static analysis" /label ~"Engineering Productivity" ~"development guidelines" ~"static code analysis"
/cc @gitlab-org/maintainers/rails-backend /cc @gitlab-org/maintainers/rails-backend

View file

@ -21,7 +21,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
- [ ] Assign to a reviewer and maintainer, per our [Code Review process]. - [ ] Assign to a reviewer and maintainer, per our [Code Review process].
- [ ] Ensure it's approved according to our [Approval Guidelines]. - [ ] Ensure it's approved according to our [Approval Guidelines].
- [ ] Ensure it's approved by an AppSec engineer. - [ ] Ensure it's approved by an AppSec engineer.
- If you're unsure who should approve, find the AppSec engineer associated to the issue in the [Canonical repository], or ask #sec-appsec on Slack. - Please see the security release [Code reviews and Approvals](https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#code-reviews-and-approvals) documentation for details on which AppSec team member to ping for approval.
- Trigger the [`package-and-qa` build]. The docker image generated will be used by the AppSec engineer to validate the security vulnerability has been remediated. - Trigger the [`package-and-qa` build]. The docker image generated will be used by the AppSec engineer to validate the security vulnerability has been remediated.
- [ ] For a backport MR targeting a versioned stable branch (`X-Y-stable-ee`) - [ ] For a backport MR targeting a versioned stable branch (`X-Y-stable-ee`)
- [ ] Ensure it's approved by a maintainer. - [ ] Ensure it's approved by a maintainer.

89
.gitpod.yml Normal file
View file

@ -0,0 +1,89 @@
image: registry.gitlab.com/gitlab-org/gitlab-development-kit/gitpod-workspace:gitpod-workspace-image
tasks:
- name: GDK
command: gp sync-await gdk-copied && cd /workspace/gitlab-development-kit && gdk help
- init: |
echo "$(date) Copying GDK" | tee -a /workspace/startup.log
mv $HOME/.rvm-workspace /workspace/.rvm
cp -r $HOME/gitlab-development-kit /workspace/
(
set -e
cd /workspace/gitlab-development-kit
[[ ! -L /workspace/gitlab-development-kit/gitlab ]] && ln -fs /workspace/gitlab /workspace/gitlab-development-kit/gitlab
# make webpack static, prevents that GitLab tries to connect to localhost webpack from browser outside the workspace
echo "webpack:" >> gdk.yml
echo " static: true" >> gdk.yml
# reconfigure GDK
echo "$(date) Reconfiguring GDK" | tee -a /workspace/startup.log
gdk reconfigure
# run DB migrations
echo "$(date) Running DB migrations" | tee -a /workspace/startup.log
make gitlab-db-migrate
cd -
# stop GDK
echo "$(date) Stopping GDK" | tee -a /workspace/startup.log
gdk stop
echo "$(date) GDK stopped" | tee -a /workspace/startup.log
)
command: |
(
set -e
gp sync-done gdk-copied
SECONDS=0
cd /workspace/gitlab-development-kit
# update GDK
if [ "$GITLAB_UPDATE_GDK" == true ]; then
echo "$(date) Updating GDK" | tee -a /workspace/startup.log
gdk update
fi
# start GDK
echo "$(date) Starting GDK" | tee -a /workspace/startup.log
export RAILS_HOSTS=$(gp url 3000 | sed -e 's+^http[s]*://++')
gdk start
# Run DB migrations
if [ "$GITLAB_RUN_DB_MIGRATIONS" == true ]; then
make gitlab-db-migrate
fi
# Fix DB key
if [ "$GITLAB_FIX_DB_KEY" = true ]; then
echo "$(date) Fixing DB key" | tee -a /workspace/startup.log
cd gitlab
# see https://gitlab.com/gitlab-org/gitlab-foss/-/issues/56403#note_132515069
printf 'ApplicationSetting.last.update_column(:runners_registration_token_encrypted, nil)\nexit\n' | bundle exec rails c
cd -
fi
# Waiting for GitLab ...
gp await-port 3000
printf "Waiting for GitLab at $(gp url 3000) ..."
until $(curl -sNL $(gp url 3000) | grep -q "GitLab"); do printf '.'; sleep 5; done && echo ""
# Give Gitpod a few more seconds to set up everything ...
sleep 5
printf "$(date) GitLab is up (took ~%.1f minutes)\n" "$((10*$SECONDS/60))e-1" | tee -a /workspace/startup.log
gp preview $(gp url 3000) || true
)
ports:
- port: 3000 # rails-web
onOpen: ignore
- port: 3010 # gitlab-pages
onOpen: ignore
- port: 3808 # webpack
onOpen: ignore
- port: 5000 # auto_devops
onOpen: ignore
- port: 5778 # jaeger
onOpen: ignore
- port: 9000 # object_store / minio
onOpen: ignore
vscode:
extensions:
- rebornix.ruby@0.27.0:QyGBeRyslOfdRgOPRGm6PQ==
- wingrunr21.vscode-ruby@0.27.0:beIqQUhLRuJ5Vao4B2Lyng==
- karunamurti.haml@1.1.0:twCwOYt3/Ttfb3+iwblPDA==
- octref.vetur@0.25.0:UofirBhedyhdx/jCnPeJDg==
- dbaeumer.vscode-eslint@2.1.3:1NRvj3UKNTNwmYjptmUmIw==
- GitLab.gitlab-workflow@3.3.0:50q1byIi4M01G9qrTCCAYQ==

View file

@ -7,377 +7,374 @@
# versions of Haml-Lint, may require this file to be generated again. # versions of Haml-Lint, may require this file to be generated again.
linters: linters:
# Offense count: 1552 # Offense count: 1552
NoPlainNodes: NoPlainNodes:
enabled: true enabled: true
exclude: exclude:
- "app/views/admin/abuse_reports/_abuse_report.html.haml" - 'app/views/admin/abuse_reports/_abuse_report.html.haml'
- "app/views/admin/abuse_reports/index.html.haml" - 'app/views/admin/abuse_reports/index.html.haml'
- "app/views/admin/appearances/_form.html.haml" - 'app/views/admin/appearances/_form.html.haml'
- "app/views/admin/application_settings/_abuse.html.haml" - 'app/views/admin/application_settings/_abuse.html.haml'
- "app/views/admin/application_settings/_diff_limits.html.haml" - 'app/views/admin/application_settings/_diff_limits.html.haml'
- "app/views/admin/application_settings/_gitaly.html.haml" - 'app/views/admin/application_settings/_gitaly.html.haml'
- "app/views/admin/application_settings/_ip_limits.html.haml" - 'app/views/admin/application_settings/_ip_limits.html.haml'
- "app/views/admin/application_settings/_performance.html.haml" - 'app/views/admin/application_settings/_performance.html.haml'
- "app/views/admin/application_settings/_plantuml.html.haml" - 'app/views/admin/application_settings/_plantuml.html.haml'
- "app/views/admin/application_settings/_prometheus.html.haml" - 'app/views/admin/application_settings/_prometheus.html.haml'
- "app/views/admin/application_settings/_realtime.html.haml" - 'app/views/admin/application_settings/_realtime.html.haml'
- "app/views/admin/application_settings/_repository_check.html.haml" - 'app/views/admin/application_settings/_repository_check.html.haml'
- "app/views/admin/application_settings/_signin.html.haml" - 'app/views/admin/application_settings/_signin.html.haml'
- "app/views/admin/application_settings/_signup.html.haml" - 'app/views/admin/application_settings/_signup.html.haml'
- "app/views/admin/application_settings/_spam.html.haml" - 'app/views/admin/application_settings/_spam.html.haml'
- "app/views/admin/application_settings/_terminal.html.haml" - 'app/views/admin/application_settings/_terminal.html.haml'
- "app/views/admin/application_settings/_usage.html.haml" - 'app/views/admin/application_settings/_usage.html.haml'
- "app/views/admin/application_settings/_visibility_and_access.html.haml" - 'app/views/admin/application_settings/_visibility_and_access.html.haml'
- "app/views/admin/applications/_delete_form.html.haml" - 'app/views/admin/applications/_delete_form.html.haml'
- "app/views/admin/applications/_form.html.haml" - 'app/views/admin/applications/_form.html.haml'
- "app/views/admin/applications/edit.html.haml" - 'app/views/admin/applications/edit.html.haml'
- "app/views/admin/applications/index.html.haml" - 'app/views/admin/applications/index.html.haml'
- "app/views/admin/applications/new.html.haml" - 'app/views/admin/applications/new.html.haml'
- "app/views/admin/applications/show.html.haml" - 'app/views/admin/applications/show.html.haml'
- "app/views/admin/background_jobs/show.html.haml" - 'app/views/admin/background_jobs/show.html.haml'
- "app/views/admin/broadcast_messages/index.html.haml" - 'app/views/admin/broadcast_messages/index.html.haml'
- "app/views/admin/dashboard/index.html.haml" - 'app/views/admin/dashboard/index.html.haml'
- "app/views/admin/deploy_keys/new.html.haml" - 'app/views/admin/deploy_keys/new.html.haml'
- "app/views/admin/health_check/show.html.haml" - 'app/views/admin/health_check/show.html.haml'
- "app/views/admin/hook_logs/_index.html.haml" - 'app/views/admin/hook_logs/_index.html.haml'
- "app/views/admin/hook_logs/show.html.haml" - 'app/views/admin/hook_logs/show.html.haml'
- "app/views/admin/hooks/_form.html.haml" - 'app/views/admin/hooks/_form.html.haml'
- "app/views/admin/hooks/edit.html.haml" - 'app/views/admin/hooks/edit.html.haml'
- "app/views/admin/logs/show.html.haml" - 'app/views/admin/logs/show.html.haml'
- "app/views/admin/projects/_projects.html.haml" - 'app/views/admin/projects/_projects.html.haml'
- "app/views/admin/requests_profiles/index.html.haml" - 'app/views/admin/requests_profiles/index.html.haml'
- "app/views/admin/runners/_runner.html.haml" - 'app/views/admin/runners/_runner.html.haml'
- "app/views/admin/runners/index.html.haml" - 'app/views/admin/runners/index.html.haml'
- "app/views/admin/runners/show.html.haml" - 'app/views/admin/runners/show.html.haml'
- "app/views/admin/services/_form.html.haml" - 'app/views/admin/services/_form.html.haml'
- "app/views/admin/services/index.html.haml" - 'app/views/admin/services/index.html.haml'
- "app/views/admin/spam_logs/_spam_log.html.haml" - 'app/views/admin/spam_logs/_spam_log.html.haml'
- "app/views/admin/spam_logs/index.html.haml" - 'app/views/admin/spam_logs/index.html.haml'
- "app/views/admin/system_info/show.html.haml" - 'app/views/admin/system_info/show.html.haml'
- "app/views/admin/users/_form.html.haml" - 'app/views/admin/users/_form.html.haml'
- "app/views/admin/users/_head.html.haml" - 'app/views/admin/users/_head.html.haml'
- "app/views/admin/users/_profile.html.haml" - 'app/views/admin/users/_profile.html.haml'
- "app/views/admin/users/_projects.html.haml" - 'app/views/admin/users/_projects.html.haml'
- "app/views/admin/users/new.html.haml" - 'app/views/admin/users/new.html.haml'
- "app/views/admin/users/projects.html.haml" - 'app/views/admin/users/projects.html.haml'
- "app/views/admin/users/show.html.haml" - 'app/views/admin/users/show.html.haml'
- 'app/views/authentication/_authenticate.html.haml' - 'app/views/authentication/_authenticate.html.haml'
- 'app/views/authentication/_register.html.haml' - 'app/views/authentication/_register.html.haml'
- "app/views/clusters/clusters/_cluster.html.haml" - 'app/views/clusters/clusters/_cluster.html.haml'
- "app/views/clusters/clusters/new.html.haml" - 'app/views/clusters/clusters/new.html.haml'
- "app/views/dashboard/milestones/index.html.haml" - 'app/views/dashboard/milestones/index.html.haml'
- "app/views/dashboard/projects/_blank_state_admin_welcome.html.haml" - 'app/views/dashboard/projects/_blank_state_admin_welcome.html.haml'
- "app/views/dashboard/projects/_blank_state_welcome.html.haml" - 'app/views/dashboard/projects/_blank_state_welcome.html.haml'
- "app/views/dashboard/todos/_todo.html.haml" - 'app/views/dashboard/todos/_todo.html.haml'
- "app/views/dashboard/todos/index.html.haml" - 'app/views/dashboard/todos/index.html.haml'
- "app/views/devise/confirmations/almost_there.haml" - 'app/views/devise/confirmations/almost_there.haml'
- "app/views/devise/mailer/_confirmation_instructions_account.html.haml" - 'app/views/devise/mailer/_confirmation_instructions_account.html.haml'
- "app/views/devise/mailer/_confirmation_instructions_secondary.html.haml" - 'app/views/devise/mailer/_confirmation_instructions_secondary.html.haml'
- "app/views/devise/mailer/email_changed.html.haml" - 'app/views/devise/mailer/email_changed.html.haml'
- "app/views/devise/mailer/password_change.html.haml" - 'app/views/devise/mailer/password_change.html.haml'
- "app/views/devise/mailer/reset_password_instructions.html.haml" - 'app/views/devise/mailer/reset_password_instructions.html.haml'
- "app/views/devise/mailer/unlock_instructions.html.haml" - 'app/views/devise/mailer/unlock_instructions.html.haml'
- "app/views/devise/passwords/edit.html.haml" - 'app/views/devise/passwords/edit.html.haml'
- "app/views/devise/sessions/_new_base.html.haml" - 'app/views/devise/sessions/_new_base.html.haml'
- "app/views/devise/sessions/_new_crowd.html.haml" - 'app/views/devise/sessions/_new_crowd.html.haml'
- "app/views/devise/sessions/_new_ldap.html.haml" - 'app/views/devise/sessions/_new_ldap.html.haml'
- "app/views/devise/sessions/new.html.haml" - 'app/views/devise/sessions/new.html.haml'
- "app/views/devise/sessions/two_factor.html.haml" - 'app/views/devise/sessions/two_factor.html.haml'
- "app/views/devise/shared/_omniauth_box.html.haml" - 'app/views/devise/shared/_omniauth_box.html.haml'
- "app/views/devise/shared/_sign_in_link.html.haml" - 'app/views/devise/shared/_sign_in_link.html.haml'
- "app/views/devise/shared/_tabs_normal.html.haml" - 'app/views/devise/shared/_tabs_normal.html.haml'
- "app/views/discussions/_discussion.html.haml" - 'app/views/discussions/_discussion.html.haml'
- "app/views/discussions/_headline.html.haml" - 'app/views/discussions/_headline.html.haml'
- "app/views/discussions/_notes.html.haml" - 'app/views/discussions/_notes.html.haml'
- "app/views/doorkeeper/applications/_delete_form.html.haml" - 'app/views/doorkeeper/applications/_delete_form.html.haml'
- "app/views/doorkeeper/authorized_applications/_delete_form.html.haml" - 'app/views/doorkeeper/authorized_applications/_delete_form.html.haml'
- "app/views/errors/encoding.html.haml" - 'app/views/errors/encoding.html.haml'
- "app/views/errors/git_not_found.html.haml" - 'app/views/errors/git_not_found.html.haml'
- "app/views/errors/omniauth_error.html.haml" - 'app/views/errors/omniauth_error.html.haml'
- "app/views/errors/precondition_failed.html.haml" - 'app/views/errors/precondition_failed.html.haml'
- "app/views/events/_event_push.atom.haml" - 'app/views/events/_event_push.atom.haml'
- "app/views/events/event/_push.html.haml" - 'app/views/events/event/_push.html.haml'
- "app/views/groups/_create_chat_team.html.haml" - 'app/views/groups/_create_chat_team.html.haml'
- "app/views/groups/_group_admin_settings.html.haml" - 'app/views/groups/_group_admin_settings.html.haml'
- "app/views/groups/labels/edit.html.haml" - 'app/views/groups/labels/edit.html.haml'
- "app/views/groups/labels/new.html.haml" - 'app/views/groups/labels/new.html.haml'
- "app/views/groups/milestones/edit.html.haml" - 'app/views/groups/milestones/edit.html.haml'
- "app/views/groups/milestones/index.html.haml" - 'app/views/groups/milestones/index.html.haml'
- "app/views/groups/milestones/new.html.haml" - 'app/views/groups/milestones/new.html.haml'
- "app/views/groups/projects.html.haml" - 'app/views/groups/projects.html.haml'
- "app/views/groups/runners/edit.html.haml" - 'app/views/groups/runners/edit.html.haml'
- "app/views/groups/settings/_advanced.html.haml" - 'app/views/groups/settings/_advanced.html.haml'
- "app/views/groups/settings/_lfs.html.haml" - 'app/views/groups/settings/_lfs.html.haml'
- "app/views/help/_shortcuts.html.haml" - 'app/views/help/_shortcuts.html.haml'
- "app/views/help/index.html.haml" - 'app/views/help/index.html.haml'
- "app/views/help/instance_configuration.html.haml" - 'app/views/help/instance_configuration.html.haml'
- "app/views/help/instance_configuration/_gitlab_ci.html.haml" - 'app/views/help/instance_configuration/_gitlab_ci.html.haml'
- "app/views/help/instance_configuration/_gitlab_pages.html.haml" - 'app/views/help/instance_configuration/_gitlab_pages.html.haml'
- "app/views/import/bitbucket/status.html.haml" - 'app/views/import/bitbucket/status.html.haml'
- "app/views/import/bitbucket_server/status.html.haml" - 'app/views/import/bitbucket_server/status.html.haml'
- "app/views/invites/show.html.haml" - 'app/views/invites/show.html.haml'
- "app/views/jira_connect/subscriptions/index.html.haml" - 'app/views/jira_connect/subscriptions/index.html.haml'
- "app/views/layouts/_mailer.html.haml" - 'app/views/layouts/_mailer.html.haml'
- "app/views/layouts/experiment_mailer.html.haml" - 'app/views/layouts/experiment_mailer.html.haml'
- "app/views/layouts/header/_default.html.haml" - 'app/views/layouts/header/_default.html.haml'
- "app/views/layouts/header/_new_dropdown.haml" - 'app/views/layouts/header/_new_dropdown.haml'
- "app/views/layouts/jira_connect.html.haml" - 'app/views/layouts/jira_connect.html.haml'
- "app/views/layouts/notify.html.haml" - 'app/views/layouts/notify.html.haml'
- "app/views/notify/_failed_builds.html.haml" - 'app/views/notify/_failed_builds.html.haml'
- "app/views/notify/_reassigned_issuable_email.html.haml" - 'app/views/notify/_reassigned_issuable_email.html.haml'
- "app/views/notify/_removal_notification.html.haml" - 'app/views/notify/_removal_notification.html.haml'
- "app/views/notify/_successful_pipeline.html.haml" - 'app/views/notify/_successful_pipeline.html.haml'
- "app/views/notify/autodevops_disabled_email.html.haml" - 'app/views/notify/autodevops_disabled_email.html.haml'
- "app/views/notify/changed_milestone_email.html.haml" - 'app/views/notify/changed_milestone_email.html.haml'
- "app/views/notify/import_issues_csv_email.html.haml" - 'app/views/notify/import_issues_csv_email.html.haml'
- "app/views/notify/issue_moved_email.html.haml" - 'app/views/notify/issue_moved_email.html.haml'
- "app/views/notify/member_access_denied_email.html.haml" - 'app/views/notify/member_access_denied_email.html.haml'
- "app/views/notify/member_invite_accepted_email.html.haml" - 'app/views/notify/member_invite_accepted_email.html.haml'
- "app/views/notify/member_invited_email.html.haml" - 'app/views/notify/member_invited_email.html.haml'
- "app/views/notify/new_gpg_key_email.html.haml" - 'app/views/notify/new_gpg_key_email.html.haml'
- "app/views/notify/new_mention_in_issue_email.html.haml" - 'app/views/notify/new_mention_in_issue_email.html.haml'
- "app/views/notify/new_ssh_key_email.html.haml" - 'app/views/notify/new_ssh_key_email.html.haml'
- "app/views/notify/new_user_email.html.haml" - 'app/views/notify/new_user_email.html.haml'
- "app/views/notify/pages_domain_disabled_email.html.haml" - 'app/views/notify/pages_domain_disabled_email.html.haml'
- "app/views/notify/pages_domain_enabled_email.html.haml" - 'app/views/notify/pages_domain_enabled_email.html.haml'
- "app/views/notify/pages_domain_verification_failed_email.html.haml" - 'app/views/notify/pages_domain_verification_failed_email.html.haml'
- "app/views/notify/pages_domain_verification_succeeded_email.html.haml" - 'app/views/notify/pages_domain_verification_succeeded_email.html.haml'
- "app/views/notify/pipeline_failed_email.html.haml" - 'app/views/notify/pipeline_failed_email.html.haml'
- "app/views/notify/project_was_exported_email.html.haml" - 'app/views/notify/project_was_exported_email.html.haml'
- "app/views/notify/project_was_moved_email.html.haml" - 'app/views/notify/project_was_moved_email.html.haml'
- "app/views/notify/project_was_not_exported_email.html.haml" - 'app/views/notify/project_was_not_exported_email.html.haml'
- "app/views/notify/push_to_merge_request_email.html.haml" - 'app/views/notify/push_to_merge_request_email.html.haml'
- "app/views/notify/remote_mirror_update_failed_email.html.haml" - 'app/views/notify/remote_mirror_update_failed_email.html.haml'
- "app/views/notify/removed_milestone_issue_email.html.haml" - 'app/views/notify/removed_milestone_issue_email.html.haml'
- "app/views/notify/removed_milestone_merge_request_email.html.haml" - 'app/views/notify/removed_milestone_merge_request_email.html.haml'
- "app/views/notify/repository_push_email.html.haml" - 'app/views/notify/repository_push_email.html.haml'
- "app/views/profiles/chat_names/_chat_name.html.haml" - 'app/views/profiles/chat_names/_chat_name.html.haml'
- "app/views/profiles/chat_names/index.html.haml" - 'app/views/profiles/chat_names/index.html.haml'
- "app/views/profiles/chat_names/new.html.haml" - 'app/views/profiles/chat_names/new.html.haml'
- "app/views/projects/_bitbucket_import_modal.html.haml" - 'app/views/projects/_bitbucket_import_modal.html.haml'
- "app/views/projects/_customize_workflow.html.haml" - 'app/views/projects/_customize_workflow.html.haml'
- "app/views/projects/_deletion_failed.html.haml" - 'app/views/projects/_deletion_failed.html.haml'
- "app/views/projects/_fork_suggestion.html.haml" - 'app/views/projects/_fork_suggestion.html.haml'
- "app/views/projects/_gitlab_import_modal.html.haml" - 'app/views/projects/_gitlab_import_modal.html.haml'
- "app/views/projects/_home_panel.html.haml" - 'app/views/projects/_home_panel.html.haml'
- "app/views/projects/_import_project_pane.html.haml" - 'app/views/projects/_import_project_pane.html.haml'
- "app/views/projects/_issuable_by_email.html.haml" - 'app/views/projects/_issuable_by_email.html.haml'
- "app/views/projects/_readme.html.haml" - 'app/views/projects/_readme.html.haml'
- "app/views/projects/artifacts/_artifact.html.haml" - 'app/views/projects/artifacts/_artifact.html.haml'
- "app/views/projects/artifacts/_tree_file.html.haml" - 'app/views/projects/artifacts/_tree_file.html.haml'
- "app/views/projects/artifacts/browse.html.haml" - 'app/views/projects/artifacts/browse.html.haml'
- "app/views/projects/blame/_age_map_legend.html.haml" - 'app/views/projects/blame/_age_map_legend.html.haml'
- "app/views/projects/blame/show.html.haml" - 'app/views/projects/blame/show.html.haml'
- "app/views/projects/blob/_editor.html.haml" - 'app/views/projects/blob/_editor.html.haml'
- "app/views/projects/blob/_header_content.html.haml" - 'app/views/projects/blob/_header_content.html.haml'
- "app/views/projects/blob/_remove.html.haml" - 'app/views/projects/blob/_remove.html.haml'
- "app/views/projects/blob/_render_error.html.haml" - 'app/views/projects/blob/_render_error.html.haml'
- "app/views/projects/blob/edit.html.haml" - 'app/views/projects/blob/edit.html.haml'
- "app/views/projects/blob/new.html.haml" - 'app/views/projects/blob/new.html.haml'
- "app/views/projects/blob/preview.html.haml" - 'app/views/projects/blob/preview.html.haml'
- "app/views/projects/blob/viewers/_empty.html.haml" - 'app/views/projects/blob/viewers/_empty.html.haml'
- "app/views/projects/blob/viewers/_stl.html.haml" - 'app/views/projects/blob/viewers/_stl.html.haml'
- "app/views/projects/branches/_branch.html.haml" - 'app/views/projects/branches/_branch.html.haml'
- "app/views/projects/branches/_delete_protected_modal.html.haml" - 'app/views/projects/branches/_delete_protected_modal.html.haml'
- "app/views/projects/branches/new.html.haml" - 'app/views/projects/branches/new.html.haml'
- "app/views/projects/ci/builds/_build.html.haml" - 'app/views/projects/ci/builds/_build.html.haml'
- "app/views/projects/ci/lints/_create.html.haml" - 'app/views/projects/ci/lints/_create.html.haml'
- "app/views/projects/compare/_form.html.haml" - 'app/views/projects/compare/_form.html.haml'
- "app/views/projects/compare/index.html.haml" - 'app/views/projects/compare/index.html.haml'
- "app/views/projects/cycle_analytics/_empty_stage.html.haml" - 'app/views/projects/cycle_analytics/_empty_stage.html.haml'
- "app/views/projects/cycle_analytics/_no_access.html.haml" - 'app/views/projects/cycle_analytics/_no_access.html.haml'
- "app/views/projects/cycle_analytics/_overview.html.haml" - 'app/views/projects/cycle_analytics/_overview.html.haml'
- "app/views/projects/cycle_analytics/show.html.haml" - 'app/views/projects/cycle_analytics/show.html.haml'
- "app/views/projects/deploy_keys/_form.html.haml" - 'app/views/projects/deploy_keys/_form.html.haml'
- "app/views/projects/deploy_keys/_index.html.haml" - 'app/views/projects/deploy_keys/_index.html.haml'
- "app/views/projects/deploy_keys/edit.html.haml" - 'app/views/projects/deploy_keys/edit.html.haml'
- "app/views/projects/deployments/_deployment.html.haml" - 'app/views/projects/deployments/_deployment.html.haml'
- "app/views/projects/diffs/_file_header.html.haml" - 'app/views/projects/diffs/_file_header.html.haml'
- "app/views/projects/diffs/_replaced_image_diff.html.haml" - 'app/views/projects/diffs/_replaced_image_diff.html.haml'
- "app/views/projects/diffs/_stats.html.haml" - 'app/views/projects/diffs/_stats.html.haml'
- "app/views/projects/empty.html.haml" - 'app/views/projects/empty.html.haml'
- "app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml" - 'app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml'
- "app/views/projects/hook_logs/_index.html.haml" - 'app/views/projects/hook_logs/_index.html.haml'
- "app/views/projects/hook_logs/show.html.haml" - 'app/views/projects/hook_logs/show.html.haml'
- "app/views/projects/hooks/edit.html.haml" - 'app/views/projects/hooks/edit.html.haml'
- "app/views/projects/imports/new.html.haml" - 'app/views/projects/imports/new.html.haml'
- "app/views/projects/imports/show.html.haml" - 'app/views/projects/imports/show.html.haml'
- "app/views/projects/issues/_new_branch.html.haml" - 'app/views/projects/issues/_new_branch.html.haml'
- "app/views/projects/issues/import_csv/_modal.html.haml" - 'app/views/projects/issues/import_csv/_modal.html.haml'
- "app/views/projects/issues/show.html.haml" - 'app/views/projects/issues/show.html.haml'
- "app/views/projects/jobs/_header.html.haml" - 'app/views/projects/jobs/_header.html.haml'
- "app/views/projects/jobs/_table.html.haml" - 'app/views/projects/jobs/_table.html.haml'
- "app/views/projects/jobs/index.html.haml" - 'app/views/projects/jobs/index.html.haml'
- "app/views/projects/labels/edit.html.haml" - 'app/views/projects/labels/edit.html.haml'
- "app/views/projects/labels/new.html.haml" - 'app/views/projects/labels/new.html.haml'
- "app/views/projects/mattermosts/_no_teams.html.haml" - 'app/views/projects/mattermosts/_no_teams.html.haml'
- "app/views/projects/mattermosts/_team_selection.html.haml" - 'app/views/projects/mattermosts/_team_selection.html.haml'
- "app/views/projects/mattermosts/new.html.haml" - 'app/views/projects/mattermosts/new.html.haml'
- "app/views/projects/merge_requests/_commits.html.haml" - 'app/views/projects/merge_requests/_commits.html.haml'
- "app/views/projects/merge_requests/_discussion.html.haml" - 'app/views/projects/merge_requests/_how_to_merge.html.haml'
- "app/views/projects/merge_requests/_how_to_merge.html.haml" - 'app/views/projects/merge_requests/_mr_title.html.haml'
- "app/views/projects/merge_requests/_mr_title.html.haml" - 'app/views/projects/merge_requests/conflicts/_commit_stats.html.haml'
- "app/views/projects/merge_requests/conflicts/_commit_stats.html.haml" - 'app/views/projects/merge_requests/conflicts/_file_actions.html.haml'
- "app/views/projects/merge_requests/conflicts/_file_actions.html.haml" - 'app/views/projects/merge_requests/conflicts/_submit_form.html.haml'
- "app/views/projects/merge_requests/conflicts/_submit_form.html.haml" - 'app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml'
- "app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml" - 'app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml'
- "app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml" - 'app/views/projects/merge_requests/conflicts/show.html.haml'
- "app/views/projects/merge_requests/conflicts/show.html.haml" - 'app/views/projects/merge_requests/creations/_diffs.html.haml'
- "app/views/projects/merge_requests/creations/_diffs.html.haml" - 'app/views/projects/merge_requests/creations/_new_compare.html.haml'
- "app/views/projects/merge_requests/creations/_new_compare.html.haml" - 'app/views/projects/merge_requests/creations/_new_submit.html.haml'
- "app/views/projects/merge_requests/creations/_new_submit.html.haml" - 'app/views/projects/merge_requests/diffs/_different_base.html.haml'
- "app/views/projects/merge_requests/diffs/_different_base.html.haml" - 'app/views/projects/merge_requests/diffs/_diffs.html.haml'
- "app/views/projects/merge_requests/diffs/_diffs.html.haml" - 'app/views/projects/merge_requests/diffs/_version_controls.html.haml'
- "app/views/projects/merge_requests/diffs/_version_controls.html.haml" - 'app/views/projects/merge_requests/invalid.html.haml'
- "app/views/projects/merge_requests/invalid.html.haml" - 'app/views/projects/merge_requests/widget/open/_error.html.haml'
- "app/views/projects/merge_requests/widget/open/_error.html.haml" - 'app/views/projects/mirrors/_regenerate_public_ssh_key_confirm_modal.html.haml'
- "app/views/projects/mirrors/_regenerate_public_ssh_key_confirm_modal.html.haml" - 'app/views/projects/mirrors/_ssh_host_keys.html.haml'
- "app/views/projects/mirrors/_ssh_host_keys.html.haml" - 'app/views/projects/no_repo.html.haml'
- "app/views/projects/no_repo.html.haml" - 'app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml'
- "app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml" - 'app/views/projects/pipelines/_info.html.haml'
- "app/views/projects/pipelines/_info.html.haml" - 'app/views/projects/protected_branches/shared/_dropdown.html.haml'
- "app/views/projects/protected_branches/shared/_dropdown.html.haml" - 'app/views/projects/protected_branches/shared/_index.html.haml'
- "app/views/projects/protected_branches/shared/_index.html.haml" - 'app/views/projects/protected_branches/shared/_matching_branch.html.haml'
- "app/views/projects/protected_branches/shared/_matching_branch.html.haml" - 'app/views/projects/protected_branches/shared/_protected_branch.html.haml'
- "app/views/projects/protected_branches/shared/_protected_branch.html.haml" - 'app/views/projects/protected_branches/show.html.haml'
- "app/views/projects/protected_branches/show.html.haml" - 'app/views/projects/protected_tags/shared/_create_protected_tag.html.haml'
- "app/views/projects/protected_tags/shared/_create_protected_tag.html.haml" - 'app/views/projects/protected_tags/shared/_dropdown.html.haml'
- "app/views/projects/protected_tags/shared/_dropdown.html.haml" - 'app/views/projects/protected_tags/shared/_index.html.haml'
- "app/views/projects/protected_tags/shared/_index.html.haml" - 'app/views/projects/protected_tags/shared/_matching_tag.html.haml'
- "app/views/projects/protected_tags/shared/_matching_tag.html.haml" - 'app/views/projects/protected_tags/shared/_protected_tag.html.haml'
- "app/views/projects/protected_tags/shared/_protected_tag.html.haml" - 'app/views/projects/protected_tags/shared/_tags_list.html.haml'
- "app/views/projects/protected_tags/shared/_tags_list.html.haml" - 'app/views/projects/protected_tags/show.html.haml'
- "app/views/projects/protected_tags/show.html.haml" - 'app/views/projects/registry/repositories/_tag.html.haml'
- "app/views/projects/registry/repositories/_tag.html.haml" - 'app/views/projects/repositories/_feed.html.haml'
- "app/views/projects/repositories/_feed.html.haml" - 'app/views/projects/runners/_shared_runners.html.haml'
- "app/views/projects/runners/_shared_runners.html.haml" - 'app/views/projects/runners/edit.html.haml'
- "app/views/projects/runners/edit.html.haml" - 'app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml'
- "app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml" - 'app/views/projects/services/mattermost_slash_commands/_help.html.haml'
- "app/views/projects/services/mattermost_slash_commands/_help.html.haml" - 'app/views/projects/services/prometheus/_metrics.html.haml'
- "app/views/projects/services/prometheus/_metrics.html.haml" - 'app/views/projects/services/slack_slash_commands/_help.html.haml'
- "app/views/projects/services/slack_slash_commands/_help.html.haml" - 'app/views/projects/settings/ci_cd/_badge.html.haml'
- "app/views/projects/settings/ci_cd/_badge.html.haml" - 'app/views/projects/settings/ci_cd/_form.html.haml'
- "app/views/projects/settings/ci_cd/_form.html.haml" - 'app/views/projects/tags/index.html.haml'
- "app/views/projects/tags/index.html.haml" - 'app/views/projects/tags/releases/edit.html.haml'
- "app/views/projects/tags/releases/edit.html.haml" - 'app/views/projects/tree/_tree_row.html.haml'
- "app/views/projects/tree/_tree_row.html.haml" - 'app/views/projects/tree/_truncated_notice_tree_row.html.haml'
- "app/views/projects/tree/_truncated_notice_tree_row.html.haml" - 'app/views/projects/triggers/_form.html.haml'
- "app/views/projects/triggers/_form.html.haml" - 'app/views/projects/triggers/_index.html.haml'
- "app/views/projects/triggers/_index.html.haml" - 'app/views/projects/triggers/_trigger.html.haml'
- "app/views/projects/triggers/_trigger.html.haml" - 'app/views/projects/triggers/edit.html.haml'
- "app/views/projects/triggers/edit.html.haml" - 'app/views/search/results/_issue.html.haml'
- "app/views/search/results/_issue.html.haml" - 'app/views/search/results/_note.html.haml'
- "app/views/search/results/_note.html.haml" - 'app/views/search/results/_snippet_blob.html.haml'
- "app/views/search/results/_snippet_blob.html.haml" - 'app/views/search/results/_snippet_title.html.haml'
- "app/views/search/results/_snippet_title.html.haml" - 'app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml'
- "app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml" - 'app/views/shared/_commit_message_container.html.haml'
- "app/views/shared/_commit_message_container.html.haml" - 'app/views/shared/_delete_label_modal.html.haml'
- "app/views/shared/_delete_label_modal.html.haml" - 'app/views/shared/_group_form.html.haml'
- "app/views/shared/_group_form.html.haml" - 'app/views/shared/_group_tips.html.haml'
- "app/views/shared/_group_tips.html.haml" - 'app/views/shared/_md_preview.html.haml'
- "app/views/shared/_md_preview.html.haml" - 'app/views/shared/_milestone_expired.html.haml'
- "app/views/shared/_milestone_expired.html.haml" - 'app/views/shared/_no_password.html.haml'
- "app/views/shared/_no_password.html.haml" - 'app/views/shared/_ping_consent.html.haml'
- "app/views/shared/_ping_consent.html.haml" - 'app/views/shared/_project_limit.html.haml'
- "app/views/shared/_project_limit.html.haml" - 'app/views/shared/boards/components/_sidebar.html.haml'
- "app/views/shared/boards/components/_sidebar.html.haml" - 'app/views/shared/boards/components/sidebar/_due_date.html.haml'
- "app/views/shared/boards/components/sidebar/_due_date.html.haml" - 'app/views/shared/boards/components/sidebar/_labels.html.haml'
- "app/views/shared/boards/components/sidebar/_labels.html.haml" - 'app/views/shared/boards/components/sidebar/_milestone.html.haml'
- "app/views/shared/boards/components/sidebar/_milestone.html.haml" - 'app/views/shared/hook_logs/_content.html.haml'
- "app/views/shared/hook_logs/_content.html.haml" - 'app/views/shared/issuable/_assignees.html.haml'
- "app/views/shared/issuable/_assignees.html.haml" - 'app/views/shared/issuable/_board_create_list_dropdown.html.haml'
- "app/views/shared/issuable/_board_create_list_dropdown.html.haml" - 'app/views/shared/issuable/_close_reopen_report_toggle.html.haml'
- "app/views/shared/issuable/_close_reopen_report_toggle.html.haml" - 'app/views/shared/issuable/_form.html.haml'
- "app/views/shared/issuable/_form.html.haml" - 'app/views/shared/issuable/_search_bar.html.haml'
- "app/views/shared/issuable/_search_bar.html.haml" - 'app/views/shared/issuable/_sidebar.html.haml'
- "app/views/shared/issuable/_sidebar.html.haml" - 'app/views/shared/issuable/form/_default_templates.html.haml'
- "app/views/shared/issuable/form/_default_templates.html.haml" - 'app/views/shared/issuable/form/_template_selector.html.haml'
- "app/views/shared/issuable/form/_template_selector.html.haml" - 'app/views/shared/issuable/form/_title.html.haml'
- "app/views/shared/issuable/form/_title.html.haml" - 'app/views/shared/labels/_form.html.haml'
- "app/views/shared/labels/_form.html.haml" - 'app/views/shared/members/_member.html.haml'
- "app/views/shared/members/_member.html.haml" - 'app/views/shared/milestones/_form_dates.html.haml'
- "app/views/shared/milestones/_form_dates.html.haml" - 'app/views/shared/milestones/_issuable.html.haml'
- "app/views/shared/milestones/_issuable.html.haml" - 'app/views/shared/milestones/_milestone.html.haml'
- "app/views/shared/milestones/_milestone.html.haml" - 'app/views/shared/milestones/_sidebar.html.haml'
- "app/views/shared/milestones/_sidebar.html.haml" - 'app/views/shared/milestones/_top.html.haml'
- "app/views/shared/milestones/_top.html.haml" - 'app/views/shared/notes/_hints.html.haml'
- "app/views/shared/notes/_hints.html.haml" - 'app/views/shared/notifications/_button.html.haml'
- "app/views/shared/notifications/_button.html.haml" - 'app/views/shared/notifications/_new_button.html.haml'
- "app/views/shared/notifications/_new_button.html.haml" - 'app/views/shared/runners/_runner_description.html.haml'
- "app/views/shared/runners/_runner_description.html.haml" - 'app/views/shared/runners/show.html.haml'
- "app/views/shared/runners/show.html.haml" - 'app/views/shared/snippets/_header.html.haml'
- "app/views/shared/snippets/_header.html.haml" - 'app/views/shared/snippets/_snippet.html.haml'
- "app/views/shared/snippets/_snippet.html.haml" - 'app/views/shared/web_hooks/_form.html.haml'
- "app/views/shared/web_hooks/_form.html.haml" - 'app/views/shared/web_hooks/_hook.html.haml'
- "app/views/shared/web_hooks/_hook.html.haml" - 'app/views/shared/wikis/_pages_wiki_page.html.haml'
- "app/views/shared/wikis/_pages_wiki_page.html.haml" - 'app/views/users/_deletion_guidance.html.haml'
- "app/views/users/_deletion_guidance.html.haml" - 'ee/app/views/admin/_namespace_plan_info.html.haml'
- "ee/app/views/admin/_namespace_plan_info.html.haml" - 'ee/app/views/admin/application_settings/_templates.html.haml'
- "ee/app/views/admin/application_settings/_templates.html.haml" - 'ee/app/views/admin/audit_logs/index.html.haml'
- "ee/app/views/admin/audit_logs/index.html.haml" - 'ee/app/views/admin/emails/show.html.haml'
- "ee/app/views/admin/emails/show.html.haml" - 'ee/app/views/admin/geo/projects/_registry_failed.html.haml'
- "ee/app/views/admin/geo/projects/_registry_failed.html.haml" - 'ee/app/views/admin/geo/projects/_registry_never.html.haml'
- "ee/app/views/admin/geo/projects/_registry_never.html.haml" - 'ee/app/views/admin/licenses/_upload_trial_license.html.haml'
- "ee/app/views/admin/licenses/_upload_trial_license.html.haml" - 'ee/app/views/admin/licenses/new.html.haml'
- "ee/app/views/admin/licenses/new.html.haml" - 'ee/app/views/admin/monitoring/ee/_nav.html.haml'
- "ee/app/views/admin/monitoring/ee/_nav.html.haml" - 'ee/app/views/admin/projects/_shared_runner_status.html.haml'
- "ee/app/views/admin/projects/_shared_runner_status.html.haml" - 'ee/app/views/admin/users/_auditor_access_level_radio.html.haml'
- "ee/app/views/admin/users/_auditor_access_level_radio.html.haml" - 'ee/app/views/admin/users/_auditor_user_badge.html.haml'
- "ee/app/views/admin/users/_auditor_user_badge.html.haml" - 'ee/app/views/admin/users/_limits.html.haml'
- "ee/app/views/admin/users/_limits.html.haml" - 'ee/app/views/admin/users/_user_detail_note.html.haml'
- "ee/app/views/admin/users/_user_detail_note.html.haml" - 'ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml'
- "ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml" - 'ee/app/views/errors/kerberos_denied.html.haml'
- "ee/app/views/errors/kerberos_denied.html.haml" - 'ee/app/views/groups/ee/_settings_nav.html.haml'
- "ee/app/views/groups/ee/_settings_nav.html.haml" - 'ee/app/views/groups/group_members/_ldap_sync.html.haml'
- "ee/app/views/groups/group_members/_ldap_sync.html.haml" - 'ee/app/views/groups/group_members/_sync_button.html.haml'
- "ee/app/views/groups/group_members/_sync_button.html.haml" - 'ee/app/views/groups/hooks/edit.html.haml'
- "ee/app/views/groups/hooks/edit.html.haml" - 'ee/app/views/groups/ldap_group_links/index.html.haml'
- "ee/app/views/groups/ldap_group_links/index.html.haml" - 'ee/app/views/layouts/nav/ee/admin/_new_monitoring_sidebar.html.haml'
- "ee/app/views/layouts/nav/ee/admin/_new_monitoring_sidebar.html.haml" - 'ee/app/views/layouts/service_desk.html.haml'
- "ee/app/views/layouts/service_desk.html.haml" - 'ee/app/views/ldap_group_links/_form.html.haml'
- "ee/app/views/ldap_group_links/_form.html.haml" - 'ee/app/views/ldap_group_links/_ldap_group_link.html.haml'
- "ee/app/views/ldap_group_links/_ldap_group_link.html.haml" - 'ee/app/views/ldap_group_links/_ldap_group_links.html.haml'
- "ee/app/views/ldap_group_links/_ldap_group_links.html.haml" - 'ee/app/views/ldap_group_links/_ldap_group_links_show.html.haml'
- "ee/app/views/ldap_group_links/_ldap_group_links_show.html.haml" - 'ee/app/views/namespaces/_shared_runner_status.html.haml'
- "ee/app/views/ldap_group_links/_ldap_group_links_synchronizations.html.haml" - 'ee/app/views/namespaces/_shared_runners_minutes_setting.html.haml'
- "ee/app/views/namespaces/_shared_runner_status.html.haml" - 'ee/app/views/namespaces/pipelines_quota/_extra_shared_runners_minutes_quota.html.haml'
- "ee/app/views/namespaces/_shared_runners_minutes_setting.html.haml" - 'ee/app/views/namespaces/pipelines_quota/_list.haml'
- "ee/app/views/namespaces/pipelines_quota/_extra_shared_runners_minutes_quota.html.haml" - 'ee/app/views/notify/approved_merge_request_email.html.haml'
- "ee/app/views/namespaces/pipelines_quota/_list.haml" - 'ee/app/views/notify/epic_status_changed_email.html.haml'
- "ee/app/views/notify/approved_merge_request_email.html.haml" - 'ee/app/views/notify/new_review_email.html.haml'
- "ee/app/views/notify/epic_status_changed_email.html.haml" - 'ee/app/views/notify/send_admin_notification.html.haml'
- "ee/app/views/notify/new_review_email.html.haml" - 'ee/app/views/notify/send_unsubscribed_notification.html.haml'
- "ee/app/views/notify/send_admin_notification.html.haml" - 'ee/app/views/notify/unapproved_merge_request_email.html.haml'
- "ee/app/views/notify/send_unsubscribed_notification.html.haml" - 'ee/app/views/oauth/geo_auth/error.html.haml'
- "ee/app/views/notify/unapproved_merge_request_email.html.haml" - 'ee/app/views/projects/commits/_mirror_status.html.haml'
- "ee/app/views/oauth/geo_auth/error.html.haml" - 'ee/app/views/projects/merge_requests/_approvals_count.html.haml'
- "ee/app/views/projects/commits/_mirror_status.html.haml" - 'ee/app/views/projects/merge_requests/widget/open/_geo.html.haml'
- "ee/app/views/projects/merge_requests/_approvals_count.html.haml" - 'ee/app/views/projects/mirrors/_mirrored_repositories_count.html.haml'
- "ee/app/views/projects/merge_requests/widget/open/_geo.html.haml" - 'ee/app/views/projects/protected_branches/_update_protected_branch.html.haml'
- "ee/app/views/projects/mirrors/_mirrored_repositories_count.html.haml" - 'ee/app/views/projects/protected_branches/ee/_create_protected_branch.html.haml'
- "ee/app/views/projects/protected_branches/_update_protected_branch.html.haml" - 'ee/app/views/projects/protected_branches/ee/_dropdown.html.haml'
- "ee/app/views/projects/protected_branches/ee/_create_protected_branch.html.haml" - 'ee/app/views/projects/protected_tags/_protected_tag_extra_create_access_levels.haml'
- "ee/app/views/projects/protected_branches/ee/_dropdown.html.haml" - 'ee/app/views/projects/protected_tags/ee/_create_protected_tag.html.haml'
- "ee/app/views/projects/protected_tags/_protected_tag_extra_create_access_levels.haml" - 'ee/app/views/projects/push_rules/_index.html.haml'
- "ee/app/views/projects/protected_tags/ee/_create_protected_tag.html.haml" - 'ee/app/views/projects/services/gitlab_slack_application/_help.html.haml'
- "ee/app/views/projects/push_rules/_index.html.haml" - 'ee/app/views/projects/services/gitlab_slack_application/_slack_integration_form.html.haml'
- "ee/app/views/projects/services/gitlab_slack_application/_help.html.haml" - 'ee/app/views/projects/settings/slacks/edit.html.haml'
- "ee/app/views/projects/services/gitlab_slack_application/_slack_integration_form.html.haml" - 'ee/app/views/shared/_mirror_update_button.html.haml'
- "ee/app/views/projects/settings/slacks/edit.html.haml" - 'ee/app/views/shared/epic/_search_bar.html.haml'
- "ee/app/views/shared/_mirror_update_button.html.haml" - 'ee/app/views/shared/issuable/_approvals.html.haml'
- "ee/app/views/shared/epic/_search_bar.html.haml" - 'ee/app/views/shared/issuable/_board_create_list_dropdown.html.haml'
- "ee/app/views/shared/issuable/_approvals.html.haml" - 'ee/app/views/shared/issuable/_filter_weight.html.haml'
- "ee/app/views/shared/issuable/_board_create_list_dropdown.html.haml" - 'ee/app/views/shared/members/ee/_ldap_tag.html.haml'
- "ee/app/views/shared/issuable/_filter_weight.html.haml" - 'ee/app/views/shared/members/ee/_override_member_buttons.html.haml'
- "ee/app/views/shared/members/ee/_ldap_tag.html.haml" - 'ee/app/views/shared/members/ee/_sso_badge.html.haml'
- "ee/app/views/shared/members/ee/_override_member_buttons.html.haml" - 'ee/app/views/shared/milestones/_burndown.html.haml'
- "ee/app/views/shared/members/ee/_sso_badge.html.haml" - 'ee/app/views/shared/milestones/_weight.html.haml'
- "ee/app/views/shared/milestones/_burndown.html.haml" - 'ee/app/views/shared/promotions/_promote_issue_weights.html.haml'
- "ee/app/views/shared/milestones/_weight.html.haml" - 'ee/app/views/shared/promotions/_promote_repository_features.html.haml'
- "ee/app/views/shared/promotions/_promote_issue_weights.html.haml" - 'ee/app/views/shared/promotions/_promote_servicedesk.html.haml'
- "ee/app/views/shared/promotions/_promote_repository_features.html.haml" - 'ee/app/views/shared/push_rules/_form.html.haml'
- "ee/app/views/shared/promotions/_promote_servicedesk.html.haml" - 'ee/app/views/unsubscribes/show.html.haml'
- "ee/app/views/shared/push_rules/_form.html.haml"
- "ee/app/views/unsubscribes/show.html.haml"

View file

@ -293,7 +293,15 @@ Gitlab/AvoidFeatureGet:
Enabled: true Enabled: true
RSpec/TimecopFreeze: RSpec/TimecopFreeze:
Enabled: false Enabled: true
AutoCorrect: true
Include:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
- 'qa/spec/**/*.rb'
RSpec/TimecopTravel:
Enabled: true
AutoCorrect: true AutoCorrect: true
Include: Include:
- 'spec/**/*.rb' - 'spec/**/*.rb'
@ -327,7 +335,7 @@ Gitlab/Union:
- 'spec/**/*' - 'spec/**/*'
- 'ee/spec/**/*' - 'ee/spec/**/*'
API/GrapeAPIInstance: API/Base:
Enabled: true Enabled: true
Include: Include:
- 'lib/**/api/**/*.rb' - 'lib/**/api/**/*.rb'
@ -354,6 +362,21 @@ Graphql/AuthorizeTypes:
- 'spec/**/*.rb' - 'spec/**/*.rb'
- 'ee/spec/**/*.rb' - 'ee/spec/**/*.rb'
Graphql/GIDExpectedType:
Enabled: true
Include:
- 'app/graphql/**/*'
- 'ee/app/graphql/**/*'
Exclude:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
Graphql/IDType:
Enabled: true
Include:
- 'app/graphql/**/*'
- 'ee/app/graphql/**/*'
Graphql/JSONType: Graphql/JSONType:
Enabled: true Enabled: true
Include: Include:
@ -548,3 +571,9 @@ Gitlab/RailsLogger:
Exclude: Exclude:
- 'spec/**/*.rb' - 'spec/**/*.rb'
- 'ee/spec/**/*.rb' - 'ee/spec/**/*.rb'
# WIP See https://gitlab.com/gitlab-org/gitlab/-/issues/267606
FactoryBot/InlineAssociation:
Include:
- 'spec/factories/**/*.rb'
- 'ee/spec/factories/**/*.rb'

View file

@ -105,14 +105,6 @@ Lint/StructNewOverride:
- 'app/serializers/environment_serializer.rb' - 'app/serializers/environment_serializer.rb'
- 'lib/gitlab/ci/pipeline/duration.rb' - 'lib/gitlab/ci/pipeline/duration.rb'
# Offense count: 7
Lint/UriEscapeUnescape:
Exclude:
- 'app/controllers/application_controller.rb'
- 'app/models/project_services/drone_ci_service.rb'
- 'spec/lib/google_api/auth_spec.rb'
- 'spec/requests/api/files_spec.rb'
# Offense count: 65 # Offense count: 65
# Cop supports --auto-correct. # Cop supports --auto-correct.
Migration/DepartmentName: Migration/DepartmentName:
@ -162,8 +154,6 @@ Performance/Count:
- 'app/helpers/groups_helper.rb' - 'app/helpers/groups_helper.rb'
- 'app/services/merge_requests/add_context_service.rb' - 'app/services/merge_requests/add_context_service.rb'
- 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb' - 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb'
- 'ee/spec/controllers/projects/feature_flags_controller_spec.rb'
- 'ee/spec/requests/api/feature_flags_spec.rb'
- 'lib/gitlab/sidekiq_status.rb' - 'lib/gitlab/sidekiq_status.rb'
- 'spec/lib/gitlab/conflict/file_spec.rb' - 'spec/lib/gitlab/conflict/file_spec.rb'
- 'spec/lib/gitlab/git/tree_spec.rb' - 'spec/lib/gitlab/git/tree_spec.rb'
@ -175,8 +165,6 @@ Performance/Count:
Performance/Detect: Performance/Detect:
Exclude: Exclude:
- 'ee/spec/controllers/projects/dependencies_controller_spec.rb' - 'ee/spec/controllers/projects/dependencies_controller_spec.rb'
- 'ee/spec/controllers/projects/feature_flags_controller_spec.rb'
- 'ee/spec/requests/api/unleash_spec.rb'
- 'spec/lib/gitlab/git/tree_spec.rb' - 'spec/lib/gitlab/git/tree_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb' - 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
- 'spec/models/event_spec.rb' - 'spec/models/event_spec.rb'
@ -195,6 +183,35 @@ RSpec/ContextWording:
RSpec/ExpectChange: RSpec/ExpectChange:
Enabled: false Enabled: false
# Offense count: 47
RSpec/ExpectGitlabTracking:
Exclude:
- 'ee/spec/controllers/groups/analytics/coverage_reports_controller_spec.rb'
- 'ee/spec/controllers/projects/settings/operations_controller_spec.rb'
- 'ee/spec/controllers/registrations_controller_spec.rb'
- 'ee/spec/requests/api/visual_review_discussions_spec.rb'
- 'ee/spec/services/epics/issue_promote_service_spec.rb'
- 'spec/controllers/groups/registry/repositories_controller_spec.rb'
- 'spec/controllers/groups_controller_spec.rb'
- 'spec/controllers/projects/registry/repositories_controller_spec.rb'
- 'spec/controllers/projects/registry/tags_controller_spec.rb'
- 'spec/controllers/projects/settings/operations_controller_spec.rb'
- 'spec/controllers/registrations_controller_spec.rb'
- 'spec/lib/api/helpers_spec.rb'
- 'spec/lib/gitlab/experimentation_spec.rb'
- 'spec/mailers/notify_spec.rb'
- 'spec/models/project_services/prometheus_service_spec.rb'
- 'spec/requests/api/project_container_repositories_spec.rb'
- 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
- 'spec/services/issues/zoom_link_service_spec.rb'
- 'spec/support/helpers/snowplow_helpers.rb'
- 'spec/support/shared_examples/controllers/trackable_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/packages_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/tracking_shared_examples.rb'
- 'spec/support/snowplow.rb'
# Offense count: 751 # Offense count: 751
RSpec/ExpectInHook: RSpec/ExpectInHook:
Enabled: false Enabled: false
@ -700,13 +717,9 @@ Rails/SaveBang:
- 'ee/spec/models/application_setting_spec.rb' - 'ee/spec/models/application_setting_spec.rb'
- 'ee/spec/models/approval_merge_request_rule_spec.rb' - 'ee/spec/models/approval_merge_request_rule_spec.rb'
- 'ee/spec/models/approval_project_rule_spec.rb' - 'ee/spec/models/approval_project_rule_spec.rb'
- 'ee/spec/models/approval_state_spec.rb'
- 'ee/spec/models/burndown_spec.rb' - 'ee/spec/models/burndown_spec.rb'
- 'ee/spec/models/ci/pipeline_spec.rb' - 'ee/spec/models/ci/pipeline_spec.rb'
- 'ee/spec/models/ci/subscriptions/project_spec.rb' - 'ee/spec/models/ci/subscriptions/project_spec.rb'
- 'ee/spec/models/concerns/approver_migrate_hook_spec.rb'
- 'ee/spec/models/concerns/deprecated_approvals_before_merge_spec.rb'
- 'ee/spec/models/concerns/elastic/note_spec.rb'
- 'ee/spec/models/ee/appearance_spec.rb' - 'ee/spec/models/ee/appearance_spec.rb'
- 'ee/spec/models/ee/ci/job_artifact_spec.rb' - 'ee/spec/models/ee/ci/job_artifact_spec.rb'
- 'ee/spec/models/ee/protected_branch_spec.rb' - 'ee/spec/models/ee/protected_branch_spec.rb'
@ -954,16 +967,12 @@ Rails/SaveBang:
- 'spec/lib/gitlab/ci/ansi2json/style_spec.rb' - 'spec/lib/gitlab/ci/ansi2json/style_spec.rb'
- 'spec/lib/gitlab/ci/status/build/common_spec.rb' - 'spec/lib/gitlab/ci/status/build/common_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb' - 'spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/events_spec.rb'
- 'spec/lib/gitlab/database/custom_structure_spec.rb' - 'spec/lib/gitlab/database/custom_structure_spec.rb'
- 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb' - 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb'
- 'spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb' - 'spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb'
- 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb' - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
- 'spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb' - 'spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb'
- 'spec/lib/gitlab/gfm/reference_rewriter_spec.rb' - 'spec/lib/gitlab/gfm/reference_rewriter_spec.rb'
- 'spec/lib/gitlab/git/object_pool_spec.rb'
- 'spec/lib/gitlab/git/remote_mirror_spec.rb'
- 'spec/lib/gitlab/git/repository_spec.rb'
- 'spec/lib/gitlab/git_access_spec.rb' - 'spec/lib/gitlab/git_access_spec.rb'
- 'spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb' - 'spec/lib/gitlab/gitaly_client/object_pool_service_spec.rb'
- 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb' - 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
@ -1042,18 +1051,6 @@ Rails/SaveBang:
- 'spec/models/clusters/applications/helm_spec.rb' - 'spec/models/clusters/applications/helm_spec.rb'
- 'spec/models/commit_spec.rb' - 'spec/models/commit_spec.rb'
- 'spec/models/commit_status_spec.rb' - 'spec/models/commit_status_spec.rb'
- 'spec/models/concerns/avatarable_spec.rb'
- 'spec/models/concerns/bulk_insertable_associations_spec.rb'
- 'spec/models/concerns/cache_markdown_field_spec.rb'
- 'spec/models/concerns/case_sensitivity_spec.rb'
- 'spec/models/concerns/featurable_spec.rb'
- 'spec/models/concerns/issuable_spec.rb'
- 'spec/models/concerns/mentionable_spec.rb'
- 'spec/models/concerns/milestoneable_spec.rb'
- 'spec/models/concerns/milestoneish_spec.rb'
- 'spec/models/concerns/routable_spec.rb'
- 'spec/models/concerns/subscribable_spec.rb'
- 'spec/models/concerns/token_authenticatable_spec.rb'
- 'spec/models/container_repository_spec.rb' - 'spec/models/container_repository_spec.rb'
- 'spec/models/deploy_keys_project_spec.rb' - 'spec/models/deploy_keys_project_spec.rb'
- 'spec/models/deploy_token_spec.rb' - 'spec/models/deploy_token_spec.rb'
@ -1085,7 +1082,6 @@ Rails/SaveBang:
- 'spec/models/note_spec.rb' - 'spec/models/note_spec.rb'
- 'spec/models/notification_setting_spec.rb' - 'spec/models/notification_setting_spec.rb'
- 'spec/models/operations/feature_flag_scope_spec.rb' - 'spec/models/operations/feature_flag_scope_spec.rb'
- 'spec/models/operations/feature_flag_spec.rb'
- 'spec/models/operations/feature_flags/strategy_spec.rb' - 'spec/models/operations/feature_flags/strategy_spec.rb'
- 'spec/models/operations/feature_flags/user_list_spec.rb' - 'spec/models/operations/feature_flags/user_list_spec.rb'
- 'spec/models/pages_domain_spec.rb' - 'spec/models/pages_domain_spec.rb'
@ -1140,31 +1136,11 @@ Rails/SaveBang:
- 'spec/services/emails/confirm_service_spec.rb' - 'spec/services/emails/confirm_service_spec.rb'
- 'spec/services/groups/destroy_service_spec.rb' - 'spec/services/groups/destroy_service_spec.rb'
- 'spec/services/groups/import_export/import_service_spec.rb' - 'spec/services/groups/import_export/import_service_spec.rb'
- 'spec/services/issuable/bulk_update_service_spec.rb'
- 'spec/services/issuable/clone/attributes_rewriter_spec.rb'
- 'spec/services/issuable/common_system_notes_service_spec.rb'
- 'spec/services/labels/promote_service_spec.rb' - 'spec/services/labels/promote_service_spec.rb'
- 'spec/services/milestones/destroy_service_spec.rb'
- 'spec/services/milestones/promote_service_spec.rb'
- 'spec/services/milestones/transfer_service_spec.rb'
- 'spec/services/notes/create_service_spec.rb' - 'spec/services/notes/create_service_spec.rb'
- 'spec/services/notification_recipients/build_service_spec.rb' - 'spec/services/notification_recipients/build_service_spec.rb'
- 'spec/services/notification_service_spec.rb' - 'spec/services/notification_service_spec.rb'
- 'spec/services/packages/conan/create_package_file_service_spec.rb' - 'spec/services/packages/conan/create_package_file_service_spec.rb'
- 'spec/services/projects/after_rename_service_spec.rb'
- 'spec/services/projects/autocomplete_service_spec.rb'
- 'spec/services/projects/create_service_spec.rb'
- 'spec/services/projects/destroy_service_spec.rb'
- 'spec/services/projects/fork_service_spec.rb'
- 'spec/services/projects/hashed_storage/base_attachment_service_spec.rb'
- 'spec/services/projects/move_access_service_spec.rb'
- 'spec/services/projects/move_project_group_links_service_spec.rb'
- 'spec/services/projects/overwrite_project_service_spec.rb'
- 'spec/services/projects/propagate_service_template_spec.rb'
- 'spec/services/projects/unlink_fork_service_spec.rb'
- 'spec/services/projects/update_pages_service_spec.rb'
- 'spec/services/projects/update_service_spec.rb'
- 'spec/services/quick_actions/interpret_service_spec.rb'
- 'spec/services/reset_project_cache_service_spec.rb' - 'spec/services/reset_project_cache_service_spec.rb'
- 'spec/services/resource_events/change_milestone_service_spec.rb' - 'spec/services/resource_events/change_milestone_service_spec.rb'
- 'spec/services/system_hooks_service_spec.rb' - 'spec/services/system_hooks_service_spec.rb'
@ -1176,20 +1152,188 @@ Rails/SaveBang:
- 'spec/services/users/repair_ldap_blocked_service_spec.rb' - 'spec/services/users/repair_ldap_blocked_service_spec.rb'
- 'spec/services/verify_pages_domain_service_spec.rb' - 'spec/services/verify_pages_domain_service_spec.rb'
- 'spec/sidekiq/cron/job_gem_dependency_spec.rb' - 'spec/sidekiq/cron/job_gem_dependency_spec.rb'
- 'spec/support/migrations_helpers/cluster_helpers.rb'
- 'spec/support/migrations_helpers/namespaces_helper.rb' # Offense count: 187
- 'spec/support/shared_contexts/email_shared_context.rb' # Cop supports --auto-correct.
- 'spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb' RSpec/TimecopFreeze:
- 'spec/support/shared_contexts/mailers/notify_shared_context.rb' Exclude:
- 'spec/support/shared_examples/controllers/cache_control_shared_examples.rb' - 'ee/spec/controllers/admin/application_settings_controller_spec.rb'
- 'spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb' - 'ee/spec/controllers/projects/security/network_policies_controller_spec.rb'
- 'spec/support/shared_examples/controllers/sessionless_auth_controller_shared_examples.rb' - 'ee/spec/features/admin/admin_reset_pipeline_minutes_spec.rb'
- 'spec/support/shared_examples/features/editable_merge_request_shared_examples.rb' - 'ee/spec/features/boards/sidebar_spec.rb'
- 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb' - 'ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb'
- 'spec/support/shared_examples/policies/project_policy_shared_examples.rb' - 'ee/spec/features/groups/iteration_spec.rb'
- 'spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb' - 'ee/spec/features/projects/mirror_spec.rb'
- 'spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb' - 'ee/spec/features/projects/services/prometheus_custom_metrics_spec.rb'
- 'spec/support/shared_examples/serializers/note_entity_shared_examples.rb' - 'ee/spec/finders/productivity_analytics_finder_spec.rb'
- 'spec/tasks/gitlab/web_hook_rake_spec.rb' - 'ee/spec/frontend/fixtures/analytics.rb'
- 'spec/uploaders/file_uploader_spec.rb' - 'ee/spec/helpers/vulnerabilities_helper_spec.rb'
- 'spec/uploaders/object_storage_spec.rb' - 'ee/spec/lib/analytics/merge_request_metrics_refresh_spec.rb'
- 'ee/spec/lib/analytics/productivity_analytics_request_params_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/populate_vulnerability_historical_statistics_spec.rb'
- 'ee/spec/lib/gitlab/analytics/cycle_analytics/data_collector_spec.rb'
- 'ee/spec/lib/gitlab/analytics/cycle_analytics/group_stage_time_summary_spec.rb'
- 'ee/spec/lib/gitlab/analytics/cycle_analytics/summary/group/stage_time_summary_spec.rb'
- 'ee/spec/lib/gitlab/analytics/type_of_work/tasks_by_type_spec.rb'
- 'ee/spec/lib/gitlab/auth/group_saml/sso_enforcer_spec.rb'
- 'ee/spec/lib/gitlab/database/load_balancing/host_spec.rb'
- 'ee/spec/lib/gitlab/geo/base_request_spec.rb'
- 'ee/spec/lib/gitlab/geo/event_gap_tracking_spec.rb'
- 'ee/spec/lib/gitlab/geo/git_push_http_spec.rb'
- 'ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb'
- 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_updated_event_spec.rb'
- 'ee/spec/lib/gitlab/geo/oauth/login_state_spec.rb'
- 'ee/spec/lib/gitlab/insights/reducers/count_per_period_reducer_spec.rb'
- 'ee/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb'
- 'ee/spec/lib/gitlab/prometheus/queries/cluster_query_spec.rb'
- 'ee/spec/migrations/populate_vulnerability_historical_statistics_for_year_spec.rb'
- 'ee/spec/migrations/remove_duplicated_cs_findings_spec.rb'
- 'ee/spec/migrations/remove_duplicated_cs_findings_without_vulnerability_id_spec.rb'
- 'ee/spec/migrations/schedule_fix_orphan_promoted_issues_spec.rb'
- 'ee/spec/migrations/schedule_merge_request_any_approval_rule_migration_spec.rb'
- 'ee/spec/migrations/schedule_populate_resolved_on_default_branch_column_spec.rb'
- 'ee/spec/migrations/schedule_populate_vulnerability_historical_statistics_spec.rb'
- 'ee/spec/migrations/schedule_project_any_approval_rule_migration_spec.rb'
- 'ee/spec/migrations/set_resolved_state_on_vulnerabilities_spec.rb'
- 'ee/spec/migrations/20190926180443_schedule_epic_issues_after_epics_move_spec.rb'
- 'ee/spec/models/analytics/cycle_analytics/group_level_spec.rb'
- 'ee/spec/models/burndown_spec.rb'
- 'ee/spec/models/ee/namespace_spec.rb'
- 'ee/spec/models/geo/project_registry_spec.rb'
- 'ee/spec/models/merge_train_spec.rb'
- 'ee/spec/models/productivity_analytics_spec.rb'
- 'ee/spec/models/project_spec.rb'
- 'ee/spec/models/vulnerabilities/export_spec.rb'
- 'ee/spec/requests/api/vulnerabilities_spec.rb'
- 'ee/spec/services/geo/file_download_service_spec.rb'
- 'ee/spec/services/vulnerabilities/confirm_service_spec.rb'
- 'ee/spec/services/vulnerabilities/dismiss_service_spec.rb'
- 'ee/spec/services/vulnerabilities/resolve_service_spec.rb'
- 'ee/spec/services/vulnerabilities/revert_to_detected_service_spec.rb'
- 'ee/spec/services/vulnerability_exports/export_service_spec.rb'
- 'ee/spec/support/shared_contexts/lib/gitlab/insights/reducers/reducers_shared_contexts.rb'
- 'qa/spec/support/repeater_spec.rb'
- 'spec/features/profiles/active_sessions_spec.rb'
- 'spec/features/projects/environments/environment_metrics_spec.rb'
- 'spec/features/users/active_sessions_spec.rb'
- 'spec/lib/atlassian/jira_connect/client_spec.rb'
- 'spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb'
- 'spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb'
- 'spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb'
- 'spec/lib/gitlab/anonymous_session_spec.rb'
- 'spec/lib/gitlab/auth/unique_ips_limiter_spec.rb'
- 'spec/lib/gitlab/checks/timed_logger_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/usage_data_spec.rb'
- 'spec/lib/gitlab/instrumentation_helper_spec.rb'
- 'spec/lib/gitlab/omniauth_logging/json_formatter_spec.rb'
- 'spec/lib/gitlab/puma_logging/json_formatter_spec.rb'
- 'spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb'
- 'spec/lib/json_web_token/hmac_token_spec.rb'
- 'spec/lib/rspec_flaky/flaky_example_spec.rb'
- 'spec/lib/rspec_flaky/listener_spec.rb'
- 'spec/models/active_session_spec.rb'
- 'spec/models/container_repository_spec.rb'
- 'spec/models/pages/lookup_path_spec.rb'
- 'spec/models/project_feature_usage_spec.rb'
- 'spec/requests/api/v3/github_spec.rb'
- 'spec/serializers/entity_date_helper_spec.rb'
- 'spec/support/cycle_analytics_helpers/test_generation.rb'
- 'spec/support/helpers/cycle_analytics_helpers.rb'
- 'spec/support/helpers/javascript_fixtures_helpers.rb'
- 'spec/support/shared_contexts/rack_attack_shared_context.rb'
- 'spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb'
- 'spec/workers/concerns/reenqueuer_spec.rb'
- 'spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb'
# Offense count: 54
# Cop supports --auto-correct.
RSpec/TimecopTravel:
Exclude:
- 'ee/spec/lib/gitlab/geo/event_gap_tracking_spec.rb'
- 'ee/spec/lib/gitlab/geo/git_push_http_spec.rb'
- 'ee/spec/lib/gitlab/geo/jwt_request_decoder_spec.rb'
- 'ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb'
- 'ee/spec/models/broadcast_message_spec.rb'
- 'ee/spec/models/burndown_spec.rb'
- 'qa/spec/support/repeater_spec.rb'
- 'spec/features/users/terms_spec.rb'
- 'spec/lib/feature_spec.rb'
- 'spec/models/broadcast_message_spec.rb'
- 'spec/models/concerns/issuable_spec.rb'
- 'spec/requests/api/ci/runner/jobs_trace_spec.rb'
- 'spec/requests/api/issues/put_projects_issues_spec.rb'
- 'spec/support/shared_contexts/cache_allowed_users_in_namespace_shared_context.rb'
- 'spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb'
- 'spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb'
- 'spec/workers/concerns/reenqueuer_spec.rb'
- 'spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb'
# Offense count: 43
Graphql/IDType:
Exclude:
- 'ee/app/graphql/ee/mutations/issues/update.rb'
- 'ee/app/graphql/ee/types/boards/board_issue_input_base_type.rb'
- 'ee/app/graphql/mutations/issues/set_epic.rb'
- 'ee/app/graphql/mutations/iterations/update.rb'
- 'ee/app/graphql/resolvers/iterations_resolver.rb'
- 'app/graphql/mutations/boards/create.rb'
- 'app/graphql/mutations/boards/issues/issue_move_list.rb'
- 'app/graphql/mutations/boards/lists/update.rb'
- 'app/graphql/mutations/issues/update.rb'
- 'app/graphql/mutations/metrics/dashboard/annotations/delete.rb'
- 'app/graphql/mutations/snippets/destroy.rb'
- 'app/graphql/mutations/snippets/mark_as_spam.rb'
- 'app/graphql/mutations/snippets/update.rb'
- 'app/graphql/resolvers/board_lists_resolver.rb'
- 'app/graphql/resolvers/boards_resolver.rb'
- 'app/graphql/resolvers/design_management/design_at_version_resolver.rb'
- 'app/graphql/resolvers/design_management/design_resolver.rb'
- 'app/graphql/resolvers/design_management/designs_resolver.rb'
- 'app/graphql/resolvers/design_management/version/design_at_version_resolver.rb'
- 'app/graphql/resolvers/design_management/version_in_collection_resolver.rb'
- 'app/graphql/resolvers/design_management/version_resolver.rb'
- 'app/graphql/resolvers/design_management/versions_resolver.rb'
- 'app/graphql/resolvers/error_tracking/sentry_detailed_error_resolver.rb'
- 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
- 'app/graphql/resolvers/snippets_resolver.rb'
- 'app/graphql/resolvers/user_merge_requests_resolver.rb'
- 'app/graphql/resolvers/user_resolver.rb'
# Offense count: 86
# Cop supports --auto-correct.
FactoryBot/InlineAssociation:
Exclude:
- 'ee/spec/factories/analytics/cycle_analytics/group_stages.rb'
- 'ee/spec/factories/geo/event_log.rb'
- 'ee/spec/factories/groups.rb'
- 'ee/spec/factories/merge_request_blocks.rb'
- 'ee/spec/factories/resource_iteration_event.rb'
- 'ee/spec/factories/resource_weight_events.rb'
- 'ee/spec/factories/vulnerabilities/feedback.rb'
- 'spec/factories/atlassian_identities.rb'
- 'spec/factories/audit_events.rb'
- 'spec/factories/design_management/design_at_version.rb'
- 'spec/factories/design_management/designs.rb'
- 'spec/factories/design_management/versions.rb'
- 'spec/factories/events.rb'
- 'spec/factories/git_wiki_commit_details.rb'
- 'spec/factories/gitaly/commit.rb'
- 'spec/factories/go_module_commits.rb'
- 'spec/factories/go_module_versions.rb'
- 'spec/factories/go_modules.rb'
- 'spec/factories/group_group_links.rb'
- 'spec/factories/import_export_uploads.rb'
- 'spec/factories/merge_requests.rb'
- 'spec/factories/notes.rb'
- 'spec/factories/packages.rb'
- 'spec/factories/packages/package_file.rb'
- 'spec/factories/resource_label_events.rb'
- 'spec/factories/resource_milestone_event.rb'
- 'spec/factories/resource_state_event.rb'
- 'spec/factories/sent_notifications.rb'
- 'spec/factories/serverless/domain.rb'
- 'spec/factories/serverless/domain_cluster.rb'
- 'spec/factories/terraform/state.rb'
- 'spec/factories/uploads.rb'
- 'spec/factories/wiki_pages.rb'

View file

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtgemxR8RUJXi3p7G/dkh
Yuln1L4lA6GtQsT83X0yTVDbLVsI2C6bepsRjGiLV0R/9JGvTojORx+9F/ZQAiEC
g6QXWasAOSmrzr4EjADG6cWcCnOju8hX9yib1HUIBxl+jHkmXP3NPuwyb8p2G149
EG1o4apEqE5RtqV/Xyx/u57xTYYZShJ/c7o4iA8xvt6IAKFPFKpQwb5hv4KvUZBP
h0xG2qvOjDu430fK8JclPlXHqPjXDkXOZyLd4FvRStdEQU3RVXvUQfuGt/tOMS7J
nPQ94fr/xdaEbcEtIlr32+tcgsMWyhqtDCPUWJT1aRPviUgaJKLoVs8tRKwYMV9+
1wIDAQAB
-----END PUBLIC KEY-----

11
.theia/settings.json Normal file
View file

@ -0,0 +1,11 @@
{
"ruby.codeCompletion": "rcodetools",
"ruby.format": "standard",
"ruby.intellisense": "rubyLocate",
"ruby.useBundler": true,
"ruby.useLanguageServer": true,
"ruby.lint": {
"rubocop": true,
"useBundler": true
}
}

View file

@ -2,7 +2,7 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 13.4.7 (2020-12-07) ## 13.5.5 (2020-12-07)
### Security (10 changes) ### Security (10 changes)
@ -18,18 +18,30 @@ entry.
- Do not show starred & contributed projects of users with private profile. - Do not show starred & contributed projects of users with private profile.
## 13.4.6 (2020-11-03) ## 13.5.4 (2020-11-13)
### Fixed (1 change) ### Fixed (4 changes)
- Fix Vue Labels Select dropdown keyboard scroll. !43874
- Hashed Storage: make migration and rollback resilient to exceptions. !46178
- Fix compliance framework database migration on CE instances. !46761
- Resolve problem when namespace_settings were not created for groups created via admin panel. !46875
## 13.5.3 (2020-11-03)
### Fixed (3 changes)
- Fix IDE issues with special characters. !46398
- Ensure that copy to clipboard button is visible. !46466
- Auto Deploy: fixes issues for fetching other charts from stable repo. !46531 - Auto Deploy: fixes issues for fetching other charts from stable repo. !46531
### Other (1 change) ### Added (1 change)
- GitLab-managed apps: Use GitLab's repo as replacement for the Helm stable repo. !44875 - Add environment variables to override backup/restore DB settings. !45855
## 13.4.5 (2020-11-02) ## 13.5.2 (2020-11-02)
### Security (9 changes) ### Security (9 changes)
@ -44,6 +56,587 @@ entry.
- Fix unauthorized user is able to access schedule pipeline variables and values. - Fix unauthorized user is able to access schedule pipeline variables and values.
## 13.5.1 (2020-10-22)
### Other (1 change)
- Update GitLab Shell to v13.11.0. !45660
## 13.5.0 (2020-10-22)
### Security (1 change)
- Update GitLab Runner Helm Chart to 0.21.1.
### Removed (3 changes, 2 of them are from the community)
- Drop Iglu registry URL column. !42939
- Remove coverage_report_view feature flag. !43711 (David Barr @davebarr)
- Remove release_evidence_collection feature flag. !44234 (David Barr @davebarr)
### Fixed (118 changes, 9 of them are from the community)
- Include builds from child pipelines in latest sucessful build for ref/sha. !29710
- Fix branches_to_be_notified API param for hangouts chat service. !35599
- Add empty dependencies value to ECS Deploy job. !36862
- Fix issues with optional merge requests approval in CE. !42119 (Pavel Kuznetsov)
- Fix type of SentryErrorType global ID. !42185
- Remove linux arch only rule for coverage fuzzing. !42316
- Do not show retried builds in the MR code coverage. !42402 (Simon Lenz @koala7)
- Does not refresh project/snippet statistics on a read-only instance. !42417
- Rendering trailing slash in reference links (issue 205151). !42484
- Remove retry icon on failed job if merge pipeline. !42495
- Designs: return an error if uploading designs with duplicate names. !42514 (Sushil Khanchi)
- Unit Test Report: Fix icon for errored status. !42540
- Copy designs to issue when an issue with designs is moved. !42548
- Fix triggering multiple children pipeline with the same artifact. !42595
- Fix caret sizes in navigation. !42605
- Revert required encryption on CI runner tokens. !42623
- Fix Markdown "Preview" tab on New/Edit Release and New Snippet pages. !42640
- Fixed a bug causing 'Missing author note' to be added to notes for mapped users when importing project using GitLab Import. !42648
- Hides batch suggestions button if there is only 1 suggestion. !42681
- Fix GraphQL token authentication when installed under a relative URL. !42706
- Update pipeline failed notification e-mail warning. !42736
- Fix clickable width of release asset links. !42757
- Fix size of edit button on releases page. !42779
- Move before_script into script for CQ template. !42782 (Vicken Simonian @vicken.papaya)
- Resolve Error when quickly reordering designs. !42818
- Eliminate extra spacing on MR diffs from mobile/tablet screen. !42821 (Takuya Noguchi)
- Fix migrating some empty diffs. !42825
- Fix filtering epics when sorting by dates. !42827
- Fix edge case when updating snippet with no repo. !42964
- Fix group deploy tokens permissions for package access. !43007
- Empty state Packages UI links to user docs. !43009
- Allow Unleash clients to request feature flags when repository is private. !43059
- Show incident list for users who can read issues. !43060
- Auto-accept TOS if project bot. !43067
- Fix checking of task lists when MR description starts with a blank line. !43125
- Fix iteration validation not checking parent groups. !43234
- Fix theme selector not working immediately for some themes. !43239
- Reset labels select search text on Enter. !43285
- Ensure JobWaiter keys always expire. !43320
- Make git lfs for push mirrors work to GitHub.com. !43321
- Fix incorrect HTTP response in deactivate user API for internal user. !43356 (Sashi Kumar)
- Fix bug to allow container cleanup policies to properly run. !43359
- Delete project bot when token is revoked. !43373
- Allow to include project files in parent-child pipelines. !43404
- Fix button placement on pipeline graph. !43419
- Fix 500 error in block user API for internal user. !43461 (Sashi Kumar)
- Fix Web hook deletion not working when many hook logs are present. !43464
- Fix copy_indexes migration helper skipping the opclass for indexes with operator classes defined for them. !43471
- Add markdown icon to more file extensions. !43479
- Fix suggested squashed messages for MR. !43508
- Ensure code search results link to searched ref. !43510
- Fix broken user avatars in Jira Development Panel. !43563
- Update database helpers to set the current_schema. !43568
- Remove project bot user membership when project access token expires. !43605
- Improve the Commit box on the Merge Request Changs tab when browsing per commit. !43613
- GraphQL: No longer allows to omit ID when querying for a single board. !43627
- Fix group deploy tokens to return all projects and work with the Maven group endpoint. !43628
- Fix GraphQL backward pagination when merge requests are ordered by merged_at. !43701
- Fix approvedBy filed in MR GraphQL API. !43705
- Customize value of note_target_type for designs. !43727
- Fix displaying a message when design copying is in progress. !43749
- Fix verifying LFS uploads with GitHub. !43852
- Fix Delete User dialog formatted strings. !43871
- Add cleanup migration for JobWaiter Redis keys. !43882
- Include Design Management git repositories in GitLab Backup. !43947
- Add fuzzy search support to labels dropdown. !43969
- Fix broken button default class. !43977
- Fix full screen comment button on snippets. !44083
- Allow unauthenticated users access to public Personal Snippets via the REST API. !44135
- Fix the ability to assign labels based on license feature availability. !44171
- Recover gracefully when issuable counts are too expensive. !44184
- Fix attach file button not working in description fields. !44216
- Fix design scale bug when navigating to a design after zooming. !44262
- Prefer server-provided authentication for LFS push mirroring. !44284
- Return nil when fetching a wiki page with invalid arguments. !44302
- Update Design thumbnail after uploading an image with the same filename. !44305
- Add tooltip for pipeline actions. !44317
- Ensure suggestion works for number text. !44332
- Update NuGet version validation to allow for extended versions. !44335
- Respect DNT when tracking experiments. !44420
- Fix merge conflict button text if "None" code style selected. !44427 (David Barr @davebarr)
- Allow unauthenticated users access to public Project Snippets via the REST API. !44446
- Fix instance statistics GraphQL query with identifier. !44475
- Designs are moved with an Issue that is moved. !44524
- Fix Auto Deploy scale subcommand unintentionally recreates legacy PostgreSQL. !44535
- Fix emoji rendering in certain edge cases. !44542
- Return 422 error rather than 500 when composer.json is missing or malformed. !44587 (David Barr @davebarr)
- Use optimistic locking to safely migrate a build trace chunk. !44588
- Avoid New Environment button glitching when changing tabs. !44603
- Perform git actions with a user with elevated git permissions during a design copy. !44662
- Align badge with avatar in MR List. !44671
- Fix regression when uploading / viewing binary files in the Web IDE. !44699
- Exclude policies with no container repositories when executing them. !44748
- Fix unnecessarily escaped merge error text. !44844
- Fix button row margin on empty project. !44860
- Add note about cross site cookies browser limitaion to Jira App page. !44898
- Allow re-sending invite to minimal access user. !44936
- Fix dark mode for boards and swimlanes. !44951
- Fix dark mode for milestones. !44952
- Add missing 90x avatar size for image scaling. !45025
- Allow size limit to be available by default in the project pages settings form. !45054
- Fixed incorrect parameter in GraphQL startup call. !45115
- Fix table border hover for incidents and alerts. !45117
- Fix Jira Connect App update webhooks. !45151
- Fix scoped label markdown padding. !45153
- Fix redirects to issue sidebar JSON when visiting the login page. !45194
- Revert of Background migration for setting Jira tracker data deployment type. !45205
- Delete any outstanding BackfillJiraTrackerDeploymentType. !45219
- Fix mobile view of filtering bar. !45226
- Fix the maven md5 upload endpoint. !45271
- Redirect when no user is signed in when updating registration. !45276
- Class and markup cleanup to prevent SVG header bar overlap in Static Site Editor. !45334
- Update to Rack v2.1.4. !45340
- Avooid opening 2 modals for enabling review app. !45361
- Fix undefined tooltip text flashing on clipboard icon. !45482
- Fix error when cleaning up MR with no head ref. !45504
- Disable target branch filter option on merge requests dashboard.
- Fixed merge request tabs overlapping with system header.
### Deprecated (2 changes, 1 of them is from the community)
- Set abuse_notification_email instead of admin_notification_email. !41319 (Hiromi Nozawa)
- Drop column instance_statistics_visibility_private. !42969
### Changed (141 changes, 11 of them are from the community)
- Set default Referrer-Policy to strict-origin-when-cross-origin and set it in a header rather than HTML. !26065 (nhirokinet)
- Background migration for setting Jira tracker data deployment type. !37002
- Update clipboard button to use Pajamas. !38421
- Parallelize removal of expired artifacts. !39464
- Update styling of design comment pins. !39797
- Update confidential form buttons to gl-button. !40893
- Replace bootstrap alerts in app/views/admin/broadcast_messages/_form.html.haml. !41271 (Gilang Gumilar)
- Replace bootstrap alerts in app/views/import/shared/_errors.html.haml. !41288 (Gilang Gumilar)
- Replace bootstrap alerts in app/views/projects/diffs/_warning.html.haml. !41295 (Gilang Gumilar)
- Replace bootstrap alerts in app/views/profiles/accounts/show.html.haml. !41299 (Gilang Gumilar)
- Replace bootstrap alerts in app/views/admin/projects/show.html.haml. !41389 (Gilang Gumilar)
- Replace bootstrap alerts in app/views/projects/milestones/show.html.haml. !41396 (Gilang Gumilar)
- Update lock form buttons to gl-button. !41454
- Updated Discard Changes button in WebIDE. !41899
- Migrate DeprecatedModal to GitLab UI Modal. !42113
- Migrate custom Tabs to GlTabs. !42236
- Revert justified-content-end settings buttons. !42273
- Add Web IDE as dropdown item to diff file edit. !42275
- Expose the option to use namespace-per-project instead of namespace-per-environment for Kubernetes clusters. !42309
- Split name to first and last name for signup. !42346
- Move job token specs to core. !42374 (Mathieu Parent)
- Resolve Add filter capabilities to Incident list. !42377
- Remove angle brackets from empty name in U2F device settings. !42440
- Update sidebar operations order. !42493
- Add Gitpod enabled instance setting to Usage Data. !42563
- Add Gitpod enabled user setting to Usage Data. !42570
- Remove accept terms checkbox for signup. !42581
- Add user sign in indicator to Jira connect app. !42628
- Include monitoring tool from payload in system note for alert creation. !42631
- Rename Created to Published in package sort dropdown. !42677
- Breadcrumb like UI for project path in packages list. !42684
- Allow alerts to open on new tab. !42691
- Replace button component. !42716
- Add Issue Link to "Issue opened by" Integration Chat Message. !42785 (Kev @KevSlashNull)
- Hide instance-level integrations on GitLab.com. !42808
- Remove banner that suggests Web IDE for editing gitlab-ci.yml. !42815
- Updated the admin and user SSH key delete confirmation to use GlModal. !42824
- Add confirmation modal on instance-level integration form. !42840
- Use Conan recipe as package name in package API. !42860
- Show wiki tree structure in sidebar and pages overview. !42867
- Allow member mapping to map importer user on Group/Project Import. !42882
- Migrate environments folder tabs to GlTabs. !42894
- Update pypi install command to work with external dependencies. !42916
- Allow designs to be added, changed, or destroyed on locked and moved issues. !42935
- Add a title section to the Package Registry UI. !42963
- Allow time tracking in incidents. !42965
- Feature flags form: Replace fa-chevron-down with GitLab SVG. !42968
- Create a set of models to store the temporary data needed for a bulk import. !42978
- Adjusted deactivation threshold from 180 to 90 days. !42989
- Fix profile scoped label CSS. !43005
- Store pipeline counts by status for instance statistics. !43027
- Remove internal fields from alert details table. !43076
- Add hosts field to alert detail table. !43087
- Update alert GFM reference in highlight bar. !43104
- Replace fa-search with GitLab SVG search icon. !43110
- Update programming language colors and metadata. !43111
- Global Search - Bold Issue's Search Term. !43124
- Replace fa-external-link with GitLab SVG in group folder. !43128
- Add sort by similarity to getProjects GraphQL call. !43136
- Improve two button review submit in merge requests. !43149
- Update user feedback to a dedicated page as opposed to solely a button with a loader. !43189
- Enable project access tokens on GitLab.com. !43190
- VSA: Replace fa-warning with GitLab SVG. !43262
- Add assignee usernames to issue resolver. !43294
- Create ComplianceManagement::Framework Model. !43301
- Add invitation declined page. !43305
- Move approval MR filter and quick actions to CE. !43326 (Pavel Kuznetsov)
- Always set created_by_id when creating a user. !43342
- Description Templates: Replace fontawesome icons with GitLab SVGs. !43379
- Improve WebIDE error messages on committing. !43408
- Remove bootstrap usage from merge_requests/invalid. !43439
- Expose file path from XML Test Report artifact. !43594
- Always show the "Clear cluster cache" button among the advanced Kubernetes cluster configuration options. !43619
- Deprecate lowercase values for sort enums in GraphQL. !43650
- Replace double angle icons with GitLab SVG in issuables sidebar. !43655
- Set performance cookie to last for a year. !43692
- Add snippets to GitLab backups. !43694
- Restore snippet repositories from backups. !43696
- Update issue boards modal to gl-tabs. !43740
- Update nav icons to chevron-down. !43767
- Display alert for partially executed cleanup policies. !43831
- Show keep path for expired locked artifacts. !43866
- Replace fa-search fontawesome icons with GitLab SVG in Vue components. !43879
- Update toggle focus mode icon to gl-icon. !43888
- VSA: Replace fa-warning with GitLab SVG icon. !43994
- Add spam flag to snippet create/update mutations. !44010
- Include cached sql calls in performance bar. !44022
- Updated GraphQL mutation input ids to be more type specific. !44073
- Remove jquery tooltip API call from stop environment button. !44199
- Add filters on Milestone title in the GraphQL API. !44208
- Display conan recipe as package name on package detail page. !44294
- Respect Group's default branch name when present. !44370
- Enable automatic allocation of purchased storage. !44376
- Move remove board column button to sidebar. !44380
- Reposition wiki title on wiki pages. !44390
- Move wiki edit button inline with wiki title. !44391
- Allow users to navigate to the incidents show details page wrapper through `/issues/incidents/:id` from the Incident list. !44438
- Update delete badge modal to gl-modal. !44495
- Remove jquery tooltip from IDE activity bar. !44526
- Remove the `store_instance_statistics_measurements` feature flag. !44566
- Use GitLab SVG icons in file_type_icon_class helper. !44580
- Add pipeline_artifacts_size to RootStorageStatisticsType. !44595
- Copy project homepage default view for anonymous users. !44606 (George Tsiolis)
- Handle the blacklisted ip error in the Go middleware. !44614
- Add limit to number of test cases parsed by JUnit parser. !44615
- Track unique wiki page views in Usage Ping. !44622
- Automatically expand diffs for merge requests with changes to a single file. !44629
- Move feature flags to core. !44642
- Indicate on signin page instance is self-managed. !44681
- Replace fa icon with GitLab SVG in repository preview. !44696
- Replace fa-file-text-o icons with GitLab SVG doc-text icon. !44706
- Replace bootstrap alert in app/views/shared/milestones/_top.html.haml. !44731
- Back-port free instance review for instances with 50+ users from EE Core to CE. !44770
- Search for python packages with normalized name to allow installs of packages with periods and underscores. !44807
- Update integration descriptions to not be project-specific. !44893
- Projects created from templates inherits integrations. !44932
- Update issue and MR sidebar labels to use Vue instead of Haml. !44942
- Replaced blob-content-edit with editor-lite compoennt for Snippet edit form. !44994
- Replace fa-chevron-down with GitLab SVG in project visibility settings. !45021
- Allow more naming conventions for VSA production environment. !45069
- GraphQL: Changes fields in detailedStatus to be nullable. !45072
- Truncate over-long alert fields instead of return error response. !45099
- Raise Puma Worker Killer RAM limits. !45116
- Replace fa icons in CI build table. !45123
- Replace switcher fa- icons in blob viewer models. !45124
- Replace fa-calendar icon with GitLab SVG. !45175
- Minor UI improvements to Wiki edit page. !45247
- Replace fa-angle-double-left and fa-angle-double-right icons with GitLab SVG. !45251
- Remove CSS that ligthens texts in the pipeline. !45253
- Support all stackprof profiling modes. !45277
- Allow automatically selecting repository storage on move. !45338
- Updated GraphQL note mutation input ids to be more type-specific. !45341
- Update GraphQL discussionToggleResolve mutation input id to be more type-specific. !45346
- Update GitLab-Shell to v13.9.0. !45358
- Replace fa-file icons with GitLab SVG document icon. !45380
- Migrate '.fa-spinner' to '.spinner' for 'awards_list.vue'. !45393
- Update gitlab-shell to v13.10.0. !45408
- Replace fa-bitbucket-* icons with GitLab SVG. !45437
- Replace fa-google with GitLab SVG. !45506
- Replace fa-github with GitLab SVG MERGE_REQUEST_ID. !45533
- Move diff header actions into dropdown menu.
### Performance (21 changes, 1 of them is from the community)
- Improve n+1 in pipeline serializer for triggered pipelines. !42421
- Load issues tab in the milestone page asynchronously. !42473
- Add state_id index for merge_requests list. !42481
- Cleanup request http method/code metrics. !42618
- Optimise cleaning up LFS objects. !42830
- Modify time_period for last 28 days to improve batch counting performance. !42972
- Less inconsistent Edit links in sidebar. !43106
- Performance fix for issue placement. !43315
- Reduce cached SQL for JobsController#show. !43559
- Add index for project_id and sha to deployments table. !43836
- Don't expose http_request_duration_seconds metrics in sidekiq exporter. !43941
- Remove index on issues.relative_position. !43991
- Loads cropper css only when needed. !44137
- Preloading of Fontawesome Icon Font. !44282
- Remove duplicate index from the Vulnerabilities table. !44422 (Borivoje Tasovac @borivojetasovac)
- Optionally use merge request metrics association for merge request diff stats in GraphQL. !44613
- Remove Sentry implementation to investigate performance impact. !44643
- Optimize the loading of diffStats in merge request GraphQL API. !44752
- Preload `user_notes_count` in MergeRequest GraphQL API. !44894
- Remove the commit count from the commits API. !44934
- Enable caching of markdown when viewing blob. !45367
### Added (147 changes, 13 of them are from the community)
- Add canonical links for moved/duplicated issues. !34604
- Change transfer, update and create services for groups and projects to take in consideration shared runners settings. !36080 (Arthur de Lapertosa Lisboa)
- Add approval rules with approvers to usage ping. !36737
- Add index on ci_builds relation to improve Usage Ping metrics collection performance. !37581
- UI to disable shared runners by group. !39249
- Report auth events in manage stage usage ping. !39747
- Display youtube videos on the Static Site Editor. !39756
- Add LSIF to Go Auto DevOps gitlab-ci.yml. !40072
- Measure npm request forwarding usage. !40174
- Make URL links in job logs clickable. !40175 (Łukasz Groszkowski @falxcerebri)
- Add No Access Role for top group members. !40942
- Clean up unused LFS objects during repository housekeeping. !40979
- Send chat notification when deployment starts. !41214 (Sashi Kumar)
- Log failed BatchCount queries. !41552
- Add Group Import usage ping. !41663
- Add Sample Data. !41699
- Add Go(lang) to Packages. !41712 (Ethan Reesor (@firelizzard))
- Copy designs to new issue when issue is moved. !41714
- Add namespace setting to allow to mark if parent group allow subgroups to require 2FA. !41760
- Add cache:when keyword for ci yml config. !41822
- Adds package event tracking. !41846
- Add notification setting for merge request reviewers. !41851
- Track unique number of test cases parsed. !41918
- Introduce '.gitlab/static-site-editor.yml' config file, with support for 'static_site_generator' entry. !41957
- Migrate u2f registrations to webauthn registrations. !42159 (Jan Beckmann)
- Add internal API to download LFS objects. !42161
- Add state field to DastSiteValidation. !42198
- Pre-Collapsed Sections in CI Job Logs. !42231 (Kev @KevSlashNull)
- Improve issuable reaction search. !42321 (Ethan Reesor (@firelizzard))
- Show expanded CI config in CI lint API endpoint. !42380
- Display cluster list node information. !42396
- Validate not null file_store field on packages_package_files to maintain data integrity. !42400
- Add API endpoints to manage individual Terraform state versions. !42415
- Display Contributor badges on notes. !42576 (Mycroft Kang @TaehyeokKang)
- Add expiration policy started at support in container repositories. !42598
- Add a REST API endpoint to list group's descendants. !42620
- Match against description and unicode character when autocompleting GFM emoji. !42669 (Ethan Reesor (@firelizzard))
- Add Debian API skeleton. !42670 (Mathieu Parent)
- Use fuzzy matching for issuable awards. !42674 (Ethan Reesor (@firelizzard))
- Add Documentation URL to Admin Area. !42702
- Add close button to issue, MR, and epic sidebar labels. !42703
- Add :default_branch_name column to namespace_settings. !42778
- Add severity and published sorting for incident issues. !42800
- Replaced ACE with Editor Lite for CI linting. !42814
- Include `used_fields` and `used_deprecated_fields` in GraphQL logs. !42820
- Validate build traces using CRC32 checksums. !42829
- Reference pages_deployments in pages_metadata. !42834
- Display user project count on Admin Dashboard. !42871
- Add runner setup methods. !42878
- Add og:description meta tag to individual "Release" page. !42889
- Add validator for IP address/inet columns. !42893
- Add buttons in the Search page to clear Group and Project filters. !42897
- Update golang version in vendored Dockerfile template. !42917
- Strip markdown from og:description meta tags. !42918
- Add DesignCollection copyState GraphQL field. !42919
- Add projects_creating_incidents to usage ping counts. !42934
- Add project scoped CI lint API endpoint. !42998
- GrahphQL: Adds status to jobs, stages, and groups. !43069
- Destroy issue board list via GraphQL. !43081
- JS client for increment_unique_users API. !43084
- Add missing fontawesome file icon classes. !43091
- Adds button to update merge request draft status on merge request show page. !43098
- Sort incidents list by severity and published columns. !43121
- Update skeleton loader shape on releases pages. !43138
- Add security bot. !43147
- Redirect to documentation pages URL when configuration option is set. !43157
- Add on-demand DAST scan options (scanType, showDebugMessages, useAjaxSpider) ajax spider and set the scan type. !43240
- Enable snippet multiple files. !43246
- Add Debian regexps. !43259 (Mathieu Parent)
- Add sort parameter to Issue and Merge Request scopes. !43295
- Add timeline toggle button for incidents comments. !43302
- Add Gitpod Spring Petclinic to Project Templates. !43319
- Allow a users public GPG Keys to be API accessible. !43332
- Add file name column to CI unit test report. !43338
- Add GraphQL endpoint for Terraform state metadata. !43375
- Store user mentions to DB. !43393
- Upgrade GitLab Pages to 1.26.0. !43416
- Remove graphql_lookahead_support feature flag. !43438
- Introduce 'image_upload_path' entry support for '.gitlab/static-site-editor.yml' config file. !43481
- Introduce 'mounts' entry support for '.gitlab/static-site-editor.yml' config file. !43485
- Introduce required_code_owners_sections table. !43573
- Adds flexible rollout strategy UX and documentation. !43611
- Add table for alert http integrations for project. !43634
- Add a database column to enable or disable the setting that puts newly registered users in a pending state, requiring admin approval for their activation. !43661
- Seed initial version for non-versioned terraform states. !43665
- API support for a specific GPG Key for given user. !43693
- Enable design management reference filter. !43731
- Add GraphQL mutation to create an issue. !43735
- Enable wiki events on git push. !43738
- Adds a Terraform.latest.gitlab-ci.yml to support quick development of Terraform related features. !43802
- Store pipeline counts by status for instance statistics. !43857
- Show labels origin path on project labels page. !43858
- Enable querying for merge requests within a group. !43863
- Add API Fuzzing plan limits db column. !43934
- Enable Gitpod button on file tree view. !43961
- Accept issue filters when getting board lists in GraphQL. !43968
- Add system note on incident severity change. !43998
- Move Tracing usage data ping to Core. !44006
- Update Add Members API to accept user_id array. !44051
- GraphQL: Adds scheduledAt to CiJob. !44054
- IDE editor - Adding syntax highlighting for terraform / hcl. !44056
- Allow to update issue state on GraphQL. !44061
- Add merge request title and description UI to Static Site Editor submission flow. !44071
- GraphQL: Adds action to DetailedStatusType and StatusActioType. !44088
- Feature Flags limits UX and documentation. !44089
- Add Incident Sla timer columns to DB. !44099
- Add the ability to insert a YouTube video. !44102
- Include LFS blobs in archives. !44116
- Add sorting parameters to Releases API. !44118
- Add product analytics for design created and modified events. !44129
- Upgrade GitLab Pages to 1.27.0. !44162
- Add the Alerts integrations table to Alert integrations settings in the Operations section. !44181
- Add Issuable Service Level Agreement (SLA) table. !44253
- Use Web IDE to create new files in empty repos. !44287
- Create an issue board via GraphQL mutation. !44298
- Status icons for alerts integratiosn list. !44318
- Added UsageData metrics for issues added/removed from Epics. !44371
- Added UsageData metrics for Issue designs' usage. !44373
- Add unattended database migration option. !44392
- Add feature flag for a phased rollout of cleanup policies. !44444
- Sync LFS objects when push mirroring over HTTPS. !44457
- Snowplow count of clicks on timeline toggle for incident comments. !44487
- Allow to move issues between projects on GraphQL. !44491
- Support ci_forward_deployment_enabled in edit API. !44510
- Preserve the merge request title and description in the static site editor upon modal close. !44512
- Schedule adding "Missed SLA" label to issues. !44546
- Add usage ping to count Static Site Editor views. !44573
- Move Tracing feature to Core. !44574
- Added new editor-lite Vue component. !44577
- Add Middleman Logo for Project Templates. !44617
- Allow groups to disable 2FA requirement for subgroups. !44712
- Editor Lite to saupport extensions in instance constructor. !44723
- Enable core_security_mr_widget feature flag by default. !44764
- Add apply button when user changes assignees. !44812
- Make alerts searchable by assignee username in GraphQL API. !44911
- Include PostgreSQL system identifier in usage ping. !44972
- Snowplow tracking of Incident details views. !45011
- Show origin path of labels on subgroup labels page. !45040
- Enable one_dimensional_matrix feature flag by default. !45086
- Add support for Generic packages. !45102
- Expose `created_at` in Group and Project members API response. !45156 (Rajendra Kadam)
- Show all inherited labels in projects and subgroups. !45161
- Disallow NULL Bytes (U+0000) in requests. !45223
- Introduce 'admin approvals for new user signups' feature. !45233
- Upgrade GitLab Pages to 1.28.0. !45257
- Add vuex stores for milestone comboxbox. !45287
- Add support for manual bridges for CI pipelines. !45368
### Other (114 changes, 53 of them are from the community)
- Replace-GIDeprecatedDropdown-in-app/assets/javascripts/alert_management. !41409 (nuwe1)
- Replace-GlDeprecatedDropdown-with-GlDropdown-in-app/assets/javascripts/ci_variable_list. !41413 (nuwe1)
- Replace deprecated cluster dropdowns with updated dropdowns. !41414 (nuwe1)
- Replace-GlDeprecatedDropdown-with-GlDropdown-in-app/assets/javascripts/confidential_merge_request/components/dropdown.vue. !41416 (nuwe1)
- Replace-GlDeprecatedDropdown-with-GlDropdown-in-app/assets/javascripts/logs. !41421 (nuwe1)
- Replace-GlDeprecatedDropdown-with-GlDropdown-in-app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue. !41423 (nuwe1)
- Replace-GlDeprecatedDropdown-with-GlDropdown-in-app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue. !41424 (nuwe1)
- Replace `GlDeprecatedDropdown` with `GlDropdown` in `app/assets/javascripts/repository/components/breadcrumbs.vue`. !41427 (nuwe1)
- Replace `GlDeprecatedDropdown` with `GlDropdown` in app/assets/javascripts/vue_shared/components/split_button.vue. !41433 (nuwe1)
- Replace GlDeprecatedDropdown with GlDropDown in timezone-dropdown.vue. !41434 (nuwe1)
- Replace-GlDeprecatedDropdown-with-GlDropdown-in-ee/app/assets/javascripts/geo_node_form-and-ee/app/assets/javascripts/geo_replicable. !41438 (nuwe1)
- Remove bootrap alert from gcp offer. !41814
- Update database index on namespaces for type and id. !42128
- Populate issues blocking_issues_count. !42277
- Move shared logic into utils. !42407
- Update button to gl-button on GitLab for Slack page. !42426
- Refactor the invites controller member method. !42727
- Set hook_log css to gl-button. !42730 (Mike Terhar @mterhar)
- Remove an unnecessary element from every page. !42769 (Takuya Noguchi)
- Revise tooltip text of note role badge. !42771 (Mycroft Kang @TaehyeokKang)
- Fix Rails/SaveBang offenses for spec files in spec/services/milestones/*. !42775 (Rajendra Kadam)
- Fix Rails/SaveBang offenses for spec files in spec/services/issuable/*. !42780 (Rajendra Kadam)
- Fixes Rails/SaveBang cop for spec files in ee/spec/models/concerns/*. !42839 (Rajendra Kadam)
- Update GitLab Runner Helm Chart to 0.21.0. !42844
- Notifications icon: Render empty string for custom setting. !42848
- Update GitLab Workhorse to v8.47.0. !42855
- Remove duplicate index on cluster_agents. !42902
- Fixes Rails/SaveBang cop for spec files in spec/models/concerns/*. !42942 (Rajendra Kadam)
- Add issue_email_participants table and related model. !42943
- Add database view for postgres indexes. !42967
- Apply GitLab UI button styles to HAML buttons app/views/projects/blob. !42991 (Andrei Kyrnich @kyrnich)
- Fixes Rails/SaveBang cop for spec files in spec/lib/gitlab/git/*. !43013 (Rajendra Kadam)
- Migrate Recover hidden stage dropdown. !43032
- Remove unused cluster_providers_aws.created_by_user_id column. !43064
- Migrate badge list row buttons to new buttons. !43072
- Apply GitLab UI button styles to HAML buttons app/views/projects/forks. !43101 (Andrei Kyrnich @kyrnich)
- Remove temporary index for fixing broken CS fingerprints. !43126
- Track statistics for index rebuilds. !43156
- Allow get approvals on merge request by GraphQL in CE. !43325 (Pavel Kuznetsov)
- Apply GitLab UI styles to buttons in app/views/shared/labels directory. !43346 (Gary Bell @garybell)
- Update IDE compare changes view button to link style. !43403
- Remove bootstrap from pages/form. !43442
- Update popover to gl-popover on WebIDE commit message. !43499
- Update GitLab Workhorse to v8.48.0. !43586
- Add gl-button class to import and cancel buttons for project member import page. !43620 (Gary Bell @garybell)
- Update Design Management toolbar to use GitLab UI classes. !43682
- Remove type column on audit_events table. !43703
- Update button in modal_copy_button.vue to use GlButton from GitLab UI. !43714
- Migrate deprecated button to GlButton in ingress_modsecurity_settings.vue. !43717
- Migrate button in alert_widget_form.vue. !43720
- Migrate button in fluentd_output_settings.vue. !43724
- Apply GitLab UI button styles to HAML buttons app/views/projects/ci/builds. !43728 (Andrei Kyrnich @kyrnich)
- Log CarrierWave::IntegrityError without sending exception. !43750 (gaga5lala)
- Update node-sass from 4.12.0 to 4.14.1. !43808 (Takuya Noguchi)
- Replace in-repo SVGs with @gitlab/svgs in Cycle Analytics. !43823 (Takuya Noguchi)
- Add more issue change events to usage ping. !43828
- Limit postgres_indexes to owned schemas. !43834
- Add migration to validate design_management_designs.filename text limit constraint. !43952
- Enable track_unique_visits feature flag by default. !43989
- Update GitLab Workhorse to v8.49.0. !43999
- Rate limit documentation for non-configurable limits. !44003
- Fix spelling of PyPI. !44058 (Peter Bittner (@bittner))
- Apply gl-button class to projects/issues/export_csv directory. !44106 (Lakshit)
- Apply GitLab UI button styles to buttons in app/views/sherlock/file_samples. !44109 (Lakshit)
- Remove temporary index for container scanning findings. !44131
- Update doc links in app. !44134
- Add undo helpers for change_column_type_concurrently and cleanup_concurrent_column_type_change. !44155
- Add darkmode support for merge conflict page. !44168
- Remove jquery tooltip API call from delete environment button. !44191
- Add gl-button class to app/views/projects/deployments. !44203 (Lakshit)
- Update Cycle Analytics with Value Stream Analytics in University. !44244 (Takuya Noguchi)
- Apply GitLab UI button styles to buttons in app/views/invites directory. !44289 (Lakshit)
- Apply GitLab UI button styles to buttons in app/views/admin/jobs directory. !44291 (Lakshit)
- Apply GitLab UI button styles to buttons in app/views/projects/services/mattermost_slash_commands. !44293 (Lakshit)
- Apply GitLab UI button styles to buttons in app/views/projects/commits directory. !44331 (Lakshit)
- Apply GitLab UI button styles to buttons in app/views/shared/wikis directory. !44338 (Lakshit)
- Apply GitLab UI button styles to buttons in app/views/projects/compare directory. !44342 (Lakshit)
- Update buttons to use GitLab button class gl-button. !44361 (Gary Bell @garybell)
- Track issue time tracking events in usage ping. !44404
- Fix Rails/SaveBang offenses for spec files in spec/support/shared_example/*. !44424 (matthewbried)
- Bump mini_magick gem version. !44450
- Replace Font Awesome social icons with GitLab SVGs on user profile page. !44599
- Migrating deprecated buttons to GlButtons for modals that have not yet been migrated to the new GlModal component. !44611
- Add product analytics for group-level integrations. !44726
- Add migration helpers for copying check constraints. !44777
- Fix Rails/SaveBang offenses in spec/uploaders/* and spec/tasks/. !44820 (matthewbried)
- Remove d-md-none/d-sm-none when d-sm-none/d-none exists. !44845 (Takuya Noguchi)
- Remove duplicated BS display properties from Admin DevOps report' HAML. !44846 (Takuya Noguchi)
- Remove duplicated BS display properties from Commit's HAML. !44847 (Takuya Noguchi)
- Remove duplicated BS display properties from Diff's HAML. !44848 (Takuya Noguchi)
- Upgrade gitlab-shell to v13.8.0. !44852
- Bump kubeclient to 4.9.1 which includes ability to integrate Kubernetes clusters where their API url is on a sub-path. !44856
- Remove an outdated comment. !44861 (Robin Dupret)
- Migrate collapsed time tracking tooltip. !44874
- GitLab-managed apps: Use GitLab's repo as replacement for the Helm stable repo. !44875
- Fix Rails/SaveBang offenses in spec/support/*. !44884 (matthewbried)
- Track audit event searches via Snowplow. !44888
- Remove duplicated BS display property from Commit/Snippet's HAML. !44917 (Takuya Noguchi)
- Update the copy in the insert image modal to align with copy guidelines. !44949
- Fix Rails/SaveBang offenses in spec/services/projects/*. !44980 (matthewbried)
- Enable usage_data_api feature flag by default. !45004
- Copy profile route under - scope. !45045
- Replacing vue shared tooltip on calendar icon. !45059
- Remove duplicated BS display properties from Environments. !45167 (Takuya Noguchi)
- Remove duplicated BS display properties from Pipelines. !45171 (Takuya Noguchi)
- Populate blocking issues count. !45176
- Remove duplicated BS display properties from Issuables. !45177 (Takuya Noguchi)
- Migrate auto devops message from bootstrap. !45221
- Update Rouge to v3.24. !45225
- Update GitLab Workhorse to v8.51.0. !45256
- Migrate blocked_by issue links to blocks type by swapping source and target. !45262
- Fix documentation link, spacing, and error handling in alert integrations list. !45304
- Replace tooltip with GLTooltip in epic sidebar datepicker. !45392
- Bump cluster applications CI template. !45472
## 13.4.4 (2020-10-15) ## 13.4.4 (2020-10-15)
### Fixed (2 changes) ### Fixed (2 changes)
@ -65,42 +658,6 @@ entry.
- Fix large backups not working with Azure Blob storage. !44233 - Fix large backups not working with Azure Blob storage. !44233
## 13.4.2 (2020-10-01)
### Security (14 changes)
- Do not store session id in Redis.
- Fix permission checks when updating confidentiality and milestone on issues or merge requests.
- Purge unaccepted member invitations older than 90 days.
- Adds feature flags plan limits.
- Prevent SVG XSS via Web IDE.
- Ensure user has no solo owned groups before triggering account deletion.
- Security fix safe params helper.
- Do not bypass admin mode when authenticated with deploy token.
- Fixes release asset link filepath ReDoS.
- Ensure global ID is of Annotation type in GraphQL destroy mutation.
- Validate that membership expiry dates are not in the past.
- Rate limit adding new email and re-sending email confirmation.
- Fix redaction of confidential Todos.
- Update GitLab Runner Helm Chart to 0.20.2.
## 13.4.1 (2020-09-24)
### Fixed (2 changes)
- Revert required encryption on CI runner tokens. !42623
- Allow Unleash clients to request feature flags when repository is private. !43059
### Added (1 change)
- Add missing fontawesome file icon classes. !43091
### Other (1 change)
- Notifications icon: Render empty string for custom setting. !42848
## 13.4.0 (2020-09-22) ## 13.4.0 (2020-09-22)
### Security (2 changes, 1 of them is from the community) ### Security (2 changes, 1 of them is from the community)
@ -1325,6 +1882,26 @@ entry.
- Replace fa-pencil icon with GitLab SVG. !39648 - Replace fa-pencil icon with GitLab SVG. !39648
## 13.2.10 (2020-10-01)
### Security (14 changes)
- Do not store session id in Redis.
- Fix permission checks when updating confidentiality and milestone on issues or merge requests.
- Purge unaccepted member invitations older than 90 days.
- Adds feature flags plan limits.
- Prevent SVG XSS via Web IDE.
- Ensure user has no solo owned groups before triggering account deletion.
- Security fix safe params helper.
- Do not bypass admin mode when authenticated with deploy token.
- Fixes release asset link filepath ReDoS.
- Ensure global ID is of Annotation type in GraphQL destroy mutation.
- Validate that membership expiry dates are not in the past.
- Rate limit adding new email and re-sending email confirmation.
- Fix redaction of confidential Todos.
- Update GitLab Runner Helm Chart to 0.19.4.
## 13.2.8 (2020-09-02) ## 13.2.8 (2020-09-02)
### Security (1 change) ### Security (1 change)

View file

@ -1 +1 @@
13.4.7 13.5.5

View file

@ -1 +1 @@
0.0.5 0.0.6

View file

@ -1 +1 @@
1.25.0 1.28.0

View file

@ -1 +1 @@
13.7.0 13.11.0

View file

@ -1 +1 @@
8.46.0 8.51.0

46
Gemfile
View file

@ -19,13 +19,15 @@ gem 'default_value_for', '~> 3.3.0'
gem 'pg', '~> 1.1' gem 'pg', '~> 1.1'
gem 'rugged', '~> 0.28' gem 'rugged', '~> 0.28'
gem 'grape-path-helpers', '~> 1.3' gem 'grape-path-helpers', '~> 1.4'
gem 'faraday', '~> 1.0' gem 'faraday', '~> 1.0'
gem 'marginalia', '~> 1.9.0' gem 'marginalia', '~> 1.9.0'
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.6' gem 'devise', '~> 4.7.2'
# TODO: verify ARM compile issue on 3.1.13+ version (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18828)
gem 'bcrypt', '3.1.12'
gem 'doorkeeper', '~> 5.3.0' gem 'doorkeeper', '~> 5.3.0'
gem 'doorkeeper-openid_connect', '~> 1.7.4' gem 'doorkeeper-openid_connect', '~> 1.7.4'
gem 'omniauth', '~> 1.8' gem 'omniauth', '~> 1.8'
@ -97,6 +99,7 @@ gem 'graphiql-rails', '~> 1.4.10'
gem 'apollo_upload_server', '~> 2.0.2' gem 'apollo_upload_server', '~> 2.0.2'
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test] gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
gem 'hashie'
# Disable strong_params so that Mash does not respond to :permitted? # Disable strong_params so that Mash does not respond to :permitted?
gem 'hashie-forbidden_attributes' gem 'hashie-forbidden_attributes'
@ -108,7 +111,7 @@ gem 'hamlit', '~> 2.11.0'
# Files attachments # Files attachments
gem 'carrierwave', '~> 1.3' gem 'carrierwave', '~> 1.3'
gem 'mini_magick' gem 'mini_magick', '~> 4.10.1'
# for backups # for backups
gem 'fog-aws', '~> 3.5' gem 'fog-aws', '~> 3.5'
@ -155,7 +158,7 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.10' gem 'asciidoctor', '~> 2.0.10'
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
gem 'asciidoctor-plantuml', '~> 0.0.12' gem 'asciidoctor-plantuml', '~> 0.0.12'
gem 'rouge', '~> 3.21.0' gem 'rouge', '~> 3.24.0'
gem 'truncato', '~> 0.7.11' gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0' gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.9' gem 'nokogiri', '~> 1.10.9'
@ -169,7 +172,7 @@ gem 'diffy', '~> 3.3'
gem 'diff_match_patch', '~> 0.1.0' gem 'diff_match_patch', '~> 0.1.0'
# Application server # Application server
gem 'rack', '~> 2.0.9' gem 'rack', '~> 2.1.4'
# https://github.com/sharpstone/rack-timeout/blob/master/README.md#rails-apps-manually # https://github.com/sharpstone/rack-timeout/blob/master/README.md#rails-apps-manually
gem 'rack-timeout', '~> 0.5.1', require: 'rack/timeout/base' gem 'rack-timeout', '~> 0.5.1', require: 'rack/timeout/base'
@ -244,7 +247,7 @@ gem 'atlassian-jwt', '~> 0.2.0'
gem 'flowdock', '~> 0.7' gem 'flowdock', '~> 0.7'
# Slack integration # Slack integration
gem 'slack-messenger', '~> 2.3.3' gem 'slack-messenger', '~> 2.3.4'
# Hangouts Chat integration # Hangouts Chat integration
gem 'hangouts-chat', '~> 0.0.5' gem 'hangouts-chat', '~> 0.0.5'
@ -256,7 +259,7 @@ gem 'asana', '0.10.2'
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
# Kubernetes integration # Kubernetes integration
gem 'kubeclient', '~> 4.6.0' gem 'kubeclient', '~> 4.9.1'
# Sanitize user input # Sanitize user input
gem 'sanitize', '~> 5.2.1' gem 'sanitize', '~> 5.2.1'
@ -272,7 +275,7 @@ gem 'licensee', '~> 8.9'
gem 'ace-rails-ap', '~> 4.1.0' gem 'ace-rails-ap', '~> 4.1.0'
# Detect and convert string character encoding # Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.5' gem 'charlock_holmes', '~> 0.7.7'
# Detect mime content type from content # Detect mime content type from content
gem 'mimemagic', '~> 0.3.2' gem 'mimemagic', '~> 0.3.2'
@ -284,11 +287,10 @@ gem 'fast_blank'
gem 'gitlab-chronic', '~> 0.10.5' gem 'gitlab-chronic', '~> 0.10.5'
gem 'gitlab_chronic_duration', '~> 0.10.6.2' gem 'gitlab_chronic_duration', '~> 0.10.6.2'
gem 'webpack-rails', '~> 0.9.10'
gem 'rack-proxy', '~> 0.6.0' gem 'rack-proxy', '~> 0.6.0'
gem 'sassc-rails', '~> 2.1.0' gem 'sassc-rails', '~> 2.1.0'
gem 'uglifier', '~> 2.7.2' gem 'terser', '1.0.2'
gem 'addressable', '~> 2.7' gem 'addressable', '~> 2.7'
gem 'font-awesome-rails', '~> 4.7' gem 'font-awesome-rails', '~> 4.7'
@ -308,7 +310,7 @@ gem 'sentry-raven', '~> 3.0'
gem 'premailer-rails', '~> 1.10.3' gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation # LabKit: Tracing and Correlation
gem 'gitlab-labkit', '0.12.1' gem 'gitlab-labkit', '0.12.2'
# I18n # I18n
gem 'ruby_parser', '~> 3.8', require: false gem 'ruby_parser', '~> 3.8', require: false
@ -330,13 +332,13 @@ group :metrics do
gem 'method_source', '~> 1.0', require: false gem 'method_source', '~> 1.0', require: false
# Prometheus # Prometheus
gem 'prometheus-client-mmap', '~> 0.11.0' gem 'prometheus-client-mmap', '~> 0.12.0'
gem 'raindrops', '~> 0.18' gem 'raindrops', '~> 0.18'
end end
group :development do group :development do
gem 'brakeman', '~> 4.2', require: false gem 'brakeman', '~> 4.2', require: false
gem 'danger', '~> 8.0', require: false gem 'danger', '~> 8.0.6', require: false
gem 'letter_opener_web', '~> 1.3.4' gem 'letter_opener_web', '~> 1.3.4'
@ -375,8 +377,6 @@ group :development, :test do
gem 'scss_lint', '~> 0.56.0', require: false gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.34.0', require: false gem 'haml_lint', '~> 0.34.0', require: false
gem 'simplecov', '~> 0.18.5', require: false
gem 'simplecov-cobertura', '~> 1.3.1', require: false
gem 'bundler-audit', '~> 0.6.1', require: false gem 'bundler-audit', '~> 0.6.1', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false gem 'benchmark-ips', '~> 2.3.0', require: false
@ -394,9 +394,14 @@ group :development, :test do
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
end end
group :development, :test, :coverage do
gem 'simplecov', '~> 0.18.5', require: false
gem 'simplecov-cobertura', '~> 1.3.1', require: false
end
# Gems required in omnibus-gitlab pipeline # Gems required in omnibus-gitlab pipeline
group :development, :test, :omnibus do group :development, :test, :omnibus do
gem 'license_finder', '~> 5.4', require: false gem 'license_finder', '~> 6.0', require: false
end end
group :test do group :test do
@ -411,7 +416,7 @@ group :test do
gem 'shoulda-matchers', '~> 4.0.1', require: false gem 'shoulda-matchers', '~> 4.0.1', require: false
gem 'email_spec', '~> 2.2.0' gem 'email_spec', '~> 2.2.0'
gem 'webmock', '~> 3.5.1' gem 'webmock', '~> 3.9.1'
gem 'rails-controller-testing' gem 'rails-controller-testing'
gem 'concurrent-ruby', '~> 1.1' gem 'concurrent-ruby', '~> 1.1'
gem 'test-prof', '~> 0.12.0' gem 'test-prof', '~> 0.12.0'
@ -425,7 +430,7 @@ end
gem 'octokit', '~> 4.15' gem 'octokit', '~> 4.15'
# https://gitlab.com/gitlab-org/gitlab/issues/207207 # https://gitlab.com/gitlab-org/gitlab/issues/207207
gem 'gitlab-mail_room', '~> 0.0.6', require: 'mail_room' gem 'gitlab-mail_room', '~> 0.0.7', require: 'mail_room'
gem 'email_reply_trimmer', '~> 0.1' gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text' gem 'html2text'
@ -461,7 +466,7 @@ group :ed25519 do
end end
# Gitaly GRPC protocol definitions # Gitaly GRPC protocol definitions
gem 'gitaly', '~> 13.3.0-rc1' gem 'gitaly', '~> 13.5.0-rc2'
gem 'grpc', '~> 1.30.2' gem 'grpc', '~> 1.30.2'
@ -512,3 +517,6 @@ gem 'multi_json', '~> 1.14.1'
gem 'yajl-ruby', '~> 1.4.1', require: 'yajl' gem 'yajl-ruby', '~> 1.4.1', require: 'yajl'
gem 'webauthn', '~> 2.3' gem 'webauthn', '~> 2.3'
# IPAddress utilities
gem 'ipaddress', '~> 0.8.3'

View file

@ -6,59 +6,59 @@ GEM
ace-rails-ap (4.1.2) ace-rails-ap (4.1.2)
acme-client (2.0.6) acme-client (2.0.6)
faraday (>= 0.17, < 2.0.0) faraday (>= 0.17, < 2.0.0)
actioncable (6.0.3.1) actioncable (6.0.3.3)
actionpack (= 6.0.3.1) actionpack (= 6.0.3.3)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (6.0.3.1) actionmailbox (6.0.3.3)
actionpack (= 6.0.3.1) actionpack (= 6.0.3.3)
activejob (= 6.0.3.1) activejob (= 6.0.3.3)
activerecord (= 6.0.3.1) activerecord (= 6.0.3.3)
activestorage (= 6.0.3.1) activestorage (= 6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
mail (>= 2.7.1) mail (>= 2.7.1)
actionmailer (6.0.3.1) actionmailer (6.0.3.3)
actionpack (= 6.0.3.1) actionpack (= 6.0.3.3)
actionview (= 6.0.3.1) actionview (= 6.0.3.3)
activejob (= 6.0.3.1) activejob (= 6.0.3.3)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (6.0.3.1) actionpack (6.0.3.3)
actionview (= 6.0.3.1) actionview (= 6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
rack (~> 2.0, >= 2.0.8) rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.0.3.1) actiontext (6.0.3.3)
actionpack (= 6.0.3.1) actionpack (= 6.0.3.3)
activerecord (= 6.0.3.1) activerecord (= 6.0.3.3)
activestorage (= 6.0.3.1) activestorage (= 6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (6.0.3.1) actionview (6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.0.3.1) activejob (6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (6.0.3.1) activemodel (6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
activerecord (6.0.3.1) activerecord (6.0.3.3)
activemodel (= 6.0.3.1) activemodel (= 6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
activerecord-explain-analyze (0.1.0) activerecord-explain-analyze (0.1.0)
activerecord (>= 4) activerecord (>= 4)
pg pg
activestorage (6.0.3.1) activestorage (6.0.3.3)
actionpack (= 6.0.3.1) actionpack (= 6.0.3.3)
activejob (= 6.0.3.1) activejob (= 6.0.3.3)
activerecord (= 6.0.3.1) activerecord (= 6.0.3.3)
marcel (~> 0.3.1) marcel (~> 0.3.1)
activesupport (6.0.3.1) activesupport (6.0.3.3)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
minitest (~> 5.1) minitest (~> 5.1)
@ -167,7 +167,7 @@ GEM
mime-types (>= 1.16) mime-types (>= 1.16)
cbor (0.5.9.6) cbor (0.5.9.6)
character_set (1.4.0) character_set (1.4.0)
charlock_holmes (0.7.6) charlock_holmes (0.7.7)
childprocess (3.0.0) childprocess (3.0.0)
chunky_png (1.3.5) chunky_png (1.3.5)
citrus (3.0.2) citrus (3.0.2)
@ -183,7 +183,7 @@ GEM
concord (0.1.5) concord (0.1.5)
adamantium (~> 0.2.0) adamantium (~> 0.2.0)
equalizer (~> 0.0.9) equalizer (~> 0.0.9)
concurrent-ruby (1.1.6) concurrent-ruby (1.1.7)
connection_pool (2.2.2) connection_pool (2.2.2)
contracts (0.11.0) contracts (0.11.0)
cork (0.3.0) cork (0.3.0)
@ -202,7 +202,7 @@ GEM
css_parser (1.7.0) css_parser (1.7.0)
addressable addressable
daemons (1.2.6) daemons (1.2.6)
danger (8.0.5) danger (8.0.6)
claide (~> 1.0) claide (~> 1.0)
claide-plugins (>= 0.9.2) claide-plugins (>= 0.9.2)
colored2 (~> 3.1) colored2 (~> 3.1)
@ -235,7 +235,7 @@ GEM
thor (>= 0.19, < 2) thor (>= 0.19, < 2)
unicode_plot (>= 0.0.4, < 1.0.0) unicode_plot (>= 0.0.4, < 1.0.0)
device_detector (1.0.0) device_detector (1.0.0)
devise (4.7.1) devise (4.7.3)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0) railties (>= 4.1.0)
@ -253,7 +253,7 @@ GEM
discordrb-webhooks-blackst0ne (3.3.0) discordrb-webhooks-blackst0ne (3.3.0)
rest-client (~> 2.0) rest-client (~> 2.0)
docile (1.3.2) docile (1.3.2)
domain_name (0.5.20180417) domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.3.3) doorkeeper (5.3.3)
railties (>= 5) railties (>= 5)
@ -312,7 +312,7 @@ GEM
tzinfo tzinfo
eventmachine (1.2.7) eventmachine (1.2.7)
excon (0.71.1) excon (0.71.1)
execjs (2.6.0) execjs (2.7.0)
expression_parser (0.9.0) expression_parser (0.9.0)
extended-markdown-filter (0.6.0) extended-markdown-filter (0.6.0)
html-pipeline (~> 2.0) html-pipeline (~> 2.0)
@ -416,7 +416,7 @@ GEM
rails (>= 3.2.0) rails (>= 3.2.0)
git (1.7.0) git (1.7.0)
rchardet (~> 1.8) rchardet (~> 1.8)
gitaly (13.3.0.pre.rc2) gitaly (13.5.0.pre.rc2)
grpc (~> 1.0) grpc (~> 1.0)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-chronic (0.10.5) gitlab-chronic (0.10.5)
@ -428,7 +428,7 @@ GEM
fog-json (~> 1.2.0) fog-json (~> 1.2.0)
mime-types mime-types
ms_rest_azure (~> 0.12.0) ms_rest_azure (~> 0.12.0)
gitlab-labkit (0.12.1) gitlab-labkit (0.12.2)
actionpack (>= 5.0.0, < 6.1.0) actionpack (>= 5.0.0, < 6.1.0)
activesupport (>= 5.0.0, < 6.1.0) activesupport (>= 5.0.0, < 6.1.0)
grpc (~> 1.19) grpc (~> 1.19)
@ -436,7 +436,7 @@ GEM
opentracing (~> 0.4) opentracing (~> 0.4)
redis (> 3.0.0, < 5.0.0) redis (> 3.0.0, < 5.0.0)
gitlab-license (1.0.0) gitlab-license (1.0.0)
gitlab-mail_room (0.0.6) gitlab-mail_room (0.0.7)
gitlab-markup (1.7.1) gitlab-markup (1.7.1)
gitlab-net-dns (0.9.1) gitlab-net-dns (0.9.1)
gitlab-puma (4.3.5.gitlab.3) gitlab-puma (4.3.5.gitlab.3)
@ -495,7 +495,7 @@ GEM
grape-entity (0.7.1) grape-entity (0.7.1)
activesupport (>= 4.0) activesupport (>= 4.0)
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grape-path-helpers (1.3.0) grape-path-helpers (1.4.0)
activesupport activesupport
grape (~> 1.3) grape (~> 1.3)
rake (~> 12) rake (~> 12)
@ -547,7 +547,7 @@ GEM
tilt tilt
hana (1.3.6) hana (1.3.6)
hangouts-chat (0.0.5) hangouts-chat (0.0.5)
hashdiff (0.3.8) hashdiff (1.0.1)
hashie (3.6.0) hashie (3.6.0)
hashie-forbidden_attributes (0.1.1) hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0) hashie (>= 3.0)
@ -563,21 +563,22 @@ GEM
html2text (0.2.0) html2text (0.2.0)
nokogiri (~> 1.6) nokogiri (~> 1.6)
htmlentities (4.3.4) htmlentities (4.3.4)
http (4.2.0) http (4.4.1)
addressable (~> 2.3) addressable (~> 2.3)
http-cookie (~> 1.0) http-cookie (~> 1.0)
http-form_data (~> 2.0) http-form_data (~> 2.2)
http-parser (~> 1.2.0) http-parser (~> 1.2.0)
http-accept (1.7.0)
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
http-form_data (2.1.1) http-form_data (2.3.0)
http-parser (1.2.1) http-parser (1.2.1)
ffi-compiler (>= 1.0, < 2.0) ffi-compiler (>= 1.0, < 2.0)
httparty (0.16.4) httparty (0.16.4)
mime-types (~> 3.0) mime-types (~> 3.0)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpclient (2.8.3) httpclient (2.8.3)
i18n (1.8.3) i18n (1.8.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n_data (0.8.0) i18n_data (0.8.0)
icalendar (2.4.1) icalendar (2.4.1)
@ -611,6 +612,9 @@ GEM
hana (~> 1.3) hana (~> 1.3)
regexp_parser (~> 1.5) regexp_parser (~> 1.5)
uri_template (~> 0.7) uri_template (~> 0.7)
jsonpath (1.0.5)
multi_json
to_regexp (~> 0.2.1)
jwt (2.1.0) jwt (2.1.0)
kaminari (1.2.1) kaminari (1.2.1)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
@ -631,9 +635,10 @@ GEM
rexml rexml
kramdown-parser-gfm (1.1.0) kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0) kramdown (~> 2.0)
kubeclient (4.6.0) kubeclient (4.9.1)
http (>= 3.0, < 5.0) http (>= 3.0, < 5.0)
recursive-open-struct (~> 1.0, >= 1.0.4) jsonpath (~> 1.0)
recursive-open-struct (~> 1.1, >= 1.1.1)
rest-client (~> 2.0) rest-client (~> 2.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
@ -643,9 +648,9 @@ GEM
actionmailer (>= 3.2) actionmailer (>= 3.2)
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
license_finder (5.4.0) license_finder (6.0.0)
bundler bundler
rubyzip rubyzip (>= 1, < 3)
thor thor
toml (= 0.2.0) toml (= 0.2.0)
with_env (= 1.1.0) with_env (= 1.1.0)
@ -662,7 +667,7 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.5.0) loofah (2.7.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
lru_redux (1.1.0) lru_redux (1.1.0)
@ -684,7 +689,7 @@ GEM
mime-types-data (3.2020.0512) mime-types-data (3.2020.0512)
mimemagic (0.3.5) mimemagic (0.3.5)
mini_histogram (0.1.3) mini_histogram (0.1.3)
mini_magick (4.9.5) mini_magick (4.10.1)
mini_mime (1.0.2) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
minitest (5.11.3) minitest (5.11.3)
@ -713,7 +718,7 @@ GEM
net-ntp (2.1.3) net-ntp (2.1.3)
net-ssh (6.0.0) net-ssh (6.0.0)
netrc (0.11.0) netrc (0.11.0)
nio4r (2.5.2) nio4r (2.5.4)
no_proxy_fix (0.1.2) no_proxy_fix (0.1.2)
nokogiri (1.10.10) nokogiri (1.10.10)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
@ -838,7 +843,7 @@ GEM
parser parser
unparser unparser
procto (0.0.3) procto (0.0.3)
prometheus-client-mmap (0.11.0) prometheus-client-mmap (0.12.0)
pry (0.13.1) pry (0.13.1)
coderay (~> 1.1) coderay (~> 1.1)
method_source (~> 1.0) method_source (~> 1.0)
@ -847,10 +852,10 @@ GEM
pry (~> 0.13.0) pry (~> 0.13.0)
pry-rails (0.3.9) pry-rails (0.3.9)
pry (>= 0.10.4) pry (>= 0.10.4)
public_suffix (4.0.3) public_suffix (4.0.6)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
raabro (1.1.6) raabro (1.1.6)
rack (2.0.9) rack (2.1.4)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (6.3.0) rack-attack (6.3.0)
@ -870,25 +875,25 @@ GEM
rack-test (1.1.0) rack-test (1.1.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rack-timeout (0.5.2) rack-timeout (0.5.2)
rails (6.0.3.1) rails (6.0.3.3)
actioncable (= 6.0.3.1) actioncable (= 6.0.3.3)
actionmailbox (= 6.0.3.1) actionmailbox (= 6.0.3.3)
actionmailer (= 6.0.3.1) actionmailer (= 6.0.3.3)
actionpack (= 6.0.3.1) actionpack (= 6.0.3.3)
actiontext (= 6.0.3.1) actiontext (= 6.0.3.3)
actionview (= 6.0.3.1) actionview (= 6.0.3.3)
activejob (= 6.0.3.1) activejob (= 6.0.3.3)
activemodel (= 6.0.3.1) activemodel (= 6.0.3.3)
activerecord (= 6.0.3.1) activerecord (= 6.0.3.3)
activestorage (= 6.0.3.1) activestorage (= 6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
bundler (>= 1.3.0) bundler (>= 1.3.0)
railties (= 6.0.3.1) railties (= 6.0.3.3)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.x) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.x) actionview (>= 5.0.1.rc1)
activesupport (>= 5.0.1.x) activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.0.3) rails-dom-testing (2.0.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
nokogiri (>= 1.6) nokogiri (>= 1.6)
@ -897,9 +902,9 @@ GEM
rails-i18n (6.0.0) rails-i18n (6.0.0)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7) railties (>= 6.0.0, < 7)
railties (6.0.3.1) railties (6.0.3.3)
actionpack (= 6.0.3.1) actionpack (= 6.0.3.3)
activesupport (= 6.0.3.1) activesupport (= 6.0.3.3)
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0) thor (>= 0.20.3, < 2.0)
@ -920,7 +925,7 @@ GEM
re2 (1.2.0) re2 (1.2.0)
recaptcha (4.13.1) recaptcha (4.13.1)
json json
recursive-open-struct (1.1.1) recursive-open-struct (1.1.2)
redis (4.1.3) redis (4.1.3)
redis-actionpack (5.2.0) redis-actionpack (5.2.0)
actionpack (>= 5, < 7) actionpack (>= 5, < 7)
@ -951,7 +956,8 @@ GEM
responders (3.0.0) responders (3.0.0)
actionpack (>= 5.0) actionpack (>= 5.0)
railties (>= 5.0) railties (>= 5.0)
rest-client (2.0.2) rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0) http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0) mime-types (>= 1.16, < 4.0)
netrc (~> 0.8) netrc (~> 0.8)
@ -959,7 +965,7 @@ GEM
rexml (3.2.4) rexml (3.2.4)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (3.21.0) rouge (3.24.0)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
@ -1065,7 +1071,7 @@ GEM
seed-fu (2.3.7) seed-fu (2.3.7)
activerecord (>= 3.1) activerecord (>= 3.1)
activesupport (>= 3.1) activesupport (>= 3.1)
selenium-webdriver (3.142.6) selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0) childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2) rubyzip (>= 1.2.2)
sentry-raven (3.0.4) sentry-raven (3.0.4)
@ -1096,7 +1102,7 @@ GEM
simplecov (~> 0.8) simplecov (~> 0.8)
simplecov-html (0.12.2) simplecov-html (0.12.2)
sixarm_ruby_unaccent (1.2.0) sixarm_ruby_unaccent (1.2.0)
slack-messenger (2.3.3) slack-messenger (2.3.4)
snowplow-tracker (0.6.1) snowplow-tracker (0.6.1)
contracts (~> 0.7, <= 0.11) contracts (~> 0.7, <= 0.11)
spring (2.0.2) spring (2.0.2)
@ -1106,7 +1112,7 @@ GEM
sprockets (3.7.2) sprockets (3.7.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
rack (> 1, < 3) rack (> 1, < 3)
sprockets-rails (3.2.1) sprockets-rails (3.2.2)
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
@ -1130,6 +1136,8 @@ GEM
temple (0.8.2) temple (0.8.2)
terminal-table (1.8.0) terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1, >= 1.1.1)
terser (1.0.2)
execjs (>= 0.3.0, < 3)
test-prof (0.12.0) test-prof (0.12.0)
text (1.3.1) text (1.3.1)
thin (1.7.2) thin (1.7.2)
@ -1143,6 +1151,7 @@ GEM
timecop (0.9.1) timecop (0.9.1)
timeliness (0.3.10) timeliness (0.3.10)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
to_regexp (0.2.1)
toml (0.2.0) toml (0.2.0)
parslet (~> 1.8.0) parslet (~> 1.8.0)
toml-rb (1.0.0) toml-rb (1.0.0)
@ -1157,12 +1166,9 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
u2f (0.2.1) u2f (0.2.1)
uber (0.1.0) uber (0.1.0)
uglifier (2.7.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.5) unf_ext (0.0.7.7)
unicode-display_width (1.7.0) unicode-display_width (1.7.0)
unicode_plot (0.0.4) unicode_plot (0.0.4)
enumerable-statistics (>= 2.0.1) enumerable-statistics (>= 2.0.1)
@ -1214,13 +1220,11 @@ GEM
webfinger (1.1.0) webfinger (1.1.0)
activesupport activesupport
httpclient (>= 2.4) httpclient (>= 2.4)
webmock (3.5.1) webmock (3.9.1)
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff hashdiff (>= 0.4.0, < 2.0.0)
webpack-rails (0.9.11) websocket-driver (0.7.3)
railties (>= 3.2.0)
websocket-driver (0.7.1)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
wikicloth (0.8.1) wikicloth (0.8.1)
@ -1232,7 +1236,7 @@ GEM
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
yajl-ruby (1.4.1) yajl-ruby (1.4.1)
zeitwerk (2.3.0) zeitwerk (2.4.0)
PLATFORMS PLATFORMS
ruby ruby
@ -1259,6 +1263,7 @@ DEPENDENCIES
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
batch-loader (~> 1.4.0) batch-loader (~> 1.4.0)
bcrypt (= 3.1.12)
bcrypt_pbkdf (~> 1.0) bcrypt_pbkdf (~> 1.0)
benchmark-ips (~> 2.3.0) benchmark-ips (~> 2.3.0)
benchmark-memory (~> 0.1) benchmark-memory (~> 0.1)
@ -1272,19 +1277,19 @@ DEPENDENCIES
capybara (~> 3.33.0) capybara (~> 3.33.0)
capybara-screenshot (~> 1.0.22) capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3) carrierwave (~> 1.3)
charlock_holmes (~> 0.7.5) charlock_holmes (~> 0.7.7)
commonmarker (~> 0.20) commonmarker (~> 0.20)
concurrent-ruby (~> 1.1) concurrent-ruby (~> 1.1)
connection_pool (~> 2.0) connection_pool (~> 2.0)
countries (~> 3.0) countries (~> 3.0)
creole (~> 0.5.0) creole (~> 0.5.0)
danger (~> 8.0) danger (~> 8.0.6)
database_cleaner (~> 1.7.0) database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.3.1) deckar01-task_list (= 2.3.1)
default_value_for (~> 3.3.0) default_value_for (~> 3.3.0)
derailed_benchmarks derailed_benchmarks
device_detector device_detector
devise (~> 4.6) devise (~> 4.7.2)
devise-two-factor (~> 3.1.0) devise-two-factor (~> 3.1.0)
diff_match_patch (~> 0.1.0) diff_match_patch (~> 0.1.0)
diffy (~> 3.3) diffy (~> 3.3)
@ -1322,13 +1327,13 @@ DEPENDENCIES
gettext (~> 3.3) gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly (~> 13.3.0.pre.rc1) gitaly (~> 13.5.0.pre.rc2)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5) gitlab-chronic (~> 0.10.5)
gitlab-fog-azure-rm (~> 1.0) gitlab-fog-azure-rm (~> 1.0)
gitlab-labkit (= 0.12.1) gitlab-labkit (= 0.12.2)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
gitlab-mail_room (~> 0.0.6) gitlab-mail_room (~> 0.0.7)
gitlab-markup (~> 1.7.1) gitlab-markup (~> 1.7.1)
gitlab-net-dns (~> 0.9.1) gitlab-net-dns (~> 0.9.1)
gitlab-puma (~> 4.3.3.gitlab.2) gitlab-puma (~> 4.3.3.gitlab.2)
@ -1343,7 +1348,7 @@ DEPENDENCIES
gpgme (~> 2.0.19) gpgme (~> 2.0.19)
grape (= 1.4.0) grape (= 1.4.0)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.3) grape-path-helpers (~> 1.4)
grape_logging (~> 1.7) grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10) graphiql-rails (~> 1.4.10)
graphql (~> 1.11.4) graphql (~> 1.11.4)
@ -1354,6 +1359,7 @@ DEPENDENCIES
haml_lint (~> 0.34.0) haml_lint (~> 0.34.0)
hamlit (~> 2.11.0) hamlit (~> 2.11.0)
hangouts-chat (~> 0.0.5) hangouts-chat (~> 0.0.5)
hashie
hashie-forbidden_attributes hashie-forbidden_attributes
health_check (~> 3.0) health_check (~> 3.0)
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
@ -1362,6 +1368,7 @@ DEPENDENCIES
httparty (~> 0.16.4) httparty (~> 0.16.4)
icalendar icalendar
invisible_captcha (~> 0.12.1) invisible_captcha (~> 0.12.1)
ipaddress (~> 0.8.3)
jira-ruby (~> 2.0.0) jira-ruby (~> 2.0.0)
js_regex (~> 3.4) js_regex (~> 3.4)
json (~> 2.3.0) json (~> 2.3.0)
@ -1371,9 +1378,9 @@ DEPENDENCIES
kaminari (~> 1.0) kaminari (~> 1.0)
knapsack (~> 1.17) knapsack (~> 1.17)
kramdown (~> 2.3.0) kramdown (~> 2.3.0)
kubeclient (~> 4.6.0) kubeclient (~> 4.9.1)
letter_opener_web (~> 1.3.4) letter_opener_web (~> 1.3.4)
license_finder (~> 5.4) license_finder (~> 6.0)
licensee (~> 8.9) licensee (~> 8.9)
lockbox (~> 0.3.3) lockbox (~> 0.3.3)
lograge (~> 0.5) lograge (~> 0.5)
@ -1384,7 +1391,7 @@ DEPENDENCIES
memory_profiler (~> 0.9) memory_profiler (~> 0.9)
method_source (~> 1.0) method_source (~> 1.0)
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
mini_magick mini_magick (~> 4.10.1)
minitest (~> 5.11.0) minitest (~> 5.11.0)
multi_json (~> 1.14.1) multi_json (~> 1.14.1)
nakayoshi_fork (~> 0.0.4) nakayoshi_fork (~> 0.0.4)
@ -1419,10 +1426,10 @@ DEPENDENCIES
pg (~> 1.1) pg (~> 1.1)
png_quantizator (~> 0.2.1) png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3) premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.11.0) prometheus-client-mmap (~> 0.12.0)
pry-byebug (~> 3.9.0) pry-byebug (~> 3.9.0)
pry-rails (~> 0.3.9) pry-rails (~> 0.3.9)
rack (~> 2.0.9) rack (~> 2.1.4)
rack-attack (~> 6.3.0) rack-attack (~> 6.3.0)
rack-cors (~> 1.0.6) rack-cors (~> 1.0.6)
rack-oauth2 (~> 1.9.3) rack-oauth2 (~> 1.9.3)
@ -1444,7 +1451,7 @@ DEPENDENCIES
request_store (~> 1.5) request_store (~> 1.5)
responders (~> 3.0) responders (~> 3.0)
retriable (~> 3.1.2) retriable (~> 3.1.2)
rouge (~> 3.21.0) rouge (~> 3.24.0)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-parameterized rspec-parameterized
rspec-rails (~> 4.0.0) rspec-rails (~> 4.0.0)
@ -1473,7 +1480,7 @@ DEPENDENCIES
simple_po_parser (~> 1.1.2) simple_po_parser (~> 1.1.2)
simplecov (~> 0.18.5) simplecov (~> 0.18.5)
simplecov-cobertura (~> 1.3.1) simplecov-cobertura (~> 1.3.1)
slack-messenger (~> 2.3.3) slack-messenger (~> 2.3.4)
snowplow-tracker (~> 0.6.1) snowplow-tracker (~> 0.6.1)
spring (~> 2.0.0) spring (~> 2.0.0)
spring-commands-rspec (~> 1.0.4) spring-commands-rspec (~> 1.0.4)
@ -1482,13 +1489,13 @@ DEPENDENCIES
stackprof (~> 0.2.15) stackprof (~> 0.2.15)
state_machines-activerecord (~> 0.6.0) state_machines-activerecord (~> 0.6.0)
sys-filesystem (~> 1.1.6) sys-filesystem (~> 1.1.6)
terser (= 1.0.2)
test-prof (~> 0.12.0) test-prof (~> 0.12.0)
thin (~> 1.7.0) thin (~> 1.7.0)
timecop (~> 0.9.1) timecop (~> 0.9.1)
toml-rb (~> 1.0.0) toml-rb (~> 1.0.0)
truncato (~> 0.7.11) truncato (~> 0.7.11)
u2f (~> 0.2.1) u2f (~> 0.2.1)
uglifier (~> 2.7.2)
unf (~> 0.1.4) unf (~> 0.1.4)
unicorn (~> 5.5) unicorn (~> 5.5)
unicorn-worker-killer (~> 0.4.4) unicorn-worker-killer (~> 0.4.4)
@ -1498,8 +1505,7 @@ DEPENDENCIES
version_sorter (~> 2.2.4) version_sorter (~> 2.2.4)
vmstat (~> 2.3.0) vmstat (~> 2.3.0)
webauthn (~> 2.3) webauthn (~> 2.3)
webmock (~> 3.5.1) webmock (~> 3.9.1)
webpack-rails (~> 0.9.10)
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
yajl-ruby (~> 1.4.1) yajl-ruby (~> 1.4.1)

View file

@ -73,7 +73,7 @@ sensitive as to how you word things. Use Emoji to express your feelings (heart,
star, smile, etc.). Some good tips about code reviews can be found in our star, smile, etc.). Some good tips about code reviews can be found in our
[Code Review Guidelines]. [Code Review Guidelines].
[Code Review Guidelines]: https://docs.gitlab.com/ce/development/code_review.html [Code Review Guidelines]: https://docs.gitlab.com/ee/development/code_review.html
## Feature flags ## Feature flags
@ -217,5 +217,5 @@ rebase with master to see if that solves the issue.
[team]: https://about.gitlab.com/team/ [team]: https://about.gitlab.com/team/
[done]: https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#definition-of-done [done]: https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#definition-of-done
[automatic_ce_ee_merge]: https://docs.gitlab.com/ce/development/automatic_ce_ee_merge.html [automatic_ce_ee_merge]: https://docs.gitlab.com/ee/development/automatic_ce_ee_merge.html
[ee_features]: https://docs.gitlab.com/ce/development/ee_features.html [ee_features]: https://docs.gitlab.com/ee/development/ee_features.html

View file

@ -1 +1 @@
13.4.7 13.5.5

View file

@ -9,13 +9,13 @@ export default {
}, },
inject: { inject: {
svgPath: { svgPath: {
type: String, default: '',
}, },
docsLink: { docsLink: {
type: String, default: '',
}, },
primaryButtonPath: { primaryButtonPath: {
type: String, default: '',
}, },
}, },
}; };

View file

@ -10,16 +10,16 @@ export default {
}, },
inject: { inject: {
isAdmin: { isAdmin: {
type: Boolean, default: false,
}, },
svgPath: { svgPath: {
type: String, default: '',
}, },
docsLink: { docsLink: {
type: String, default: '',
}, },
primaryButtonPath: { primaryButtonPath: {
type: String, default: '',
}, },
}, },
}; };

View file

@ -1,13 +1,21 @@
// This allows us to dismiss alerts that we've migrated from bootstrap // This allows us to dismiss alerts and banners that we've migrated from bootstrap
// Note: This ONLY works on alerts that are created on page load // Note: This ONLY works on elements that are created on page load
// You can follow this effort in the following epic // You can follow this effort in the following epic
// https://gitlab.com/groups/gitlab-org/-/epics/4070 // https://gitlab.com/groups/gitlab-org/-/epics/4070
export default function initAlertHandler() { export default function initAlertHandler() {
const ALERT_SELECTOR = '.gl-alert'; const DISMISSIBLE_SELECTORS = ['.gl-alert', '.gl-banner'];
const CLOSE_SELECTOR = '.gl-alert-dismiss'; const DISMISS_LABEL = '[aria-label="Dismiss"]';
const DISMISS_CLASS = '.gl-alert-dismiss';
const dismissAlert = ({ target }) => target.closest(ALERT_SELECTOR).remove(); DISMISSIBLE_SELECTORS.forEach(selector => {
const closeButtons = document.querySelectorAll(`${ALERT_SELECTOR} ${CLOSE_SELECTOR}`); const elements = document.querySelectorAll(selector);
closeButtons.forEach(alert => alert.addEventListener('click', dismissAlert)); elements.forEach(element => {
const button = element.querySelector(DISMISS_LABEL) || element.querySelector(DISMISS_CLASS);
if (!button) {
return;
}
button.addEventListener('click', () => element.remove());
});
});
} }

View file

@ -1,16 +1,17 @@
<script> <script>
/* eslint-disable vue/no-v-html */
import * as Sentry from '@sentry/browser';
import { import {
GlAlert, GlAlert,
GlBadge, GlBadge,
GlIcon, GlIcon,
GlLink,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
GlTabs, GlTabs,
GlTab, GlTab,
GlButton, GlButton,
GlSafeHtmlDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import * as Sentry from '~/sentry/wrapper';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import alertQuery from '../graphql/queries/details.query.graphql'; import alertQuery from '../graphql/queries/details.query.graphql';
import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql'; import sidebarStatusQuery from '../graphql/queries/sidebar_status.query.graphql';
@ -28,6 +29,8 @@ import SystemNote from './system_notes/system_note.vue';
import AlertSidebar from './alert_sidebar.vue'; import AlertSidebar from './alert_sidebar.vue';
import AlertMetrics from './alert_metrics.vue'; import AlertMetrics from './alert_metrics.vue';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import AlertSummaryRow from './alert_summary_row.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
const containerEl = document.querySelector('.page-with-contextual-sidebar'); const containerEl = document.querySelector('.page-with-contextual-sidebar');
@ -39,6 +42,9 @@ export default {
reportedAt: s__('AlertManagement|Reported %{when}'), reportedAt: s__('AlertManagement|Reported %{when}'),
reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'), reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'),
}, },
directives: {
SafeHtml: GlSafeHtmlDirective,
},
severityLabels: ALERTS_SEVERITY_LABELS, severityLabels: ALERTS_SEVERITY_LABELS,
tabsConfig: [ tabsConfig: [
{ {
@ -56,9 +62,11 @@ export default {
], ],
components: { components: {
AlertDetailsTable, AlertDetailsTable,
AlertSummaryRow,
GlBadge, GlBadge,
GlAlert, GlAlert,
GlIcon, GlIcon,
GlLink,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
GlTab, GlTab,
@ -69,20 +77,18 @@ export default {
SystemNote, SystemNote,
AlertMetrics, AlertMetrics,
}, },
mixins: [glFeatureFlagsMixin()],
inject: { inject: {
projectPath: { projectPath: {
default: '', default: '',
}, },
alertId: { alertId: {
type: String,
default: '', default: '',
}, },
projectId: { projectId: {
type: String,
default: '', default: '',
}, },
projectIssuesPath: { projectIssuesPath: {
type: String,
default: '', default: '',
}, },
}, },
@ -143,6 +149,15 @@ export default {
this.$router.replace({ name: 'tab', params: { tabId } }); this.$router.replace({ name: 'tab', params: { tabId } });
}, },
}, },
environmentName() {
return this.shouldDisplayEnvironment && this.alert?.environment?.name;
},
environmentPath() {
return this.shouldDisplayEnvironment && this.alert?.environment?.path;
},
shouldDisplayEnvironment() {
return this.glFeatures.exposeEnvironmentPathInAlertDetails;
},
}, },
mounted() { mounted() {
this.trackPageViews(); this.trackPageViews();
@ -211,7 +226,7 @@ export default {
<template> <template>
<div> <div>
<gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError"> <gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError">
<p v-html="sidebarErrorMessage || $options.i18n.errorMsg"></p> <p v-safe-html="sidebarErrorMessage || $options.i18n.errorMsg"></p>
</gl-alert> </gl-alert>
<gl-alert <gl-alert
v-if="createIncidentError" v-if="createIncidentError"
@ -270,10 +285,9 @@ export default {
variant="default" variant="default"
class="d-sm-none gl-absolute toggle-sidebar-mobile-button" class="d-sm-none gl-absolute toggle-sidebar-mobile-button"
type="button" type="button"
icon="chevron-double-lg-left"
@click="toggleSidebar" @click="toggleSidebar"
> />
<i class="fa fa-angle-double-left"></i>
</gl-button>
</div> </div>
<div <div
v-if="alert" v-if="alert"
@ -283,54 +297,65 @@ export default {
</div> </div>
<gl-tabs v-if="alert" v-model="currentTabIndex" data-testid="alertDetailsTabs"> <gl-tabs v-if="alert" v-model="currentTabIndex" data-testid="alertDetailsTabs">
<gl-tab :data-testid="$options.tabsConfig[0].id" :title="$options.tabsConfig[0].title"> <gl-tab :data-testid="$options.tabsConfig[0].id" :title="$options.tabsConfig[0].title">
<div v-if="alert.severity" class="gl-mt-3 gl-mb-5 gl-display-flex"> <alert-summary-row v-if="alert.severity" :label="`${s__('AlertManagement|Severity')}:`">
<div class="gl-font-weight-bold gl-w-13 gl-text-right gl-pr-3"> <span data-testid="severity">
{{ s__('AlertManagement|Severity') }}: <gl-icon
</div> class="gl-vertical-align-middle"
<div class="gl-pl-2" data-testid="severity"> :size="12"
<span> :name="`severity-${alert.severity.toLowerCase()}`"
<gl-icon :class="`icon-${alert.severity.toLowerCase()}`"
class="gl-vertical-align-middle" />
:size="12"
:name="`severity-${alert.severity.toLowerCase()}`"
:class="`icon-${alert.severity.toLowerCase()}`"
/>
</span>
{{ $options.severityLabels[alert.severity] }} {{ $options.severityLabels[alert.severity] }}
</div> </span>
</div> </alert-summary-row>
<div v-if="alert.startedAt" class="gl-my-5 gl-display-flex"> <alert-summary-row
<div class="gl-font-weight-bold gl-w-13 gl-text-right gl-pr-3"> v-if="environmentName"
{{ s__('AlertManagement|Start time') }}: :label="`${s__('AlertManagement|Environment')}:`"
</div> >
<div class="gl-pl-2"> <gl-link
<time-ago-tooltip data-testid="startTimeItem" :time="alert.startedAt" /> v-if="environmentPath"
</div> class="gl-display-inline-block"
</div> data-testid="environmentPath"
<div v-if="alert.eventCount" class="gl-my-5 gl-display-flex"> :href="environmentPath"
<div class="gl-font-weight-bold gl-w-13 gl-text-right gl-pr-3"> >
{{ s__('AlertManagement|Events') }}: {{ environmentName }}
</div> </gl-link>
<div class="gl-pl-2" data-testid="eventCount">{{ alert.eventCount }}</div> <span v-else data-testid="environmentName">{{ environmentName }}</span>
</div> </alert-summary-row>
<div v-if="alert.monitoringTool" class="gl-my-5 gl-display-flex"> <alert-summary-row
<div class="gl-font-weight-bold gl-w-13 gl-text-right gl-pr-3"> v-if="alert.startedAt"
{{ s__('AlertManagement|Tool') }}: :label="`${s__('AlertManagement|Start time')}:`"
</div> >
<div class="gl-pl-2" data-testid="monitoringTool">{{ alert.monitoringTool }}</div> <time-ago-tooltip data-testid="startTimeItem" :time="alert.startedAt" />
</div> </alert-summary-row>
<div v-if="alert.service" class="gl-my-5 gl-display-flex"> <alert-summary-row
<div class="bold gl-w-13 gl-text-right gl-pr-3"> v-if="alert.eventCount"
{{ s__('AlertManagement|Service') }}: :label="`${s__('AlertManagement|Events')}:`"
</div> data-testid="eventCount"
<div class="gl-pl-2" data-testid="service">{{ alert.service }}</div> >
</div> {{ alert.eventCount }}
<div v-if="alert.runbook" class="gl-my-5 gl-display-flex"> </alert-summary-row>
<div class="bold gl-w-13 gl-text-right gl-pr-3"> <alert-summary-row
{{ s__('AlertManagement|Runbook') }}: v-if="alert.monitoringTool"
</div> :label="`${s__('AlertManagement|Tool')}:`"
<div class="gl-pl-2" data-testid="runbook">{{ alert.runbook }}</div> data-testid="monitoringTool"
</div> >
{{ alert.monitoringTool }}
</alert-summary-row>
<alert-summary-row
v-if="alert.service"
:label="`${s__('AlertManagement|Service')}:`"
data-testid="service"
>
{{ alert.service }}
</alert-summary-row>
<alert-summary-row
v-if="alert.runbook"
:label="`${s__('AlertManagement|Runbook')}:`"
data-testid="runbook"
>
{{ alert.runbook }}
</alert-summary-row>
<alert-details-table :alert="alert" :loading="loading" /> <alert-details-table :alert="alert" :loading="loading" />
</gl-tab> </gl-tab>
<gl-tab :data-testid="$options.tabsConfig[1].id" :title="$options.tabsConfig[1].title"> <gl-tab :data-testid="$options.tabsConfig[1].id" :title="$options.tabsConfig[1].title">

View file

@ -33,30 +33,13 @@ export default {
query: alertsHelpUrlQuery, query: alertsHelpUrlQuery,
}, },
}, },
props: { inject: [
enableAlertManagementPath: { 'enableAlertManagementPath',
type: String, 'userCanEnableAlertManagement',
required: true, 'emptyAlertSvgPath',
}, 'opsgenieMvcEnabled',
userCanEnableAlertManagement: { 'opsgenieMvcTargetUrl',
type: Boolean, ],
required: true,
},
emptyAlertSvgPath: {
type: String,
required: true,
},
opsgenieMvcEnabled: {
type: Boolean,
required: false,
default: false,
},
opsgenieMvcTargetUrl: {
type: String,
required: false,
default: '',
},
},
data() { data() {
return { return {
alertsHelpUrl: '', alertsHelpUrl: '',

View file

@ -1,6 +1,4 @@
<script> <script>
import Tracking from '~/tracking';
import { trackAlertListViewsOptions } from '../constants';
import AlertManagementEmptyState from './alert_management_empty_state.vue'; import AlertManagementEmptyState from './alert_management_empty_state.vue';
import AlertManagementTable from './alert_management_table.vue'; import AlertManagementTable from './alert_management_table.vue';
@ -9,67 +7,12 @@ export default {
AlertManagementEmptyState, AlertManagementEmptyState,
AlertManagementTable, AlertManagementTable,
}, },
props: { inject: ['alertManagementEnabled'],
projectPath: {
type: String,
required: true,
},
alertManagementEnabled: {
type: Boolean,
required: true,
},
enableAlertManagementPath: {
type: String,
required: true,
},
populatingAlertsHelpUrl: {
type: String,
required: true,
},
userCanEnableAlertManagement: {
type: Boolean,
required: true,
},
emptyAlertSvgPath: {
type: String,
required: true,
},
opsgenieMvcEnabled: {
type: Boolean,
required: false,
default: false,
},
opsgenieMvcTargetUrl: {
type: String,
required: false,
default: '',
},
},
mounted() {
this.trackPageViews();
},
methods: {
trackPageViews() {
const { category, action } = trackAlertListViewsOptions;
Tracking.event(category, action);
},
},
}; };
</script> </script>
<template> <template>
<div> <div>
<alert-management-table <alert-management-table v-if="alertManagementEnabled" />
v-if="alertManagementEnabled" <alert-management-empty-state v-else />
:populating-alerts-help-url="populatingAlertsHelpUrl"
:project-path="projectPath"
/>
<alert-management-empty-state
v-else
:empty-alert-svg-path="emptyAlertSvgPath"
:enable-alert-management-path="enableAlertManagementPath"
:user-can-enable-alert-management="userCanEnableAlertManagement"
:opsgenie-mvc-enabled="opsgenieMvcEnabled"
:opsgenie-mvc-target-url="opsgenieMvcTargetUrl"
/>
</div> </div>
</template> </template>

View file

@ -1,58 +1,43 @@
<script> <script>
/* eslint-disable vue/no-v-html */
import { import {
GlAlert,
GlLoadingIcon, GlLoadingIcon,
GlTable, GlTable,
GlAlert,
GlAvatarsInline, GlAvatarsInline,
GlAvatarLink, GlAvatarLink,
GlAvatar, GlAvatar,
GlIcon, GlIcon,
GlLink, GlLink,
GlTabs,
GlTab,
GlBadge,
GlPagination,
GlSearchBoxByType,
GlSprintf, GlSprintf,
GlTooltipDirective, GlTooltipDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { debounce, trim } from 'lodash'; import { s__, __ } from '~/locale';
import { __, s__ } from '~/locale';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
import { fetchPolicies } from '~/lib/graphql'; import { fetchPolicies } from '~/lib/graphql';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
import PaginatedTableWithSearchAndTabs from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue';
import {
tdClass,
thClass,
bodyTrClass,
initialPaginationState,
} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { convertToSnakeCase } from '~/lib/utils/text_utility'; import { convertToSnakeCase } from '~/lib/utils/text_utility';
import Tracking from '~/tracking';
import getAlerts from '../graphql/queries/get_alerts.query.graphql'; import getAlerts from '../graphql/queries/get_alerts.query.graphql';
import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql'; import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql';
import { import {
ALERTS_STATUS_TABS, ALERTS_STATUS_TABS,
ALERTS_SEVERITY_LABELS, ALERTS_SEVERITY_LABELS,
DEFAULT_PAGE_SIZE,
trackAlertListViewsOptions, trackAlertListViewsOptions,
trackAlertStatusUpdateOptions,
} from '../constants'; } from '../constants';
import AlertStatus from './alert_status.vue'; import AlertStatus from './alert_status.vue';
const tdClass =
'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap';
const thClass = 'gl-hover-bg-blue-50';
const bodyTrClass =
'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-bg-blue-50 gl-hover-cursor-pointer gl-hover-border-b-solid gl-hover-border-blue-200';
const TH_TEST_ID = { 'data-testid': 'alert-management-severity-sort' }; const TH_TEST_ID = { 'data-testid': 'alert-management-severity-sort' };
const initialPaginationState = {
currentPage: 1,
prevPageCursor: '',
nextPageCursor: '',
firstPageSize: DEFAULT_PAGE_SIZE,
lastPageSize: null,
};
const TWELVE_HOURS_IN_MS = 12 * 60 * 60 * 1000; const TWELVE_HOURS_IN_MS = 12 * 60 * 60 * 1000;
export default { export default {
trackAlertListViewsOptions,
i18n: { i18n: {
noAlertsMsg: s__( noAlertsMsg: s__(
'AlertManagement|No alerts available to display. See %{linkStart}enabling alert management%{linkEnd} for more information on adding alerts to the list.', 'AlertManagement|No alerts available to display. See %{linkStart}enabling alert management%{linkEnd} for more information on adding alerts to the list.',
@ -60,7 +45,6 @@ export default {
errorMsg: s__( errorMsg: s__(
"AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear.", "AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear.",
), ),
searchPlaceholder: __('Search or filter results...'),
unassigned: __('Unassigned'), unassigned: __('Unassigned'),
}, },
fields: [ fields: [
@ -94,7 +78,7 @@ export default {
}, },
{ {
key: 'issue', key: 'issue',
label: s__('AlertManagement|Issue'), label: s__('AlertManagement|Incident'),
thClass: 'gl-w-12 gl-pointer-events-none', thClass: 'gl-w-12 gl-pointer-events-none',
tdClass, tdClass,
}, },
@ -115,36 +99,23 @@ export default {
severityLabels: ALERTS_SEVERITY_LABELS, severityLabels: ALERTS_SEVERITY_LABELS,
statusTabs: ALERTS_STATUS_TABS, statusTabs: ALERTS_STATUS_TABS,
components: { components: {
GlAlert,
GlLoadingIcon, GlLoadingIcon,
GlTable, GlTable,
GlAlert,
GlAvatarsInline, GlAvatarsInline,
GlAvatarLink, GlAvatarLink,
GlAvatar, GlAvatar,
TimeAgo, TimeAgo,
GlIcon, GlIcon,
GlLink, GlLink,
GlTabs,
GlTab,
GlBadge,
GlPagination,
GlSearchBoxByType,
GlSprintf, GlSprintf,
AlertStatus, AlertStatus,
PaginatedTableWithSearchAndTabs,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
props: { inject: ['projectPath', 'textQuery', 'assigneeUsernameQuery', 'populatingAlertsHelpUrl'],
projectPath: {
type: String,
required: true,
},
populatingAlertsHelpUrl: {
type: String,
required: true,
},
},
apollo: { apollo: {
alerts: { alerts: {
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
@ -152,6 +123,7 @@ export default {
variables() { variables() {
return { return {
searchTerm: this.searchTerm, searchTerm: this.searchTerm,
assigneeUsername: this.assigneeUsername,
projectPath: this.projectPath, projectPath: this.projectPath,
statuses: this.statusFilter, statuses: this.statusFilter,
sort: this.sort, sort: this.sort,
@ -182,14 +154,16 @@ export default {
}; };
}, },
error() { error() {
this.hasError = true; this.errored = true;
}, },
}, },
alertsCount: { alertsCount: {
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
query: getAlertsCountByStatus, query: getAlertsCountByStatus,
variables() { variables() {
return { return {
searchTerm: this.searchTerm, searchTerm: this.searchTerm,
assigneeUsername: this.assigneeUsername,
projectPath: this.projectPath, projectPath: this.projectPath,
}; };
}, },
@ -200,83 +174,53 @@ export default {
}, },
data() { data() {
return { return {
searchTerm: '', errored: false,
hasError: false, serverErrorMessage: '',
errorMessage: '', isErrorAlertDismissed: false,
isAlertDismissed: false,
sort: 'STARTED_AT_DESC', sort: 'STARTED_AT_DESC',
statusFilter: [], statusFilter: [],
filteredByStatus: '', filteredByStatus: '',
pagination: initialPaginationState, alerts: {},
alertsCount: {},
sortBy: 'startedAt', sortBy: 'startedAt',
sortDesc: true, sortDesc: true,
sortDirection: 'desc', sortDirection: 'desc',
searchTerm: this.textQuery,
assigneeUsername: this.assigneeUsernameQuery,
pagination: initialPaginationState,
}; };
}, },
computed: { computed: {
showErrorMsg() {
return this.errored && !this.isErrorAlertDismissed;
},
showNoAlertsMsg() { showNoAlertsMsg() {
return ( return (
!this.hasError && !this.errored &&
!this.loading && !this.loading &&
this.alertsCount?.all === 0 && this.alertsCount?.all === 0 &&
!this.searchTerm && !this.searchTerm &&
!this.isAlertDismissed !this.assigneeUsername &&
!this.isErrorAlertDismissed
); );
}, },
loading() { loading() {
return this.$apollo.queries.alerts.loading; return this.$apollo.queries.alerts.loading;
}, },
hasAlerts() { isEmpty() {
return this.alerts?.list?.length; return !this.alerts?.list?.length;
}, },
showPaginationControls() {
return Boolean(this.prevPage || this.nextPage);
},
alertsForCurrentTab() {
return this.alertsCount ? this.alertsCount[this.filteredByStatus.toLowerCase()] : 0;
},
prevPage() {
return Math.max(this.pagination.currentPage - 1, 0);
},
nextPage() {
const nextPage = this.pagination.currentPage + 1;
return nextPage > Math.ceil(this.alertsForCurrentTab / DEFAULT_PAGE_SIZE) ? null : nextPage;
},
},
mounted() {
this.trackPageViews();
}, },
methods: { methods: {
filterAlertsByStatus(tabIndex) {
this.resetPagination();
const { filters, status } = this.$options.statusTabs[tabIndex];
this.statusFilter = filters;
this.filteredByStatus = status;
},
fetchSortedData({ sortBy, sortDesc }) { fetchSortedData({ sortBy, sortDesc }) {
const sortingDirection = sortDesc ? 'DESC' : 'ASC'; const sortingDirection = sortDesc ? 'DESC' : 'ASC';
const sortingColumn = convertToSnakeCase(sortBy).toUpperCase(); const sortingColumn = convertToSnakeCase(sortBy).toUpperCase();
this.resetPagination(); this.pagination = initialPaginationState;
this.sort = `${sortingColumn}_${sortingDirection}`; this.sort = `${sortingColumn}_${sortingDirection}`;
}, },
onInputChange: debounce(function debounceSearch(input) { navigateToAlertDetails({ iid }, index, { metaKey }) {
const trimmedInput = trim(input); return visitUrl(joinPaths(window.location.pathname, iid, 'details'), metaKey);
if (trimmedInput !== this.searchTerm) {
this.resetPagination();
this.searchTerm = trimmedInput;
}
}, 500),
navigateToAlertDetails({ iid }) {
return visitUrl(joinPaths(window.location.pathname, iid, 'details'));
},
trackPageViews() {
const { category, action } = trackAlertListViewsOptions;
Tracking.event(category, action);
},
trackStatusUpdate(status) {
const { category, action, label } = trackAlertStatusUpdateOptions;
Tracking.event(category, action, { label, property: status });
}, },
hasAssignees(assignees) { hasAssignees(assignees) {
return Boolean(assignees.nodes?.length); return Boolean(assignees.nodes?.length);
@ -284,204 +228,180 @@ export default {
getIssueLink(item) { getIssueLink(item) {
return joinPaths('/', this.projectPath, '-', 'issues', item.issueIid); return joinPaths('/', this.projectPath, '-', 'issues', item.issueIid);
}, },
handlePageChange(page) {
const { startCursor, endCursor } = this.alerts.pageInfo;
if (page > this.pagination.currentPage) {
this.pagination = {
...initialPaginationState,
nextPageCursor: endCursor,
currentPage: page,
};
} else {
this.pagination = {
lastPageSize: DEFAULT_PAGE_SIZE,
firstPageSize: null,
prevPageCursor: startCursor,
nextPageCursor: '',
currentPage: page,
};
}
},
resetPagination() {
this.pagination = initialPaginationState;
},
tbodyTrClass(item) { tbodyTrClass(item) {
return { return {
[bodyTrClass]: !this.loading && this.hasAlerts, [bodyTrClass]: !this.loading && !this.isEmpty,
'new-alert': item?.isNew, 'new-alert': item?.isNew,
}; };
}, },
handleAlertError(errorMessage) { handleAlertError(errorMessage) {
this.hasError = true; this.errored = true;
this.errorMessage = errorMessage; this.serverErrorMessage = errorMessage;
}, },
dismissError() { handleStatusUpdate() {
this.hasError = false; this.$apollo.queries.alerts.refetch();
this.errorMessage = ''; this.$apollo.queries.alertsCount.refetch();
},
pageChanged(pagination) {
this.pagination = pagination;
},
statusChanged({ filters, status }) {
this.statusFilter = filters;
this.filteredByStatus = status;
},
filtersChanged({ searchTerm, assigneeUsername }) {
this.searchTerm = searchTerm;
this.assigneeUsername = assigneeUsername;
},
errorAlertDismissed() {
this.errored = false;
this.serverErrorMessage = '';
this.isErrorAlertDismissed = true;
}, },
}, },
}; };
</script> </script>
<template> <template>
<div> <div>
<div class="incident-management-list"> <gl-alert v-if="showNoAlertsMsg" @dismiss="errorAlertDismissed">
<gl-alert v-if="showNoAlertsMsg" @dismiss="isAlertDismissed = true"> <gl-sprintf :message="$options.i18n.noAlertsMsg">
<gl-sprintf :message="$options.i18n.noAlertsMsg"> <template #link="{ content }">
<template #link="{ content }"> <gl-link class="gl-display-inline-block" :href="populatingAlertsHelpUrl" target="_blank">
<gl-link {{ content }}
class="gl-display-inline-block"
:href="populatingAlertsHelpUrl"
target="_blank"
>
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<gl-alert v-if="hasError" variant="danger" data-testid="alert-error" @dismiss="dismissError">
<p v-html="errorMessage || $options.i18n.errorMsg"></p>
</gl-alert>
<gl-tabs
content-class="gl-p-0 gl-border-b-solid gl-border-b-1 gl-border-gray-100"
@input="filterAlertsByStatus"
>
<gl-tab v-for="tab in $options.statusTabs" :key="tab.status">
<template slot="title">
<span>{{ tab.title }}</span>
<gl-badge v-if="alertsCount" pill size="sm" class="gl-tab-counter-badge">
{{ alertsCount[tab.status.toLowerCase()] }}
</gl-badge>
</template>
</gl-tab>
</gl-tabs>
<div class="gl-bg-gray-10 gl-p-5 gl-border-b-solid gl-border-b-1 gl-border-gray-100">
<gl-search-box-by-type
class="gl-bg-white"
:placeholder="$options.i18n.searchPlaceholder"
@input="onInputChange"
/>
</div>
<h4 class="d-block d-md-none my-3">
{{ s__('AlertManagement|Alerts') }}
</h4>
<gl-table
class="alert-management-table"
:items="alerts ? alerts.list : []"
:fields="$options.fields"
:show-empty="true"
:busy="loading"
stacked="md"
:tbody-tr-class="tbodyTrClass"
:no-local-sorting="true"
:sort-direction="sortDirection"
:sort-desc.sync="sortDesc"
:sort-by.sync="sortBy"
sort-icon-left
fixed
@row-clicked="navigateToAlertDetails"
@sort-changed="fetchSortedData"
>
<template #cell(severity)="{ item }">
<div
class="d-inline-flex align-items-center justify-content-between"
data-testid="severityField"
>
<gl-icon
class="mr-2"
:size="12"
:name="`severity-${item.severity.toLowerCase()}`"
:class="`icon-${item.severity.toLowerCase()}`"
/>
{{ $options.severityLabels[item.severity] }}
</div>
</template>
<template #cell(startedAt)="{ item }">
<time-ago v-if="item.startedAt" :time="item.startedAt" />
</template>
<template #cell(eventCount)="{ item }">
{{ item.eventCount }}
</template>
<template #cell(alertLabel)="{ item }">
<div
class="gl-max-w-full text-truncate"
:title="`${item.iid} - ${item.title}`"
data-testid="idField"
>
#{{ item.iid }} {{ item.title }}
</div>
</template>
<template #cell(issue)="{ item }">
<gl-link v-if="item.issueIid" data-testid="issueField" :href="getIssueLink(item)">
#{{ item.issueIid }}
</gl-link> </gl-link>
<div v-else data-testid="issueField">{{ s__('AlertManagement|None') }}</div>
</template> </template>
</gl-sprintf>
</gl-alert>
<template #cell(assignees)="{ item }"> <paginated-table-with-search-and-tabs
<div data-testid="assigneesField"> :show-error-msg="showErrorMsg"
<template v-if="hasAssignees(item.assignees)"> :i18n="$options.i18n"
<gl-avatars-inline :items="alerts.list || []"
:avatars="item.assignees.nodes" :page-info="alerts.pageInfo"
:collapsed="true" :items-count="alertsCount"
:max-visible="4" :status-tabs="$options.statusTabs"
:avatar-size="24" :track-views-options="$options.trackAlertListViewsOptions"
badge-tooltip-prop="name" :server-error-message="serverErrorMessage"
:badge-tooltip-max-chars="100" :filter-search-tokens="['assignee_username']"
> filter-search-key="alerts"
<template #avatar="{ avatar }"> @page-changed="pageChanged"
<gl-avatar-link @tabs-changed="statusChanged"
:key="avatar.username" @filters-changed="filtersChanged"
v-gl-tooltip @error-alert-dismissed="errorAlertDismissed"
target="_blank" >
:href="avatar.webUrl" <template #header-actions></template>
:title="avatar.name"
>
<gl-avatar :src="avatar.avatarUrl" :label="avatar.name" :size="24" />
</gl-avatar-link>
</template>
</gl-avatars-inline>
</template>
<template v-else>
{{ $options.i18n.unassigned }}
</template>
</div>
</template>
<template #cell(status)="{ item }"> <template #title>
<alert-status {{ s__('AlertManagement|Alerts') }}
:alert="item" </template>
:project-path="projectPath"
:is-sidebar="false"
@alert-error="handleAlertError"
/>
</template>
<template #empty> <template #table>
{{ s__('AlertManagement|No alerts to display.') }} <gl-table
</template> class="alert-management-table"
:items="alerts ? alerts.list : []"
:fields="$options.fields"
:show-empty="true"
:busy="loading"
stacked="md"
:tbody-tr-class="tbodyTrClass"
:no-local-sorting="true"
:sort-direction="sortDirection"
:sort-desc.sync="sortDesc"
:sort-by.sync="sortBy"
sort-icon-left
fixed
@row-clicked="navigateToAlertDetails"
@sort-changed="fetchSortedData"
>
<template #cell(severity)="{ item }">
<div
class="d-inline-flex align-items-center justify-content-between"
data-testid="severityField"
>
<gl-icon
class="mr-2"
:size="12"
:name="`severity-${item.severity.toLowerCase()}`"
:class="`icon-${item.severity.toLowerCase()}`"
/>
{{ $options.severityLabels[item.severity] }}
</div>
</template>
<template #table-busy> <template #cell(startedAt)="{ item }">
<gl-loading-icon size="lg" color="dark" class="mt-3" /> <time-ago v-if="item.startedAt" :time="item.startedAt" />
</template> </template>
</gl-table>
<gl-pagination <template #cell(eventCount)="{ item }">
v-if="showPaginationControls" {{ item.eventCount }}
:value="pagination.currentPage" </template>
:prev-page="prevPage"
:next-page="nextPage" <template #cell(alertLabel)="{ item }">
align="center" <div
class="gl-pagination gl-mt-3" class="gl-max-w-full text-truncate"
@input="handlePageChange" :title="`${item.iid} - ${item.title}`"
/> data-testid="idField"
</div> >
#{{ item.iid }} {{ item.title }}
</div>
</template>
<template #cell(issue)="{ item }">
<gl-link v-if="item.issueIid" data-testid="issueField" :href="getIssueLink(item)">
#{{ item.issueIid }}
</gl-link>
<div v-else data-testid="issueField">{{ s__('AlertManagement|None') }}</div>
</template>
<template #cell(assignees)="{ item }">
<div data-testid="assigneesField">
<template v-if="hasAssignees(item.assignees)">
<gl-avatars-inline
:avatars="item.assignees.nodes"
:collapsed="true"
:max-visible="4"
:avatar-size="24"
badge-tooltip-prop="name"
:badge-tooltip-max-chars="100"
>
<template #avatar="{ avatar }">
<gl-avatar-link
:key="avatar.username"
v-gl-tooltip
target="_blank"
:href="avatar.webUrl"
:title="avatar.name"
>
<gl-avatar :src="avatar.avatarUrl" :label="avatar.name" :size="24" />
</gl-avatar-link>
</template>
</gl-avatars-inline>
</template>
<template v-else>
{{ $options.i18n.unassigned }}
</template>
</div>
</template>
<template #cell(status)="{ item }">
<alert-status
:alert="item"
:project-path="projectPath"
:is-sidebar="false"
@alert-error="handleAlertError"
@hide-dropdown="handleStatusUpdate"
/>
</template>
<template #empty>
{{ s__('AlertManagement|No alerts to display.') }}
</template>
<template #table-busy>
<gl-loading-icon size="lg" color="dark" class="mt-3" />
</template>
</gl-table>
</template>
</paginated-table-with-search-and-tabs>
</div> </div>
</template> </template>

View file

@ -1,7 +1,7 @@
<script> <script>
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import * as Sentry from '@sentry/browser'; import * as Sentry from '~/sentry/wrapper';
Vue.use(Vuex); Vue.use(Vuex);

View file

@ -18,7 +18,6 @@ export default {
default: '', default: '',
}, },
projectId: { projectId: {
type: String,
default: '', default: '',
}, },
}, },

View file

@ -1,9 +1,9 @@
<script> <script>
import { GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlButton } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { trackAlertStatusUpdateOptions } from '../constants'; import { trackAlertStatusUpdateOptions } from '../constants';
import updateAlertStatus from '../graphql/mutations/update_alert_status.mutation.graphql'; import updateAlertStatusMutation from '../graphql/mutations/update_alert_status.mutation.graphql';
export default { export default {
i18n: { i18n: {
@ -18,9 +18,8 @@ export default {
RESOLVED: s__('AlertManagement|Resolved'), RESOLVED: s__('AlertManagement|Resolved'),
}, },
components: { components: {
GlDeprecatedDropdown, GlDropdown,
GlDeprecatedDropdownItem, GlDropdownItem,
GlButton,
}, },
props: { props: {
projectPath: { projectPath: {
@ -51,7 +50,7 @@ export default {
this.$emit('handle-updating', true); this.$emit('handle-updating', true);
this.$apollo this.$apollo
.mutate({ .mutate({
mutation: updateAlertStatus, mutation: updateAlertStatusMutation,
variables: { variables: {
iid: this.alert.iid, iid: this.alert.iid,
status: status.toUpperCase(), status: status.toUpperCase(),
@ -60,8 +59,6 @@ export default {
}) })
.then(resp => { .then(resp => {
this.trackStatusUpdate(status); this.trackStatusUpdate(status);
this.$emit('hide-dropdown');
const errors = resp.data?.updateAlertStatus?.errors || []; const errors = resp.data?.updateAlertStatus?.errors || [];
if (errors[0]) { if (errors[0]) {
@ -70,6 +67,8 @@ export default {
`${this.$options.i18n.UPDATE_ALERT_STATUS_ERROR} ${errors[0]}`, `${this.$options.i18n.UPDATE_ALERT_STATUS_ERROR} ${errors[0]}`,
); );
} }
this.$emit('hide-dropdown');
}) })
.catch(() => { .catch(() => {
this.$emit( this.$emit(
@ -91,39 +90,30 @@ export default {
<template> <template>
<div class="dropdown dropdown-menu-selectable" :class="dropdownClass"> <div class="dropdown dropdown-menu-selectable" :class="dropdownClass">
<gl-deprecated-dropdown <gl-dropdown
ref="dropdown" ref="dropdown"
right right
:text="$options.statuses[alert.status]" :text="$options.statuses[alert.status]"
class="w-100" class="w-100"
toggle-class="dropdown-menu-toggle" toggle-class="dropdown-menu-toggle"
variant="outline-default"
@keydown.esc.native="$emit('hide-dropdown')" @keydown.esc.native="$emit('hide-dropdown')"
@hide="$emit('hide-dropdown')" @hide="$emit('hide-dropdown')"
> >
<div v-if="isSidebar" class="dropdown-title gl-display-flex"> <p v-if="isSidebar" class="gl-new-dropdown-header-top" data-testid="dropdown-header">
<span class="alert-title gl-ml-auto">{{ s__('AlertManagement|Assign status') }}</span> {{ s__('AlertManagement|Assign status') }}
<gl-button </p>
:aria-label="__('Close')"
variant="link"
class="dropdown-title-button dropdown-menu-close gl-ml-auto gl-text-black-normal!"
icon="close"
@click="$emit('hide-dropdown')"
/>
</div>
<div class="dropdown-content dropdown-body"> <div class="dropdown-content dropdown-body">
<gl-deprecated-dropdown-item <gl-dropdown-item
v-for="(label, field) in $options.statuses" v-for="(label, field) in $options.statuses"
:key="field" :key="field"
data-testid="statusDropdownItem" data-testid="statusDropdownItem"
class="gl-vertical-align-middle"
:active="label.toUpperCase() === alert.status" :active="label.toUpperCase() === alert.status"
:active-class="'is-active'" :active-class="'is-active'"
@click="updateAlertStatus(label)" @click="updateAlertStatus(label)"
> >
{{ label }} {{ label }}
</gl-deprecated-dropdown-item> </gl-dropdown-item>
</div> </div>
</gl-deprecated-dropdown> </gl-dropdown>
</div> </div>
</template> </template>

View file

@ -0,0 +1,18 @@
<script>
export default {
props: {
label: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="gl-my-5 gl-display-flex">
<div class="gl-font-weight-bold gl-w-13 gl-text-right gl-pr-3">{{ label }}</div>
<div class="gl-pl-2">
<slot></slot>
</div>
</div>
</template>

View file

@ -1,9 +1,9 @@
<script> <script>
import { GlDeprecatedDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
export default { export default {
components: { components: {
GlDeprecatedDropdownItem, GlDropdownItem,
}, },
props: { props: {
user: { user: {
@ -24,7 +24,7 @@ export default {
</script> </script>
<template> <template>
<gl-deprecated-dropdown-item <gl-dropdown-item
:key="user.username" :key="user.username"
data-testid="assigneeDropdownItem" data-testid="assigneeDropdownItem"
class="assignee-dropdown-item gl-vertical-align-middle" class="assignee-dropdown-item gl-vertical-align-middle"
@ -47,5 +47,5 @@ export default {
</strong> </strong>
<span class="dropdown-menu-user-username"> {{ user.username }}</span> <span class="dropdown-menu-user-username"> {{ user.username }}</span>
</span> </span>
</gl-deprecated-dropdown-item> </gl-dropdown-item>
</template> </template>

View file

@ -1,10 +1,11 @@
<script> <script>
import { import {
GlIcon, GlIcon,
GlDeprecatedDropdown, GlDropdown,
GlDeprecatedDropdownDivider, GlDropdownDivider,
GlDeprecatedDropdownHeader, GlDropdownSectionHeader,
GlDeprecatedDropdownItem, GlDropdownItem,
GlSearchBoxByType,
GlLoadingIcon, GlLoadingIcon,
GlTooltip, GlTooltip,
GlButton, GlButton,
@ -33,10 +34,11 @@ export default {
}, },
components: { components: {
GlIcon, GlIcon,
GlDeprecatedDropdown, GlDropdown,
GlDeprecatedDropdownItem, GlDropdownItem,
GlDeprecatedDropdownDivider, GlDropdownDivider,
GlDeprecatedDropdownHeader, GlDropdownSectionHeader,
GlSearchBoxByType,
GlLoadingIcon, GlLoadingIcon,
GlTooltip, GlTooltip,
GlButton, GlButton,
@ -216,48 +218,32 @@ export default {
</p> </p>
<div class="dropdown dropdown-menu-selectable" :class="dropdownClass"> <div class="dropdown dropdown-menu-selectable" :class="dropdownClass">
<gl-deprecated-dropdown <gl-dropdown
ref="dropdown" ref="dropdown"
:text="userName" :text="userName"
class="w-100" class="w-100"
toggle-class="dropdown-menu-toggle" toggle-class="dropdown-menu-toggle"
variant="outline-default"
@keydown.esc.native="hideDropdown" @keydown.esc.native="hideDropdown"
@hide="hideDropdown" @hide="hideDropdown"
> >
<div class="dropdown-title gl-display-flex"> <p class="gl-new-dropdown-header-top">
<span class="alert-title gl-ml-auto">{{ __('Assign To') }}</span> {{ __('Assign To') }}
<gl-button </p>
:aria-label="__('Close')" <gl-search-box-by-type v-model.trim="search" :placeholder="__('Search users')" />
variant="link"
class="dropdown-title-button dropdown-menu-close gl-ml-auto gl-text-black-normal!"
icon="close"
@click="hideDropdown"
/>
</div>
<div class="dropdown-input">
<input
v-model.trim="search"
class="dropdown-input-field"
type="search"
:placeholder="__('Search users')"
/>
<gl-icon name="search" class="dropdown-input-search ic-search" data-hidden="true" />
</div>
<div class="dropdown-content dropdown-body"> <div class="dropdown-content dropdown-body">
<template v-if="userListValid"> <template v-if="userListValid">
<gl-deprecated-dropdown-item <gl-dropdown-item
:active="!userName" :active="!userName"
active-class="is-active" active-class="is-active"
@click="updateAlertAssignees('')" @click="updateAlertAssignees('')"
> >
{{ __('Unassigned') }} {{ __('Unassigned') }}
</gl-deprecated-dropdown-item> </gl-dropdown-item>
<gl-deprecated-dropdown-divider /> <gl-dropdown-divider />
<gl-deprecated-dropdown-header class="mt-0"> <gl-dropdown-section-header>
{{ __('Assignee') }} {{ __('Assignee') }}
</gl-deprecated-dropdown-header> </gl-dropdown-section-header>
<sidebar-assignee <sidebar-assignee
v-for="user in sortedUsers" v-for="user in sortedUsers"
:key="user.username" :key="user.username"
@ -266,12 +252,12 @@ export default {
@update-alert-assignees="updateAlertAssignees" @update-alert-assignees="updateAlertAssignees"
/> />
</template> </template>
<gl-deprecated-dropdown-item v-else-if="userListEmpty"> <p v-else-if="userListEmpty" class="mx-3 my-2">
{{ __('No Matching Results') }} {{ __('No Matching Results') }}
</gl-deprecated-dropdown-item> </p>
<gl-loading-icon v-else /> <gl-loading-icon v-else />
</div> </div>
</gl-deprecated-dropdown> </gl-dropdown>
</div> </div>
<gl-loading-icon v-if="isUpdating" :inline="true" /> <gl-loading-icon v-if="isUpdating" :inline="true" />

View file

@ -1,11 +1,12 @@
<script> <script>
/* eslint-disable vue/no-v-html */ /* eslint-disable vue/no-v-html */
import { GlIcon } from '@gitlab/ui';
import NoteHeader from '~/notes/components/note_header.vue'; import NoteHeader from '~/notes/components/note_header.vue';
import { spriteIcon } from '~/lib/utils/common_utils';
export default { export default {
components: { components: {
NoteHeader, NoteHeader,
GlIcon,
}, },
props: { props: {
note: { note: {
@ -24,23 +25,23 @@ export default {
} = this.note; } = this.note;
return { ...author, id: id?.split('/').pop() }; return { ...author, id: id?.split('/').pop() };
}, },
iconHtml() {
return spriteIcon(this.note?.systemNoteIconName);
},
}, },
}; };
</script> </script>
<template> <template>
<li :id="noteAnchorId" class="timeline-entry note system-note note-wrapper gl-px-0!"> <li :id="noteAnchorId" class="timeline-entry note system-note note-wrapper gl-p-0!">
<div class="timeline-entry-inner"> <div class="gl-display-inline-flex gl-align-items-center">
<div class="timeline-icon" v-html="iconHtml"></div> <div
<div class="timeline-content"> class="gl-display-inline gl-bg-white gl-text-gray-200 gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-full gl-box-sizing-content-box gl-p-3 gl-mt-n2 gl-mr-6"
<div class="note-header"> >
<note-header :author="noteAuthor" :created-at="note.createdAt" :note-id="note.id"> <gl-icon :name="note.systemNoteIconName" />
<span v-html="note.bodyHtml"></span> </div>
</note-header>
</div> <div class="note-header">
<note-header :author="noteAuthor" :created-at="note.createdAt" :note-id="note.id">
<span v-html="note.bodyHtml"></span>
</note-header>
</div> </div>
</div> </div>
</li> </li>

View file

@ -63,5 +63,3 @@ export const trackAlertStatusUpdateOptions = {
action: 'update_alert_status', action: 'update_alert_status',
label: 'Status', label: 'Status',
}; };
export const DEFAULT_PAGE_SIZE = 20;

View file

@ -1,11 +1,11 @@
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import produce from 'immer';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import produce from 'immer';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import createRouter from './router';
import AlertDetails from './components/alert_details.vue'; import AlertDetails from './components/alert_details.vue';
import sidebarStatusQuery from './graphql/queries/sidebar_status.query.graphql'; import sidebarStatusQuery from './graphql/queries/sidebar_status.query.graphql';
import createRouter from './router';
Vue.use(VueApollo); Vue.use(VueApollo);

View file

@ -10,6 +10,11 @@ fragment AlertDetailItem on AlertManagementAlert {
description description
updatedAt updatedAt
endedAt endedAt
hosts
environment {
name
path
}
details details
runbook runbook
todos { todos {

View file

@ -1,7 +1,6 @@
#import "../fragments/list_item.fragment.graphql" #import "../fragments/list_item.fragment.graphql"
query getAlerts( query getAlerts(
$searchTerm: String
$projectPath: ID! $projectPath: ID!
$statuses: [AlertManagementStatus!] $statuses: [AlertManagementStatus!]
$sort: AlertManagementAlertSort $sort: AlertManagementAlertSort
@ -9,10 +8,13 @@ query getAlerts(
$lastPageSize: Int $lastPageSize: Int
$prevPageCursor: String = "" $prevPageCursor: String = ""
$nextPageCursor: String = "" $nextPageCursor: String = ""
$searchTerm: String = ""
$assigneeUsername: String = ""
) { ) {
project(fullPath: $projectPath) { project(fullPath: $projectPath) {
alertManagementAlerts( alertManagementAlerts(
search: $searchTerm search: $searchTerm
assigneeUsername: $assigneeUsername
statuses: $statuses statuses: $statuses
sort: $sort sort: $sort
first: $firstPageSize first: $firstPageSize

View file

@ -1,6 +1,6 @@
query getAlertsCount($searchTerm: String, $projectPath: ID!) { query getAlertsCount($searchTerm: String, $projectPath: ID!, $assigneeUsername: String = "") {
project(fullPath: $projectPath) { project(fullPath: $projectPath) {
alertManagementAlertStatusCounts(search: $searchTerm) { alertManagementAlertStatusCounts(search: $searchTerm, assigneeUsername: $assigneeUsername) {
all all
open open
acknowledged acknowledged

View file

@ -18,12 +18,12 @@ export default () => {
populatingAlertsHelpUrl, populatingAlertsHelpUrl,
alertsHelpUrl, alertsHelpUrl,
opsgenieMvcTargetUrl, opsgenieMvcTargetUrl,
textQuery,
assigneeUsernameQuery,
alertManagementEnabled,
userCanEnableAlertManagement,
opsgenieMvcEnabled,
} = domEl.dataset; } = domEl.dataset;
let { alertManagementEnabled, userCanEnableAlertManagement, opsgenieMvcEnabled } = domEl.dataset;
alertManagementEnabled = parseBoolean(alertManagementEnabled);
userCanEnableAlertManagement = parseBoolean(userCanEnableAlertManagement);
opsgenieMvcEnabled = parseBoolean(opsgenieMvcEnabled);
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(
@ -50,23 +50,24 @@ export default () => {
return new Vue({ return new Vue({
el: selector, el: selector,
provide: {
projectPath,
textQuery,
assigneeUsernameQuery,
enableAlertManagementPath,
populatingAlertsHelpUrl,
emptyAlertSvgPath,
opsgenieMvcTargetUrl,
alertManagementEnabled: parseBoolean(alertManagementEnabled),
userCanEnableAlertManagement: parseBoolean(userCanEnableAlertManagement),
opsgenieMvcEnabled: parseBoolean(opsgenieMvcEnabled),
},
apolloProvider, apolloProvider,
components: { components: {
AlertManagementList, AlertManagementList,
}, },
render(createElement) { render(createElement) {
return createElement('alert-management-list', { return createElement('alert-management-list');
props: {
projectPath,
enableAlertManagementPath,
populatingAlertsHelpUrl,
emptyAlertSvgPath,
alertManagementEnabled,
userCanEnableAlertManagement,
opsgenieMvcTargetUrl,
opsgenieMvcEnabled,
},
});
}, },
}); });
}; };

View file

@ -180,11 +180,9 @@ export default {
/> />
</span> </span>
</div> </div>
<span class="gl-display-flex gl-justify-content-end"> <gl-button v-gl-modal.authKeyModal class="gl-mt-2" :disabled="isDisabled">{{
<gl-button v-gl-modal.authKeyModal class="gl-mt-2" :disabled="isDisabled">{{ $options.RESET_KEY
$options.RESET_KEY }}</gl-button>
}}</gl-button>
</span>
<gl-modal <gl-modal
modal-id="authKeyModal" modal-id="authKeyModal"
:title="$options.RESET_KEY" :title="$options.RESET_KEY"

View file

@ -0,0 +1,109 @@
<script>
import { GlTable, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
import { trackAlertIntergrationsViewsOptions } from '../constants';
export const i18n = {
title: s__('AlertsIntegrations|Current integrations'),
emptyState: s__('AlertsIntegrations|No integrations have been added yet'),
status: {
enabled: {
name: __('Enabled'),
tooltip: s__('AlertsIntegrations|Alerts will be created through this integration'),
},
disabled: {
name: __('Disabled'),
tooltip: s__('AlertsIntegrations|Alerts will not be created through this integration'),
},
},
};
const bodyTrClass =
'gl-border-1 gl-border-t-solid gl-border-b-solid gl-border-gray-100 gl-hover-cursor-pointer gl-hover-bg-blue-50 gl-hover-border-blue-200';
export default {
i18n,
components: {
GlTable,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
integrations: {
type: Array,
required: false,
default: () => [],
},
},
fields: [
{
key: 'activated',
label: __('Status'),
},
{
key: 'name',
label: s__('AlertsIntegrations|Integration Name'),
},
{
key: 'type',
label: __('Type'),
},
],
computed: {
tbodyTrClass() {
return {
[bodyTrClass]: this.integrations.length,
};
},
},
mounted() {
this.trackPageViews();
},
methods: {
trackPageViews() {
const { category, action } = trackAlertIntergrationsViewsOptions;
Tracking.event(category, action);
},
},
};
</script>
<template>
<div class="incident-management-list">
<h5 class="gl-font-lg">{{ $options.i18n.title }}</h5>
<gl-table
:empty-text="$options.i18n.emptyState"
:items="integrations"
:fields="$options.fields"
stacked="md"
:tbody-tr-class="tbodyTrClass"
show-empty
>
<template #cell(activated)="{ item }">
<span v-if="item.activated" data-testid="integration-activated-status">
<gl-icon
v-gl-tooltip
name="check-circle-filled"
:size="16"
class="gl-text-green-500 gl-hover-cursor-pointer gl-mr-3"
:title="$options.i18n.status.enabled.tooltip"
/>
{{ $options.i18n.status.enabled.name }}
</span>
<span v-else data-testid="integration-activated-status">
<gl-icon
v-gl-tooltip
name="warning-solid"
:size="16"
class="gl-text-red-600 gl-hover-cursor-pointer gl-mr-3"
:title="$options.i18n.status.disabled.tooltip"
/>
{{ $options.i18n.status.disabled.name }}
</span>
</template>
</gl-table>
</div>
</template>

View file

@ -14,9 +14,11 @@ import {
GlFormSelect, GlFormSelect,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { s__ } from '~/locale';
import { doesHashExistInUrl } from '~/lib/utils/url_utility';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ToggleButton from '~/vue_shared/components/toggle_button.vue'; import ToggleButton from '~/vue_shared/components/toggle_button.vue';
import IntegrationsList from './alerts_integrations_list.vue';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import service from '../services'; import service from '../services';
import { import {
@ -25,7 +27,9 @@ import {
JSON_VALIDATE_DELAY, JSON_VALIDATE_DELAY,
targetPrometheusUrlPlaceholder, targetPrometheusUrlPlaceholder,
targetOpsgenieUrlPlaceholder, targetOpsgenieUrlPlaceholder,
sectionHash,
} from '../constants'; } from '../constants';
import createFlash, { FLASH_TYPES } from '~/flash';
export default { export default {
i18n, i18n,
@ -46,11 +50,11 @@ export default {
GlSprintf, GlSprintf,
ClipboardButton, ClipboardButton,
ToggleButton, ToggleButton,
IntegrationsList,
}, },
directives: { directives: {
'gl-modal': GlModalDirective, 'gl-modal': GlModalDirective,
}, },
mixins: [glFeatureFlagsMixin()],
inject: ['prometheus', 'generic', 'opsgenie'], inject: ['prometheus', 'generic', 'opsgenie'],
data() { data() {
return { return {
@ -148,6 +152,20 @@ export default {
? this.$options.targetOpsgenieUrlPlaceholder ? this.$options.targetOpsgenieUrlPlaceholder
: this.$options.targetPrometheusUrlPlaceholder; : this.$options.targetPrometheusUrlPlaceholder;
}, },
integrations() {
return [
{
name: s__('AlertSettings|HTTP endpoint'),
type: s__('AlertsIntegrations|HTTP endpoint'),
activated: this.generic.activated,
},
{
name: s__('AlertSettings|External Prometheus'),
type: s__('AlertsIntegrations|Prometheus'),
activated: this.prometheus.activated,
},
];
},
}, },
watch: { watch: {
'testAlert.json': debounce(function debouncedJsonValidate() { 'testAlert.json': debounce(function debouncedJsonValidate() {
@ -173,9 +191,12 @@ export default {
this.authKey = this.selectedService.authKey ?? ''; this.authKey = this.selectedService.authKey ?? '';
}, },
methods: { methods: {
createUserErrorMessage(errors = { error: [''] }) { createUserErrorMessage(errors = {}) {
// eslint-disable-next-line prefer-destructuring const error = Object.entries(errors)?.[0];
this.serverError = errors.error[0]; if (error) {
const [field, [msg]] = error;
this.serverError = `${field} ${msg}`;
}
}, },
setOpsgenieAsDefault() { setOpsgenieAsDefault() {
this.options = this.options.map(el => { this.options = this.options.map(el => {
@ -245,29 +266,11 @@ export default {
? { service: { opsgenie_mvc_target_url: this.targetUrl, opsgenie_mvc_enabled: value } } ? { service: { opsgenie_mvc_target_url: this.targetUrl, opsgenie_mvc_enabled: value } }
: { service: { active: value } }, : { service: { active: value } },
}) })
.then(() => { .then(() => this.notifySuccessAndReload())
this.active = value;
this.toggleSuccess(value);
if (!this.isOpsgenie && value) {
if (!this.selectedService.authKey) {
return window.location.reload();
}
return this.removeOpsGenieOption();
}
if (this.isOpsgenie && value) {
return this.setOpsgenieAsDefault();
}
// eslint-disable-next-line no-return-assign
return (this.options = serviceOptions);
})
.catch(({ response: { data: { errors } = {} } = {} }) => { .catch(({ response: { data: { errors } = {} } = {} }) => {
this.createUserErrorMessage(errors); this.createUserErrorMessage(errors);
this.setFeedback({ this.setFeedback({
feedbackMessage: `${this.$options.i18n.errorMsg}.`, feedbackMessage: this.$options.i18n.errorMsg,
variant: 'danger', variant: 'danger',
}); });
}) })
@ -276,6 +279,12 @@ export default {
this.canSaveForm = false; this.canSaveForm = false;
}); });
}, },
reload() {
if (!doesHashExistInUrl(sectionHash)) {
window.location.hash = sectionHash;
}
window.location.reload();
},
togglePrometheusActive(value) { togglePrometheusActive(value) {
this.loading = true; this.loading = true;
return service return service
@ -288,15 +297,11 @@ export default {
redirect: window.location, redirect: window.location,
}, },
}) })
.then(() => { .then(() => this.notifySuccessAndReload())
this.active = value;
this.toggleSuccess(value);
this.removeOpsGenieOption();
})
.catch(({ response: { data: { errors } = {} } = {} }) => { .catch(({ response: { data: { errors } = {} } = {} }) => {
this.createUserErrorMessage(errors); this.createUserErrorMessage(errors);
this.setFeedback({ this.setFeedback({
feedbackMessage: `${this.$options.i18n.errorMsg}.`, feedbackMessage: this.$options.i18n.errorMsg,
variant: 'danger', variant: 'danger',
}); });
}) })
@ -305,18 +310,9 @@ export default {
this.canSaveForm = false; this.canSaveForm = false;
}); });
}, },
toggleSuccess(value) { notifySuccessAndReload() {
if (value) { createFlash({ message: this.$options.i18n.changesSaved, type: FLASH_TYPES.NOTICE });
this.setFeedback({ setTimeout(() => this.reload(), 1000);
feedbackMessage: this.$options.i18n.endPointActivated,
variant: 'info',
});
} else {
this.setFeedback({
feedbackMessage: this.$options.i18n.changesSaved,
variant: 'info',
});
}
}, },
setFeedback({ feedbackMessage, variant }) { setFeedback({ feedbackMessage, variant }) {
this.feedback = { feedbackMessage, variant }; this.feedback = { feedbackMessage, variant };
@ -375,47 +371,50 @@ export default {
<template> <template>
<div> <div>
<gl-alert v-if="showFeedbackMsg" :variant="feedback.variant" @dismiss="dismissFeedback"> <integrations-list :integrations="integrations" />
{{ feedback.feedbackMessage }}
<br />
<i v-if="serverError">{{ __('Error message:') }} {{ serverError }}</i>
<gl-button
v-if="showAlertSave"
variant="danger"
category="primary"
class="gl-display-block gl-mt-3"
@click="toggle(active)"
>
{{ __('Save anyway') }}
</gl-button>
</gl-alert>
<div data-testid="alert-settings-description" class="gl-mt-5">
<p v-for="section in sections" :key="section.text">
<gl-sprintf :message="section.text">
<template #link="{ content }">
<gl-link :href="section.url" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</div>
<gl-form @submit.prevent="onSubmit" @reset.prevent="onReset"> <gl-form @submit.prevent="onSubmit" @reset.prevent="onReset">
<gl-form-group <h5 class="gl-font-lg gl-my-5">{{ $options.i18n.integrationsLabel }}</h5>
:label="$options.i18n.integrationsLabel"
label-for="integrations" <gl-alert v-if="showFeedbackMsg" :variant="feedback.variant" @dismiss="dismissFeedback">
label-class="label-bold" {{ feedback.feedbackMessage }}
> <br />
<i v-if="serverError">{{ __('Error message:') }} {{ serverError }}</i>
<gl-button
v-if="showAlertSave"
variant="danger"
category="primary"
class="gl-display-block gl-mt-3"
@click="toggle(active)"
>
{{ __('Save anyway') }}
</gl-button>
</gl-alert>
<div data-testid="alert-settings-description">
<p v-for="section in sections" :key="section.text">
<gl-sprintf :message="section.text">
<template #link="{ content }">
<gl-link :href="section.url" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</div>
<gl-form-group label-for="integration-type" :label="$options.i18n.integration">
<gl-form-select <gl-form-select
id="integration-type"
v-model="selectedEndpoint" v-model="selectedEndpoint"
:options="options" :options="options"
data-testid="alert-settings-select" data-testid="alert-settings-select"
@change="resetFormValues" @change="resetFormValues"
/> />
<span class="gl-text-gray-200"> <span class="gl-text-gray-500">
<gl-sprintf :message="$options.i18n.integrationsInfo"> <gl-sprintf :message="$options.i18n.integrationsInfo">
<template #link="{ content }"> <template #link="{ content }">
<gl-link <gl-link
class="gl-display-inline-block" class="gl-display-inline-block"
href="https://gitlab.com/groups/gitlab-org/-/epics/3362" href="https://gitlab.com/groups/gitlab-org/-/epics/4390"
target="_blank" target="_blank"
>{{ content }}</gl-link >{{ content }}</gl-link
> >
@ -423,11 +422,7 @@ export default {
</gl-sprintf> </gl-sprintf>
</span> </span>
</gl-form-group> </gl-form-group>
<gl-form-group <gl-form-group :label="$options.i18n.activeLabel" label-for="activated">
:label="$options.i18n.activeLabel"
label-for="activated"
label-class="label-bold"
>
<toggle-button <toggle-button
id="activated" id="activated"
:disabled-input="loading" :disabled-input="loading"
@ -440,7 +435,6 @@ export default {
v-if="isOpsgenie || isPrometheus" v-if="isOpsgenie || isPrometheus"
:label="$options.i18n.apiBaseUrlLabel" :label="$options.i18n.apiBaseUrlLabel"
label-for="api-url" label-for="api-url"
label-class="label-bold"
> >
<gl-form-input <gl-form-input
id="api-url" id="api-url"
@ -449,12 +443,12 @@ export default {
:placeholder="baseUrlPlaceholder" :placeholder="baseUrlPlaceholder"
:disabled="!active" :disabled="!active"
/> />
<span class="gl-text-gray-200"> <span class="gl-text-gray-500">
{{ $options.i18n.apiBaseUrlHelpText }} {{ $options.i18n.apiBaseUrlHelpText }}
</span> </span>
</gl-form-group> </gl-form-group>
<template v-if="!isOpsgenie"> <template v-if="!isOpsgenie">
<gl-form-group :label="$options.i18n.urlLabel" label-for="url" label-class="label-bold"> <gl-form-group :label="$options.i18n.urlLabel" label-for="url">
<gl-form-input-group id="url" readonly :value="selectedService.url"> <gl-form-input-group id="url" readonly :value="selectedService.url">
<template #append> <template #append>
<clipboard-button <clipboard-button
@ -464,15 +458,11 @@ export default {
/> />
</template> </template>
</gl-form-input-group> </gl-form-input-group>
<span class="gl-text-gray-200"> <span class="gl-text-gray-500">
{{ prometheusInfo }} {{ prometheusInfo }}
</span> </span>
</gl-form-group> </gl-form-group>
<gl-form-group <gl-form-group :label="$options.i18n.authKeyLabel" label-for="authorization-key">
:label="$options.i18n.authKeyLabel"
label-for="authorization-key"
label-class="label-bold"
>
<gl-form-input-group id="authorization-key" class="gl-mb-2" readonly :value="authKey"> <gl-form-input-group id="authorization-key" class="gl-mb-2" readonly :value="authKey">
<template #append> <template #append>
<clipboard-button <clipboard-button
@ -498,7 +488,6 @@ export default {
<gl-form-group <gl-form-group
:label="$options.i18n.alertJson" :label="$options.i18n.alertJson"
label-for="alert-json" label-for="alert-json"
label-class="label-bold"
:invalid-feedback="testAlert.error" :invalid-feedback="testAlert.error"
> >
<gl-form-textarea <gl-form-textarea
@ -511,16 +500,11 @@ export default {
max-rows="10" max-rows="10"
/> />
</gl-form-group> </gl-form-group>
<div class="gl-display-flex gl-justify-content-end"> <gl-button :disabled="!canTestAlert" @click="validateTestAlert">{{
<gl-button :disabled="!canTestAlert" @click="validateTestAlert">{{ $options.i18n.testAlertInfo
$options.i18n.testAlertInfo }}</gl-button>
}}</gl-button>
</div>
</template> </template>
<div class="footer-block row-content-block gl-display-flex gl-justify-content-space-between"> <div class="footer-block row-content-block gl-display-flex gl-justify-content-space-between">
<gl-button category="primary" :disabled="!canSaveConfig" @click="onReset">
{{ __('Cancel') }}
</gl-button>
<gl-button <gl-button
variant="success" variant="success"
category="primary" category="primary"
@ -529,6 +513,9 @@ export default {
> >
{{ __('Save changes') }} {{ __('Save changes') }}
</gl-button> </gl-button>
<gl-button category="primary" :disabled="!canSaveConfig" @click="onReset">
{{ __('Cancel') }}
</gl-button>
</div> </div>
</gl-form> </gl-form>
</div> </div>

View file

@ -7,22 +7,21 @@ export const i18n = {
setupSection: s__( setupSection: s__(
"AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.", "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.",
), ),
errorMsg: s__('AlertSettings|There was an error updating the alert settings'), errorMsg: s__('AlertSettings|There was an error updating the alert settings.'),
errorKeyMsg: s__( errorKeyMsg: s__(
'AlertSettings|There was an error while trying to reset the key. Please refresh the page to try again.', 'AlertSettings|There was an error while trying to reset the key. Please refresh the page to try again.',
), ),
restKeyInfo: s__( restKeyInfo: s__(
'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.', 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
), ),
endPointActivated: s__('AlertSettings|Alerts endpoint successfully activated.'), changesSaved: s__('AlertSettings|Your integration was successfully updated.'),
changesSaved: s__('AlertSettings|Your changes were successfully updated.'),
prometheusInfo: s__('AlertSettings|Add URL and auth key to your Prometheus config file'), prometheusInfo: s__('AlertSettings|Add URL and auth key to your Prometheus config file'),
integrationsInfo: s__( integrationsInfo: s__(
'AlertSettings|Learn more about our %{linkStart}upcoming integrations%{linkEnd}', 'AlertSettings|Learn more about our improvements for %{linkStart}integrations%{linkEnd}',
), ),
resetKey: s__('AlertSettings|Reset key'), resetKey: s__('AlertSettings|Reset key'),
copyToClipboard: s__('AlertSettings|Copy'), copyToClipboard: s__('AlertSettings|Copy'),
integrationsLabel: s__('AlertSettings|Integrations'), integrationsLabel: s__('AlertSettings|Add new integrations'),
apiBaseUrlLabel: s__('AlertSettings|API URL'), apiBaseUrlLabel: s__('AlertSettings|API URL'),
authKeyLabel: s__('AlertSettings|Authorization key'), authKeyLabel: s__('AlertSettings|Authorization key'),
urlLabel: s__('AlertSettings|Webhook URL'), urlLabel: s__('AlertSettings|Webhook URL'),
@ -38,10 +37,11 @@ export const i18n = {
authKeyRest: s__( authKeyRest: s__(
'AlertSettings|Authorization key has been successfully reset. Please save your changes now.', 'AlertSettings|Authorization key has been successfully reset. Please save your changes now.',
), ),
integration: s__('AlertSettings|Integration'),
}; };
export const serviceOptions = [ export const serviceOptions = [
{ value: 'generic', text: s__('AlertSettings|Generic') }, { value: 'generic', text: s__('AlertSettings|HTTP Endpoint') },
{ value: 'prometheus', text: s__('AlertSettings|External Prometheus') }, { value: 'prometheus', text: s__('AlertSettings|External Prometheus') },
{ value: 'opsgenie', text: s__('AlertSettings|Opsgenie') }, { value: 'opsgenie', text: s__('AlertSettings|Opsgenie') },
]; ];
@ -50,3 +50,15 @@ export const JSON_VALIDATE_DELAY = 250;
export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/'; export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/';
export const targetOpsgenieUrlPlaceholder = 'https://app.opsgenie.com/alert/list/'; export const targetOpsgenieUrlPlaceholder = 'https://app.opsgenie.com/alert/list/';
export const sectionHash = 'js-alert-management-settings';
/* eslint-disable @gitlab/require-i18n-strings */
/**
* Tracks snowplow event when user views alerts intergration list
*/
export const trackAlertIntergrationsViewsOptions = {
category: 'Alert Intergrations',
action: 'view_alert_integrations_list',
};

View file

@ -0,0 +1,30 @@
<script>
import InstanceCounts from './instance_counts.vue';
import PipelinesChart from './pipelines_chart.vue';
import UsersChart from './users_chart.vue';
import { TODAY, TOTAL_DAYS_TO_SHOW, START_DATE } from '../constants';
export default {
name: 'InstanceStatisticsApp',
components: {
InstanceCounts,
PipelinesChart,
UsersChart,
},
TOTAL_DAYS_TO_SHOW,
START_DATE,
TODAY,
};
</script>
<template>
<div>
<instance-counts />
<users-chart
:start-date="$options.START_DATE"
:end-date="$options.TODAY"
:total-data-points="$options.TOTAL_DAYS_TO_SHOW"
/>
<pipelines-chart />
</div>
</template>

View file

@ -0,0 +1,64 @@
<script>
import * as Sentry from '~/sentry/wrapper';
import { s__ } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
import MetricCard from '~/analytics/shared/components/metric_card.vue';
import instanceStatisticsCountQuery from '../graphql/queries/instance_statistics_count.query.graphql';
const defaultPrecision = 0;
export default {
name: 'InstanceCounts',
components: {
MetricCard,
},
data() {
return {
counts: [],
};
},
apollo: {
counts: {
query: instanceStatisticsCountQuery,
update(data) {
return Object.entries(data).map(([key, obj]) => {
const label = this.$options.i18n.labels[key];
const formatter = getFormatter(SUPPORTED_FORMATS.number);
const value = obj.nodes?.length ? formatter(obj.nodes[0].count, defaultPrecision) : null;
return {
key,
value,
label,
};
});
},
error(error) {
createFlash(this.$options.i18n.loadCountsError);
Sentry.captureException(error);
},
},
},
i18n: {
labels: {
users: s__('InstanceStatistics|Users'),
projects: s__('InstanceStatistics|Projects'),
groups: s__('InstanceStatistics|Groups'),
issues: s__('InstanceStatistics|Issues'),
mergeRequests: s__('InstanceStatistics|Merge Requests'),
pipelines: s__('InstanceStatistics|Pipelines'),
},
loadCountsError: s__('Could not load instance counts. Please refresh the page to try again.'),
},
};
</script>
<template>
<metric-card
:title="__('Instance Statistics')"
:metrics="counts"
:is-loading="$apollo.queries.counts.loading"
class="gl-mt-4"
/>
</template>

View file

@ -0,0 +1,215 @@
<script>
import { GlLineChart } from '@gitlab/ui/dist/charts';
import { GlAlert } from '@gitlab/ui';
import { mapKeys, mapValues, pick, some, sum } from 'lodash';
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
import { s__ } from '~/locale';
import {
differenceInMonths,
formatDateAsMonth,
getDayDifference,
} from '~/lib/utils/datetime_utility';
import { getAverageByMonth, sortByDate, extractValues } from '../utils';
import pipelineStatsQuery from '../graphql/queries/pipeline_stats.query.graphql';
import { TODAY, START_DATE } from '../constants';
const DATA_KEYS = [
'pipelinesTotal',
'pipelinesSucceeded',
'pipelinesFailed',
'pipelinesCanceled',
'pipelinesSkipped',
];
const PREFIX = 'pipelines';
export default {
name: 'PipelinesChart',
components: {
GlLineChart,
GlAlert,
ChartSkeletonLoader,
},
startDate: START_DATE,
endDate: TODAY,
i18n: {
loadPipelineChartError: s__(
'InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again.',
),
noDataMessage: s__('InstanceAnalytics|There is no data available.'),
total: s__('InstanceAnalytics|Total'),
succeeded: s__('InstanceAnalytics|Succeeded'),
failed: s__('InstanceAnalytics|Failed'),
canceled: s__('InstanceAnalytics|Canceled'),
skipped: s__('InstanceAnalytics|Skipped'),
chartTitle: s__('InstanceAnalytics|Pipelines'),
yAxisTitle: s__('InstanceAnalytics|Items'),
xAxisTitle: s__('InstanceAnalytics|Month'),
},
data() {
return {
loading: true,
loadingError: null,
};
},
apollo: {
pipelineStats: {
query: pipelineStatsQuery,
variables() {
return {
firstTotal: this.totalDaysToShow,
firstSucceeded: this.totalDaysToShow,
firstFailed: this.totalDaysToShow,
firstCanceled: this.totalDaysToShow,
firstSkipped: this.totalDaysToShow,
};
},
update(data) {
const allData = extractValues(data, DATA_KEYS, PREFIX, 'nodes');
const allPageInfo = extractValues(data, DATA_KEYS, PREFIX, 'pageInfo');
return {
...mapValues(allData, sortByDate),
...allPageInfo,
};
},
result() {
if (this.hasNextPage) {
this.fetchNextPage();
}
},
error() {
this.handleError();
},
},
},
computed: {
isLoading() {
return this.$apollo.queries.pipelineStats.loading;
},
totalDaysToShow() {
return getDayDifference(this.$options.startDate, this.$options.endDate);
},
firstVariables() {
const allData = pick(this.pipelineStats, [
'nodesTotal',
'nodesSucceeded',
'nodesFailed',
'nodesCanceled',
'nodesSkipped',
]);
const allDayDiffs = mapValues(allData, data => {
const firstdataPoint = data[0];
if (!firstdataPoint) {
return 0;
}
return Math.max(
0,
getDayDifference(this.$options.startDate, new Date(firstdataPoint.recordedAt)),
);
});
return mapKeys(allDayDiffs, (value, key) => key.replace('nodes', 'first'));
},
cursorVariables() {
const pageInfoKeys = [
'pageInfoTotal',
'pageInfoSucceeded',
'pageInfoFailed',
'pageInfoCanceled',
'pageInfoSkipped',
];
return extractValues(this.pipelineStats, pageInfoKeys, 'pageInfo', 'endCursor');
},
hasNextPage() {
return (
sum(Object.values(this.firstVariables)) > 0 &&
some(this.pipelineStats, ({ hasNextPage }) => hasNextPage)
);
},
hasEmptyDataSet() {
return this.chartData.every(({ data }) => data.length === 0);
},
chartData() {
const allData = pick(this.pipelineStats, [
'nodesTotal',
'nodesSucceeded',
'nodesFailed',
'nodesCanceled',
'nodesSkipped',
]);
const options = { shouldRound: true };
return Object.keys(allData).map(key => {
const i18nName = key.slice('nodes'.length).toLowerCase();
return {
name: this.$options.i18n[i18nName],
data: getAverageByMonth(allData[key], options),
};
});
},
range() {
return {
min: this.$options.startDate,
max: this.$options.endDate,
};
},
chartOptions() {
const { endDate, startDate, i18n } = this.$options;
return {
xAxis: {
...this.range,
name: i18n.xAxisTitle,
type: 'time',
splitNumber: differenceInMonths(startDate, endDate) + 1,
axisLabel: {
interval: 0,
showMinLabel: false,
showMaxLabel: false,
align: 'right',
formatter: formatDateAsMonth,
},
},
yAxis: {
name: i18n.yAxisTitle,
},
};
},
},
methods: {
handleError() {
this.loadingError = true;
},
fetchNextPage() {
this.$apollo.queries.pipelineStats
.fetchMore({
variables: {
...this.firstVariables,
...this.cursorVariables,
},
updateQuery: (previousResult, { fetchMoreResult }) => {
return Object.keys(fetchMoreResult).reduce((memo, key) => {
const { nodes, ...rest } = fetchMoreResult[key];
const previousNodes = previousResult[key].nodes;
return { ...memo, [key]: { ...rest, nodes: [...previousNodes, ...nodes] } };
}, {});
},
})
.catch(this.handleError);
},
},
};
</script>
<template>
<div>
<h3>{{ $options.i18n.chartTitle }}</h3>
<gl-alert v-if="loadingError" variant="danger" :dismissible="false" class="gl-mt-3">
{{ this.$options.i18n.loadPipelineChartError }}
</gl-alert>
<chart-skeleton-loader v-else-if="isLoading" />
<gl-alert v-else-if="hasEmptyDataSet" variant="info" :dismissible="false" class="gl-mt-3">
{{ $options.i18n.noDataMessage }}
</gl-alert>
<gl-line-chart v-else :option="chartOptions" :include-legend-avg-max="true" :data="chartData" />
</div>
</template>

View file

@ -0,0 +1,143 @@
<script>
import { GlAlert } from '@gitlab/ui';
import { GlAreaChart } from '@gitlab/ui/dist/charts';
import produce from 'immer';
import { sortBy } from 'lodash';
import * as Sentry from '~/sentry/wrapper';
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
import { __ } from '~/locale';
import { formatDateAsMonth } from '~/lib/utils/datetime_utility';
import usersQuery from '../graphql/queries/users.query.graphql';
import { getAverageByMonth } from '../utils';
const sortByDate = data => sortBy(data, item => new Date(item[0]).getTime());
export default {
name: 'UsersChart',
components: { GlAlert, GlAreaChart, ChartSkeletonLoader },
props: {
startDate: {
type: Date,
required: true,
},
endDate: {
type: Date,
required: true,
},
totalDataPoints: {
type: Number,
required: true,
},
},
data() {
return {
loadingError: null,
users: [],
pageInfo: null,
};
},
apollo: {
users: {
query: usersQuery,
variables() {
return {
first: this.totalDataPoints,
after: null,
};
},
update(data) {
return data.users?.nodes || [];
},
result({ data }) {
const {
users: { pageInfo },
} = data;
this.pageInfo = pageInfo;
this.fetchNextPage();
},
error(error) {
this.handleError(error);
},
},
},
i18n: {
yAxisTitle: __('Total users'),
xAxisTitle: __('Month'),
loadUserChartError: __('Could not load the user chart. Please refresh the page to try again.'),
noDataMessage: __('There is no data available.'),
},
computed: {
isLoading() {
return this.$apollo.queries.users.loading || this.pageInfo?.hasNextPage;
},
chartUserData() {
const averaged = getAverageByMonth(
this.users.length > this.totalDataPoints
? this.users.slice(0, this.totalDataPoints)
: this.users,
{ shouldRound: true },
);
return sortByDate(averaged);
},
options() {
return {
xAxis: {
name: this.$options.i18n.xAxisTitle,
type: 'category',
axisLabel: {
formatter: formatDateAsMonth,
},
},
yAxis: {
name: this.$options.i18n.yAxisTitle,
},
};
},
},
methods: {
handleError(error) {
this.loadingError = true;
this.users = [];
Sentry.captureException(error);
},
fetchNextPage() {
if (this.pageInfo?.hasNextPage) {
this.$apollo.queries.users
.fetchMore({
variables: { first: this.totalDataPoints, after: this.pageInfo.endCursor },
updateQuery: (previousResult, { fetchMoreResult }) => {
return produce(fetchMoreResult, newUsers => {
// eslint-disable-next-line no-param-reassign
newUsers.users.nodes = [...previousResult.users.nodes, ...newUsers.users.nodes];
});
},
})
.catch(this.handleError);
}
},
},
};
</script>
<template>
<div>
<h3>{{ $options.i18n.yAxisTitle }}</h3>
<gl-alert v-if="loadingError" variant="danger" :dismissible="false" class="gl-mt-3">
{{ this.$options.i18n.loadUserChartError }}
</gl-alert>
<chart-skeleton-loader v-else-if="isLoading" />
<gl-alert v-else-if="!chartUserData.length" variant="info" :dismissible="false" class="gl-mt-3">
{{ $options.i18n.noDataMessage }}
</gl-alert>
<gl-area-chart
v-else
:option="options"
:include-legend-avg-max="true"
:data="[
{
name: $options.i18n.yAxisTitle,
data: chartUserData,
},
]"
/>
</div>
</template>

View file

@ -0,0 +1,5 @@
import { getDateInPast } from '~/lib/utils/datetime_utility';
export const TOTAL_DAYS_TO_SHOW = 365;
export const TODAY = new Date();
export const START_DATE = getDateInPast(TODAY, TOTAL_DAYS_TO_SHOW);

View file

@ -0,0 +1,4 @@
fragment Count on InstanceStatisticsMeasurement {
count
recordedAt
}

View file

@ -0,0 +1,4 @@
fragment Count on InstanceStatisticsMeasurement {
count
recordedAt
}

View file

@ -0,0 +1,34 @@
#import "../fragments/count.fragment.graphql"
query getInstanceCounts {
projects: instanceStatisticsMeasurements(identifier: PROJECTS, first: 1) {
nodes {
...Count
}
}
groups: instanceStatisticsMeasurements(identifier: GROUPS, first: 1) {
nodes {
...Count
}
}
users: instanceStatisticsMeasurements(identifier: USERS, first: 1) {
nodes {
...Count
}
}
issues: instanceStatisticsMeasurements(identifier: ISSUES, first: 1) {
nodes {
...Count
}
}
mergeRequests: instanceStatisticsMeasurements(identifier: MERGE_REQUESTS, first: 1) {
nodes {
...Count
}
}
pipelines: instanceStatisticsMeasurements(identifier: PIPELINES, first: 1) {
nodes {
...Count
}
}
}

View file

@ -0,0 +1,76 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "./count.fragment.graphql"
query pipelineStats(
$firstTotal: Int
$firstSucceeded: Int
$firstFailed: Int
$firstCanceled: Int
$firstSkipped: Int
$endCursorTotal: String
$endCursorSucceeded: String
$endCursorFailed: String
$endCursorCanceled: String
$endCursorSkipped: String
) {
pipelinesTotal: instanceStatisticsMeasurements(
identifier: PIPELINES
first: $firstTotal
after: $endCursorTotal
) {
nodes {
...Count
}
pageInfo {
...PageInfo
}
}
pipelinesSucceeded: instanceStatisticsMeasurements(
identifier: PIPELINES_SUCCEEDED
first: $firstSucceeded
after: $endCursorSucceeded
) {
nodes {
...Count
}
pageInfo {
...PageInfo
}
}
pipelinesFailed: instanceStatisticsMeasurements(
identifier: PIPELINES_FAILED
first: $firstFailed
after: $endCursorFailed
) {
nodes {
...Count
}
pageInfo {
...PageInfo
}
}
pipelinesCanceled: instanceStatisticsMeasurements(
identifier: PIPELINES_CANCELED
first: $firstCanceled
after: $endCursorCanceled
) {
nodes {
...Count
}
pageInfo {
...PageInfo
}
}
pipelinesSkipped: instanceStatisticsMeasurements(
identifier: PIPELINES_SKIPPED
first: $firstSkipped
after: $endCursorSkipped
) {
nodes {
...Count
}
pageInfo {
...PageInfo
}
}
}

View file

@ -0,0 +1,13 @@
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "../fragments/count.fragment.graphql"
query getUsersCount($first: Int, $after: String) {
users: instanceStatisticsMeasurements(identifier: USERS, first: $first, after: $after) {
nodes {
...Count
}
pageInfo {
...PageInfo
}
}
}

View file

@ -0,0 +1,24 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import InstanceStatisticsApp from './components/app.vue';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
export default () => {
const el = document.getElementById('js-instance-statistics-app');
if (!el) return false;
return new Vue({
el,
apolloProvider,
render(h) {
return h(InstanceStatisticsApp);
},
});
};

View file

@ -0,0 +1,69 @@
import { masks } from 'dateformat';
import { mapKeys, mapValues, pick, sortBy } from 'lodash';
import { formatDate } from '~/lib/utils/datetime_utility';
const { isoDate } = masks;
/**
* Takes an array of items and returns one item per month with the average of the `count`s from that month
* @param {Array} items
* @param {Number} items[index].count value to be averaged
* @param {String} items[index].recordedAt item dateTime time stamp to be collected into a month
* @param {Object} options
* @param {Object} options.shouldRound an option to specify whether the retuned averages should be rounded
* @return {Array} items collected into [month, average],
* where month is a dateTime string representing the first of the given month
* and average is the average of the count
*/
export function getAverageByMonth(items = [], options = {}) {
const { shouldRound = false } = options;
const itemsMap = items.reduce((memo, item) => {
const { count, recordedAt } = item;
const date = new Date(recordedAt);
const month = formatDate(new Date(date.getFullYear(), date.getMonth(), 1), isoDate);
if (memo[month]) {
const { sum, recordCount } = memo[month];
return { ...memo, [month]: { sum: sum + count, recordCount: recordCount + 1 } };
}
return { ...memo, [month]: { sum: count, recordCount: 1 } };
}, {});
return Object.keys(itemsMap).map(month => {
const { sum, recordCount } = itemsMap[month];
const avg = sum / recordCount;
if (shouldRound) {
return [month, Math.round(avg)];
}
return [month, avg];
});
}
/**
* Extracts values given a data set and a set of keys
* @example
* const data = { fooBar: { baz: 'quis' }, ignored: 'ignored' };
* extractValues(data, ['fooBar'], 'foo', 'baz') => { bazBar: 'quis' }
* @param {Object} data set to extract values from
* @param {Array} dataKeys keys describing where to look for values in the data set
* @param {String} replaceKey name key to be replaced in the data set
* @param {String} nestedKey key nested in the data set to be extracted,
* this is also used to rename the newly created data set
* @return {Object} the newly created data set with the extracted values
*/
export function extractValues(data, dataKeys = [], replaceKey, nestedKey) {
return mapKeys(pick(mapValues(data, nestedKey), dataKeys), (value, key) =>
key.replace(replaceKey, nestedKey),
);
}
/**
* Creates a new array of items sorted by the date string of each item
* @param {Array} items [description]
* @param {String} items[0] date string
* @return {Array} the new sorted array.
*/
export function sortByDate(items = []) {
return sortBy(items, ({ recordedAt }) => new Date(recordedAt).getTime());
}

View file

@ -0,0 +1,80 @@
<script>
import {
GlCard,
GlDeprecatedSkeletonLoading as GlSkeletonLoading,
GlLink,
GlIcon,
GlTooltipDirective,
} from '@gitlab/ui';
export default {
name: 'MetricCard',
components: {
GlCard,
GlSkeletonLoading,
GlLink,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
title: {
type: String,
required: true,
},
metrics: {
type: Array,
required: true,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
valueText(metric) {
const { value = null, unit = null } = metric;
if (!value || value === '-') return '-';
return unit && value ? `${value} ${unit}` : value;
},
},
};
</script>
<template>
<gl-card>
<template #header>
<strong ref="title">{{ title }}</strong>
</template>
<template #default>
<gl-skeleton-loading v-if="isLoading" class="gl-h-auto gl-py-3" />
<div v-else ref="metricsWrapper" class="gl-display-flex">
<div
v-for="metric in metrics"
:key="metric.key"
ref="metricItem"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<gl-link v-if="metric.link" :href="metric.link">
<h3 class="gl-my-2 gl-text-blue-700">{{ valueText(metric) }}</h3>
</gl-link>
<h3 v-else class="gl-my-2">{{ valueText(metric) }}</h3>
<p class="text-secondary gl-font-sm gl-mb-2">
{{ metric.label }}
<span v-if="metric.tooltipText">
&nbsp;
<gl-icon
v-gl-tooltip="{ title: metric.tooltipText }"
:size="14"
class="gl-vertical-align-middle"
name="question"
data-testid="tooltip"
/>
</span>
</p>
</div>
</div>
</template>
</gl-card>
</template>

View file

@ -30,6 +30,7 @@ const Api = {
projectProtectedBranchesPath: '/api/:version/projects/:id/protected_branches', projectProtectedBranchesPath: '/api/:version/projects/:id/protected_branches',
projectSearchPath: '/api/:version/projects/:id/search', projectSearchPath: '/api/:version/projects/:id/search',
projectMilestonesPath: '/api/:version/projects/:id/milestones', projectMilestonesPath: '/api/:version/projects/:id/milestones',
projectIssuePath: '/api/:version/projects/:id/issues/:issue_iid',
mergeRequestsPath: '/api/:version/merge_requests', mergeRequestsPath: '/api/:version/merge_requests',
groupLabelsPath: '/groups/:namespace_path/-/labels', groupLabelsPath: '/groups/:namespace_path/-/labels',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
@ -54,6 +55,7 @@ const Api = {
releaseLinkPath: '/api/:version/projects/:id/releases/:tag_name/assets/links/:link_id', releaseLinkPath: '/api/:version/projects/:id/releases/:tag_name/assets/links/:link_id',
mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines', mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
adminStatisticsPath: '/api/:version/application/statistics', adminStatisticsPath: '/api/:version/application/statistics',
pipelineJobsPath: '/api/:version/projects/:id/pipelines/:pipeline_id/jobs',
pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id', pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id',
pipelinesPath: '/api/:version/projects/:id/pipelines/', pipelinesPath: '/api/:version/projects/:id/pipelines/',
createPipelinePath: '/api/:version/projects/:id/pipeline', createPipelinePath: '/api/:version/projects/:id/pipeline',
@ -64,6 +66,10 @@ const Api = {
issuePath: '/api/:version/projects/:id/issues/:issue_iid', issuePath: '/api/:version/projects/:id/issues/:issue_iid',
tagsPath: '/api/:version/projects/:id/repository/tags', tagsPath: '/api/:version/projects/:id/repository/tags',
freezePeriodsPath: '/api/:version/projects/:id/freeze_periods', freezePeriodsPath: '/api/:version/projects/:id/freeze_periods',
usageDataIncrementUniqueUsersPath: '/api/:version/usage_data/increment_unique_users',
featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists',
featureFlagUserList: '/api/:version/projects/:id/feature_flags_user_lists/:list_iid',
billableGroupMembersPath: '/api/:version/groups/:id/billable_members',
group(groupId, callback = () => {}) { group(groupId, callback = () => {}) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
@ -111,6 +117,12 @@ const Api = {
}); });
}, },
inviteGroupMember(id, data) {
const url = Api.buildUrl(this.groupMembersPath).replace(':id', encodeURIComponent(id));
return axios.post(url, data);
},
groupMilestones(id, options) { groupMilestones(id, options) {
const url = Api.buildUrl(this.groupMilestonesPath).replace(':id', encodeURIComponent(id)); const url = Api.buildUrl(this.groupMilestonesPath).replace(':id', encodeURIComponent(id));
@ -317,6 +329,14 @@ const Api = {
}); });
}, },
addProjectIssueAsTodo(projectId, issueIid) {
const url = Api.buildUrl(Api.projectIssuePath)
.replace(':id', encodeURIComponent(projectId))
.replace(':issue_iid', encodeURIComponent(issueIid));
return axios.post(`${url}/todo`);
},
mergeRequests(params = {}) { mergeRequests(params = {}) {
const url = Api.buildUrl(Api.mergeRequestsPath); const url = Api.buildUrl(Api.mergeRequestsPath);
@ -366,7 +386,7 @@ const Api = {
}, },
commitMultiple(id, data) { commitMultiple(id, data) {
// see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions // see https://docs.gitlab.com/ee/api/commits.html#create-a-commit-with-multiple-files-and-actions
const url = Api.buildUrl(Api.commitsPath).replace(':id', encodeURIComponent(id)); const url = Api.buildUrl(Api.commitsPath).replace(':id', encodeURIComponent(id));
return axios.post(url, JSON.stringify(data), { return axios.post(url, JSON.stringify(data), {
headers: { headers: {
@ -590,6 +610,14 @@ const Api = {
return axios.get(url); return axios.get(url);
}, },
pipelineJobs(projectId, pipelineId) {
const url = Api.buildUrl(this.pipelineJobsPath)
.replace(':id', encodeURIComponent(projectId))
.replace(':pipeline_id', encodeURIComponent(pipelineId));
return axios.get(url);
},
// Return all pipelines for a project or filter by query params // Return all pipelines for a project or filter by query params
pipelines(id, options = {}) { pipelines(id, options = {}) {
const url = Api.buildUrl(this.pipelinesPath).replace(':id', encodeURIComponent(id)); const url = Api.buildUrl(this.pipelinesPath).replace(':id', encodeURIComponent(id));
@ -686,9 +714,78 @@ const Api = {
return axios.post(url, freezePeriod); return axios.post(url, freezePeriod);
}, },
trackRedisHllUserEvent(event) {
if (!gon.features?.usageDataApi) {
return null;
}
const url = Api.buildUrl(this.usageDataIncrementUniqueUsersPath);
const headers = {
'Content-Type': 'application/json',
};
return axios.post(url, { event }, { headers });
},
buildUrl(url) { buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version)); return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
}, },
fetchFeatureFlagUserLists(id, page) {
const url = Api.buildUrl(this.featureFlagUserLists).replace(':id', id);
return axios.get(url, { params: { page } });
},
createFeatureFlagUserList(id, list) {
const url = Api.buildUrl(this.featureFlagUserLists).replace(':id', id);
return axios.post(url, list);
},
fetchFeatureFlagUserList(id, listIid) {
const url = Api.buildUrl(this.featureFlagUserList)
.replace(':id', id)
.replace(':list_iid', listIid);
return axios.get(url);
},
updateFeatureFlagUserList(id, list) {
const url = Api.buildUrl(this.featureFlagUserList)
.replace(':id', id)
.replace(':list_iid', list.iid);
return axios.put(url, list);
},
deleteFeatureFlagUserList(id, listIid) {
const url = Api.buildUrl(this.featureFlagUserList)
.replace(':id', id)
.replace(':list_iid', listIid);
return axios.delete(url);
},
fetchBillableGroupMembersList(namespaceId, options = {}, callback = () => {}) {
const url = Api.buildUrl(this.billableGroupMembersPath).replace(':id', namespaceId);
const defaults = {
per_page: DEFAULT_PER_PAGE,
page: 1,
};
return axios
.get(url, {
params: {
...defaults,
...options,
},
})
.then(({ data, headers }) => {
callback(data);
return { data, headers };
});
},
}; };
export default Api; export default Api;

View file

@ -572,7 +572,7 @@ export class AwardsHandler {
} }
findMatchingEmojiElements(query) { findMatchingEmojiElements(query) {
const emojiMatches = this.emoji.filterEmojiNamesByAlias(query); const emojiMatches = this.emoji.searchEmoji(query, { match: 'fuzzy' }).map(({ name }) => name);
const $emojiElements = $('.emoji-menu-list:not(.frequent-emojis) [data-name]'); const $emojiElements = $('.emoji-menu-list:not(.frequent-emojis) [data-name]');
const $matchingElements = $emojiElements.filter( const $matchingElements = $emojiElements.filter(
(i, elm) => emojiMatches.indexOf(elm.dataset.name) >= 0, (i, elm) => emojiMatches.indexOf(elm.dataset.name) >= 0,

View file

@ -218,7 +218,7 @@ export default {
</p> </p>
</div> </div>
<div v-if="isEditing" class="row-content-block gl-display-flex gl-justify-content-end"> <div v-if="isEditing" class="row-content-block">
<gl-button class="btn-cancel gl-mr-4" data-testid="cancelEditing" @click="onCancel"> <gl-button class="btn-cancel gl-mr-4" data-testid="cancelEditing" @click="onCancel">
{{ __('Cancel') }} {{ __('Cancel') }}
</gl-button> </gl-button>
@ -232,7 +232,7 @@ export default {
{{ s__('Badges|Save changes') }} {{ s__('Badges|Save changes') }}
</gl-button> </gl-button>
</div> </div>
<div v-else class="gl-display-flex gl-justify-content-end form-group"> <div v-else class="form-group">
<gl-button :loading="isSaving" type="submit" variant="success" category="primary"> <gl-button :loading="isSaving" type="submit" variant="success" category="primary">
{{ s__('Badges|Add badge') }} {{ s__('Badges|Add badge') }}
</gl-button> </gl-button>

View file

@ -1,6 +1,6 @@
<script> <script>
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon, GlIcon } from '@gitlab/ui'; import { GlLoadingIcon, GlButton, GlModalDirective } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { PROJECT_BADGE } from '../constants'; import { PROJECT_BADGE } from '../constants';
import Badge from './badge.vue'; import Badge from './badge.vue';
@ -9,8 +9,11 @@ export default {
name: 'BadgeListRow', name: 'BadgeListRow',
components: { components: {
Badge, Badge,
GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlButton,
},
directives: {
GlModal: GlModalDirective,
}, },
props: { props: {
badge: { badge: {
@ -51,24 +54,25 @@ export default {
<span class="table-section section-30 str-truncated">{{ badge.linkUrl }}</span> <span class="table-section section-30 str-truncated">{{ badge.linkUrl }}</span>
<div class="table-section section-10 table-button-footer"> <div class="table-section section-10 table-button-footer">
<div v-if="canEditBadge" class="table-action-buttons"> <div v-if="canEditBadge" class="table-action-buttons">
<button <gl-button
:disabled="badge.isDeleting" :disabled="badge.isDeleting"
class="btn btn-default gl-mr-3" class="gl-mr-3"
type="button" variant="default"
icon="pencil"
size="medium"
:aria-label="__('Edit')"
@click="editBadge(badge)" @click="editBadge(badge)"
> />
<gl-icon :size="16" :aria-label="__('Edit')" name="pencil" /> <gl-button
</button> v-gl-modal.delete-badge-modal
<button
:disabled="badge.isDeleting" :disabled="badge.isDeleting"
class="btn btn-danger" variant="danger"
type="button" icon="remove"
data-toggle="modal" size="medium"
data-target="#delete-badge-modal" :aria-label="__('Delete')"
data-testid="delete-badge"
@click="updateBadgeInModal(badge)" @click="updateBadgeInModal(badge)"
> />
<gl-icon :size="16" :aria-label="__('Delete')" name="remove" />
</button>
<gl-loading-icon v-show="badge.isDeleting" :inline="true" /> <gl-loading-icon v-show="badge.isDeleting" :inline="true" />
</div> </div>
</div> </div>

View file

@ -1,9 +1,8 @@
<script> <script>
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { GlSprintf } from '@gitlab/ui'; import { GlSprintf, GlModal } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
import Badge from './badge.vue'; import Badge from './badge.vue';
import BadgeForm from './badge_form.vue'; import BadgeForm from './badge_form.vue';
import BadgeList from './badge_list.vue'; import BadgeList from './badge_list.vue';
@ -14,7 +13,7 @@ export default {
Badge, Badge,
BadgeForm, BadgeForm,
BadgeList, BadgeList,
GlModal: DeprecatedModal2, GlModal,
GlSprintf, GlSprintf,
}, },
i18n: { i18n: {
@ -24,6 +23,17 @@ export default {
}, },
computed: { computed: {
...mapState(['badgeInModal', 'isEditing']), ...mapState(['badgeInModal', 'isEditing']),
primaryProps() {
return {
text: s__('Delete badge'),
attributes: [{ category: 'primary' }, { variant: 'danger' }],
};
},
cancelProps() {
return {
text: s__('Cancel'),
};
},
}, },
methods: { methods: {
...mapActions(['deleteBadge']), ...mapActions(['deleteBadge']),
@ -44,11 +54,11 @@ export default {
<template> <template>
<div class="badge-settings"> <div class="badge-settings">
<gl-modal <gl-modal
id="delete-badge-modal" modal-id="delete-badge-modal"
:header-title-text="s__('Badges|Delete badge?')" :title="s__('Badges|Delete badge?')"
:footer-primary-button-text="s__('Badges|Delete badge')" :action-primary="primaryProps"
footer-primary-button-variant="danger" :action-cancel="cancelProps"
@submit="onSubmitModal" @primary="onSubmitModal"
> >
<div class="well"> <div class="well">
<badge <badge
@ -65,9 +75,9 @@ export default {
</p> </p>
</gl-modal> </gl-modal>
<badge-form v-show="isEditing" :is-editing="true" /> <badge-form v-show="isEditing" :is-editing="true" data-testid="edit-badge" />
<badge-form v-show="!isEditing" :is-editing="false" /> <badge-form v-show="!isEditing" :is-editing="false" data-testid="add-new-badge" />
<badge-list v-show="!isEditing" /> <badge-list v-show="!isEditing" />
</div> </div>
</template> </template>

View file

@ -18,11 +18,6 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
diffFile: {
type: Object,
required: false,
default: () => ({}),
},
line: { line: {
type: Object, type: Object,
required: false, required: false,

View file

@ -1,114 +1,43 @@
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import { GlButton, GlLoadingIcon, GlIcon } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
import { sprintf, n__ } from '~/locale';
import DraftsCount from './drafts_count.vue';
import PublishButton from './publish_button.vue';
import PreviewItem from './preview_item.vue'; import PreviewItem from './preview_item.vue';
export default { export default {
components: { components: {
GlButton, GlDropdown,
GlLoadingIcon, GlDropdownItem,
GlIcon, GlIcon,
DraftsCount,
PublishButton,
PreviewItem, PreviewItem,
}, },
computed: { computed: {
...mapGetters(['isNotesFetched']),
...mapGetters('batchComments', ['draftsCount', 'sortedDrafts']), ...mapGetters('batchComments', ['draftsCount', 'sortedDrafts']),
...mapState('batchComments', ['showPreviewDropdown']),
dropdownTitle() {
return sprintf(
n__('%{count} pending comment', '%{count} pending comments', this.draftsCount),
{ count: this.draftsCount },
);
},
},
watch: {
showPreviewDropdown() {
if (this.showPreviewDropdown && this.$refs.dropdown) {
this.$nextTick(() => this.$refs.dropdown.$el.focus());
}
},
},
mounted() {
document.addEventListener('click', this.onClickDocument);
},
beforeDestroy() {
document.removeEventListener('click', this.onClickDocument);
}, },
methods: { methods: {
...mapActions('batchComments', ['toggleReviewDropdown']), ...mapActions('batchComments', ['scrollToDraft']),
isLast(index) { isLast(index) {
return index === this.sortedDrafts.length - 1; return index === this.sortedDrafts.length - 1;
}, },
onClickDocument({ target }) {
if (
this.showPreviewDropdown &&
!target.closest('.review-preview-dropdown, .js-publish-draft-button')
) {
this.toggleReviewDropdown();
}
},
}, },
}; };
</script> </script>
<template> <template>
<div <gl-dropdown
class="dropdown float-right review-preview-dropdown" :header-text="n__('%d pending comment', '%d pending comments', draftsCount)"
:class="{ dropup
show: showPreviewDropdown, toggle-class="qa-review-preview-toggle"
}"
> >
<gl-button <template #button-content>
ref="dropdown" {{ __('Pending comments') }}
type="button" <gl-icon class="dropdown-chevron" name="chevron-up" />
category="primary" </template>
variant="success" <gl-dropdown-item
class="review-preview-dropdown-toggle qa-review-preview-toggle" v-for="(draft, index) in sortedDrafts"
@click="toggleReviewDropdown" :key="draft.id"
@click="scrollToDraft(draft)"
> >
{{ __('Finish review') }} <preview-item :draft="draft" :is-last="isLast(index)" />
<drafts-count /> </gl-dropdown-item>
<gl-icon name="angle-up" /> </gl-dropdown>
</gl-button>
<div
class="dropdown-menu dropdown-menu-large dropdown-menu-right dropdown-open-top"
:class="{
show: showPreviewDropdown,
}"
>
<div class="dropdown-title gl-display-flex gl-align-items-center">
<span class="gl-ml-auto">{{ dropdownTitle }}</span>
<gl-button
:aria-label="__('Close')"
type="button"
category="tertiary"
size="small"
class="dropdown-title-button gl-ml-auto gl-p-0!"
icon="close"
@click="toggleReviewDropdown"
/>
</div>
<div class="dropdown-content">
<ul v-if="isNotesFetched">
<li v-for="(draft, index) in sortedDrafts" :key="draft.id">
<preview-item :draft="draft" :is-last="isLast(index)" />
</li>
</ul>
<gl-loading-icon v-else size="lg" class="gl-mt-3 gl-mb-3" />
</div>
<div class="dropdown-footer">
<publish-button
:show-count="false"
:should-publish="true"
:label="__('Submit review')"
class="float-right gl-mr-3"
/>
</div>
</div>
</div>
</template> </template>

View file

@ -1,5 +1,5 @@
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { GlSprintf, GlIcon } from '@gitlab/ui'; import { GlSprintf, GlIcon } from '@gitlab/ui';
import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants'; import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
import { sprintf, __ } from '~/locale'; import { sprintf, __ } from '~/locale';
@ -78,7 +78,6 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions('batchComments', ['scrollToDraft']),
getLineClasses(lineNumber) { getLineClasses(lineNumber) {
return getLineClasses(lineNumber); return getLineClasses(lineNumber);
}, },
@ -88,17 +87,7 @@ export default {
</script> </script>
<template> <template>
<button <span>
type="button"
class="review-preview-item menu-item"
:class="[
componentClasses,
{
'is-last': isLast,
},
]"
@click="scrollToDraft(draft)"
>
<span class="review-preview-item-header"> <span class="review-preview-item-header">
<gl-icon class="flex-shrink-0" :name="iconName" /> <gl-icon class="flex-shrink-0" :name="iconName" />
<span <span
@ -139,5 +128,5 @@ export default {
> >
<gl-icon class="gl-mr-3" name="status_success" /> {{ resolvedStatusMessage }} <gl-icon class="gl-mr-3" name="status_success" /> {{ resolvedStatusMessage }}
</span> </span>
</button> </span>
</template> </template>

View file

@ -1,7 +1,6 @@
<script> <script>
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import DraftsCount from './drafts_count.vue'; import DraftsCount from './drafts_count.vue';
export default { export default {
@ -15,11 +14,6 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
label: {
type: String,
required: false,
default: __('Finish review'),
},
category: { category: {
type: String, type: String,
required: false, required: false,
@ -30,22 +24,14 @@ export default {
required: false, required: false,
default: 'success', default: 'success',
}, },
shouldPublish: {
type: Boolean,
required: true,
},
}, },
computed: { computed: {
...mapState('batchComments', ['isPublishing']), ...mapState('batchComments', ['isPublishing']),
}, },
methods: { methods: {
...mapActions('batchComments', ['publishReview', 'toggleReviewDropdown']), ...mapActions('batchComments', ['publishReview']),
onClick() { onClick() {
if (this.shouldPublish) { this.publishReview();
this.publishReview();
} else {
this.toggleReviewDropdown();
}
}, },
}, },
}; };
@ -59,7 +45,7 @@ export default {
:variant="variant" :variant="variant"
@click="onClick" @click="onClick"
> >
{{ label }} {{ __('Submit review') }}
<drafts-count v-if="showCount" /> <drafts-count v-if="showCount" />
</gl-button> </gl-button>
</template> </template>

View file

@ -1,22 +1,15 @@
<script> <script>
/* eslint-disable vue/no-v-html */ import { mapActions, mapGetters } from 'vuex';
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlModal, GlModalDirective, GlButton } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import PreviewDropdown from './preview_dropdown.vue'; import PreviewDropdown from './preview_dropdown.vue';
import PublishButton from './publish_button.vue';
export default { export default {
components: { components: {
GlButton,
GlModal,
PreviewDropdown, PreviewDropdown,
}, PublishButton,
directives: {
'gl-modal': GlModalDirective,
}, },
computed: { computed: {
...mapGetters(['isNotesFetched']), ...mapGetters(['isNotesFetched']),
...mapState('batchComments', ['isDiscarding']),
...mapGetters('batchComments', ['draftsCount']), ...mapGetters('batchComments', ['draftsCount']),
}, },
watch: { watch: {
@ -27,45 +20,17 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions('batchComments', ['discardReview', 'expandAllDiscussions']), ...mapActions('batchComments', ['expandAllDiscussions']),
}, },
modalId: 'discard-draft-review',
text: sprintf(
s__(
`BatchComments|You're about to discard your review which will delete all of your pending comments.
The deleted comments %{strong_start}cannot%{strong_end} be restored.`,
),
{
strong_start: '<strong>',
strong_end: '</strong>',
},
false,
),
}; };
</script> </script>
<template> <template>
<div v-show="draftsCount > 0"> <div v-show="draftsCount > 0">
<nav class="review-bar-component"> <nav class="review-bar-component">
<div class="review-bar-content qa-review-bar"> <div class="review-bar-content qa-review-bar d-flex gl-justify-content-end">
<preview-dropdown /> <preview-dropdown />
<gl-button <publish-button class="gl-ml-3" show-count />
v-gl-modal="$options.modalId"
:loading="isDiscarding"
class="qa-discard-review float-right"
>
{{ __('Discard review') }}
</gl-button>
</div> </div>
</nav> </nav>
<gl-modal
:title="s__('BatchComments|Discard review?')"
:ok-title="s__('BatchComments|Delete all pending comments')"
:modal-id="$options.modalId"
title-tag="h4"
ok-variant="danger qa-modal-delete-pending-comments"
@ok="discardReview"
>
<p v-html="$options.text"></p>
</gl-modal>
</div> </div>
</template> </template>

View file

@ -75,15 +75,6 @@ export const updateDiscussionsAfterPublish = ({ dispatch, getters, rootGetters }
}), }),
); );
export const discardReview = ({ commit, getters }) => {
commit(types.REQUEST_DISCARD_REVIEW);
return service
.discard(getters.getNotesData.draftsDiscardPath)
.then(() => commit(types.RECEIVE_DISCARD_REVIEW_SUCCESS))
.catch(() => commit(types.RECEIVE_DISCARD_REVIEW_ERROR));
};
export const updateDraft = ( export const updateDraft = (
{ commit, getters }, { commit, getters },
{ note, noteText, resolveDiscussion, position, callback }, { note, noteText, resolveDiscussion, position, callback },
@ -108,8 +99,6 @@ export const scrollToDraft = ({ dispatch, rootGetters }, draft) => {
const draftID = `note_${draft.id}`; const draftID = `note_${draft.id}`;
const el = document.querySelector(`#${tabEl} #${draftID}`); const el = document.querySelector(`#${tabEl} #${draftID}`);
dispatch('closeReviewDropdown');
window.location.hash = draftID; window.location.hash = draftID;
if (window.mrTabs.currentAction !== tab) { if (window.mrTabs.currentAction !== tab) {
@ -125,17 +114,6 @@ export const scrollToDraft = ({ dispatch, rootGetters }, draft) => {
} }
}; };
export const toggleReviewDropdown = ({ dispatch, state }) => {
if (state.showPreviewDropdown) {
dispatch('closeReviewDropdown');
} else {
dispatch('openReviewDropdown');
}
};
export const openReviewDropdown = ({ commit }) => commit(types.OPEN_REVIEW_DROPDOWN);
export const closeReviewDropdown = ({ commit }) => commit(types.CLOSE_REVIEW_DROPDOWN);
export const expandAllDiscussions = ({ dispatch, state }) => export const expandAllDiscussions = ({ dispatch, state }) =>
state.drafts state.drafts
.filter(draft => draft.discussion_id) .filter(draft => draft.discussion_id)

View file

@ -11,13 +11,6 @@ export const REQUEST_PUBLISH_REVIEW = 'REQUEST_PUBLISH_REVIEW';
export const RECEIVE_PUBLISH_REVIEW_SUCCESS = 'RECEIVE_PUBLISH_REVIEW_SUCCESS'; export const RECEIVE_PUBLISH_REVIEW_SUCCESS = 'RECEIVE_PUBLISH_REVIEW_SUCCESS';
export const RECEIVE_PUBLISH_REVIEW_ERROR = 'RECEIVE_PUBLISH_REVIEW_ERROR'; export const RECEIVE_PUBLISH_REVIEW_ERROR = 'RECEIVE_PUBLISH_REVIEW_ERROR';
export const REQUEST_DISCARD_REVIEW = 'REQUEST_DISCARD_REVIEW';
export const RECEIVE_DISCARD_REVIEW_SUCCESS = 'RECEIVE_DISCARD_REVIEW_SUCCESS';
export const RECEIVE_DISCARD_REVIEW_ERROR = 'RECEIVE_DISCARD_REVIEW_ERROR';
export const RECEIVE_DRAFT_UPDATE_SUCCESS = 'RECEIVE_DRAFT_UPDATE_SUCCESS'; export const RECEIVE_DRAFT_UPDATE_SUCCESS = 'RECEIVE_DRAFT_UPDATE_SUCCESS';
export const OPEN_REVIEW_DROPDOWN = 'OPEN_REVIEW_DROPDOWN';
export const CLOSE_REVIEW_DROPDOWN = 'CLOSE_REVIEW_DROPDOWN';
export const TOGGLE_RESOLVE_DISCUSSION = 'TOGGLE_RESOLVE_DISCUSSION'; export const TOGGLE_RESOLVE_DISCUSSION = 'TOGGLE_RESOLVE_DISCUSSION';

View file

@ -43,16 +43,6 @@ export default {
[types.RECEIVE_PUBLISH_REVIEW_ERROR](state) { [types.RECEIVE_PUBLISH_REVIEW_ERROR](state) {
state.isPublishing = false; state.isPublishing = false;
}, },
[types.REQUEST_DISCARD_REVIEW](state) {
state.isDiscarding = true;
},
[types.RECEIVE_DISCARD_REVIEW_SUCCESS](state) {
state.isDiscarding = false;
state.drafts = [];
},
[types.RECEIVE_DISCARD_REVIEW_ERROR](state) {
state.isDiscarding = false;
},
[types.RECEIVE_DRAFT_UPDATE_SUCCESS](state, data) { [types.RECEIVE_DRAFT_UPDATE_SUCCESS](state, data) {
const index = state.drafts.findIndex(draft => draft.id === data.id); const index = state.drafts.findIndex(draft => draft.id === data.id);
@ -60,12 +50,6 @@ export default {
state.drafts.splice(index, 1, processDraft(data)); state.drafts.splice(index, 1, processDraft(data));
} }
}, },
[types.OPEN_REVIEW_DROPDOWN](state) {
state.showPreviewDropdown = true;
},
[types.CLOSE_REVIEW_DROPDOWN](state) {
state.showPreviewDropdown = false;
},
[types.TOGGLE_RESOLVE_DISCUSSION](state, draftId) { [types.TOGGLE_RESOLVE_DISCUSSION](state, draftId) {
state.drafts = state.drafts.map(draft => { state.drafts = state.drafts.map(draft => {
if (draft.id === draftId) { if (draft.id === draftId) {

View file

@ -4,6 +4,4 @@ export default () => ({
drafts: [], drafts: [],
isPublishing: false, isPublishing: false,
currentlyPublishingDrafts: [], currentlyPublishingDrafts: [],
isDiscarding: false,
showPreviewDropdown: false,
}); });

View file

@ -1,5 +1,5 @@
import Autosize from 'autosize'; import Autosize from 'autosize';
import { waitForCSSLoaded } from '../helpers/startup_css_helper'; import { waitForCSSLoaded } from '~/helpers/startup_css_helper';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
waitForCSSLoaded(() => { waitForCSSLoaded(() => {

View file

@ -8,7 +8,6 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
* @sentrify * @sentrify
*/ */
export default () => { export default () => {
const $sidebarGutterToggle = $('.js-sidebar-toggle');
let bootstrapBreakpoint = bp.getBreakpointSize(); let bootstrapBreakpoint = bp.getBreakpointSize();
$(window).on('resize.app', () => { $(window).on('resize.app', () => {
@ -19,8 +18,13 @@ export default () => {
const breakpointSizes = ['md', 'sm', 'xs']; const breakpointSizes = ['md', 'sm', 'xs'];
if (breakpointSizes.includes(bootstrapBreakpoint)) { if (breakpointSizes.includes(bootstrapBreakpoint)) {
const $gutterIcon = $sidebarGutterToggle.find('i'); const $toggleContainer = $('.js-sidebar-toggle-container');
if ($gutterIcon.hasClass('fa-angle-double-right')) { const isExpanded = $toggleContainer.data('is-expanded');
const $expandIcon = $('.js-sidebar-expand');
if (isExpanded) {
const $sidebarGutterToggle = $expandIcon.closest('.js-sidebar-toggle');
$sidebarGutterToggle.trigger('click'); $sidebarGutterToggle.trigger('click');
} }
@ -28,11 +32,12 @@ export default () => {
// Sidebar has an icon which corresponds to collapsing the sidebar // Sidebar has an icon which corresponds to collapsing the sidebar
// only then trigger the click. // only then trigger the click.
if (sidebarGutterVueToggleEl) { if (
const collapseIcon = sidebarGutterVueToggleEl.querySelector('i.fa-angle-double-right'); sidebarGutterVueToggleEl &&
!sidebarGutterVueToggleEl.classList.contains('js-sidebar-collapsed')
if (collapseIcon) { ) {
collapseIcon.click(); if (sidebarGutterVueToggleEl) {
sidebarGutterVueToggleEl.click();
} }
} }
} }

View file

@ -1,19 +1,24 @@
import $ from 'jquery'; import $ from 'jquery';
import Clipboard from 'clipboard'; import Clipboard from 'clipboard';
import { sprintf, __ } from '~/locale'; import { sprintf, __ } from '~/locale';
import { fixTitle, show } from '~/tooltips';
function showTooltip(target, title) { function showTooltip(target, title) {
const $target = $(target); const { originalTitle } = target.dataset;
const originalTitle = $target.data('originalTitle'); const hideTooltip = () => {
target.removeEventListener('mouseout', hideTooltip);
setTimeout(() => {
target.setAttribute('title', originalTitle);
fixTitle(target);
}, 100);
};
if (!$target.data('hideTooltip')) { target.setAttribute('title', title);
$target
.attr('title', title) fixTitle(target);
.tooltip('_fixTitle') show(target);
.tooltip('show')
.attr('title', originalTitle) target.addEventListener('mouseout', hideTooltip);
.tooltip('_fixTitle');
}
} }
function genericSuccess(e) { function genericSuccess(e) {

View file

@ -3,9 +3,7 @@ import isEmojiUnicodeSupported from '../emoji/support';
import { initEmojiMap, getEmojiInfo, emojiFallbackImageSrc, emojiImageTag } from '../emoji'; import { initEmojiMap, getEmojiInfo, emojiFallbackImageSrc, emojiImageTag } from '../emoji';
class GlEmoji extends HTMLElement { class GlEmoji extends HTMLElement {
constructor() { connectedCallback() {
super();
this.initialize(); this.initialize();
} }
initialize() { initialize() {

View file

@ -13,6 +13,9 @@ import './toggler_behavior';
import './preview_markdown'; import './preview_markdown';
import initCollapseSidebarOnWindowResize from './collapse_sidebar_on_window_resize'; import initCollapseSidebarOnWindowResize from './collapse_sidebar_on_window_resize';
import initSelect2Dropdowns from './select2'; import initSelect2Dropdowns from './select2';
import { loadStartupCSS } from './load_startup_css';
loadStartupCSS();
installGlEmojiElement(); installGlEmojiElement();

View file

@ -0,0 +1,15 @@
export const loadStartupCSS = () => {
// We need to fallback to dispatching `load` in case our event listener was added too late
// or the browser environment doesn't load media=print.
// Do this on `window.load` so that the default deferred behavior takes precedence.
// https://gitlab.com/gitlab-org/gitlab/-/issues/239357
window.addEventListener(
'load',
() => {
document
.querySelectorAll('link[media=print]')
.forEach(x => x.dispatchEvent(new Event('load')));
},
{ once: true },
);
};

View file

@ -14,7 +14,6 @@ export default function initGFMInput($els) {
milestones: enableGFM, milestones: enableGFM,
mergeRequests: enableGFM, mergeRequests: enableGFM,
labels: enableGFM, labels: enableGFM,
vulnerabilities: enableGFM,
}); });
}); });
} }

View file

@ -0,0 +1,96 @@
import { flatten } from 'lodash';
import { s__ } from '~/locale';
import AccessorUtilities from '~/lib/utils/accessor';
import { shouldDisableShortcuts } from './shortcuts_toggle';
export const LOCAL_STORAGE_KEY = 'gl-keyboard-shortcuts-customizations';
let parsedCustomizations = {};
const localStorageIsSafe = AccessorUtilities.isLocalStorageAccessSafe();
if (localStorageIsSafe) {
try {
parsedCustomizations = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || '{}');
} catch (e) {
/* do nothing */
}
}
/**
* A map of command => keys of all keyboard shortcuts
* that have been customized by the user.
*
* @example
* { "globalShortcuts.togglePerformanceBar": ["p e r f"] }
*
* @type { Object.<string, string[]> }
*/
export const customizations = parsedCustomizations;
// All available commands
export const TOGGLE_PERFORMANCE_BAR = 'globalShortcuts.togglePerformanceBar';
/** All keybindings, grouped and ordered with descriptions */
export const keybindingGroups = [
{
groupId: 'globalShortcuts',
name: s__('KeyboardShortcuts|Global Shortcuts'),
keybindings: [
{
description: s__('KeyboardShortcuts|Toggle the Performance Bar'),
command: TOGGLE_PERFORMANCE_BAR,
// eslint-disable-next-line @gitlab/require-i18n-strings
defaultKeys: ['p b'],
},
],
},
]
// For each keybinding object, add a `customKeys` property populated with the
// user's custom keybindings (if the command has been customized).
// `customKeys` will be `undefined` if the command hasn't been customized.
.map(group => {
return {
...group,
keybindings: group.keybindings.map(binding => ({
...binding,
customKeys: customizations[binding.command],
})),
};
});
/**
* A simple map of command => keys. All user customizations are included in this map.
* This mapping is used to simplify `keysFor` below.
*
* @example
* { "globalShortcuts.togglePerformanceBar": ["p e r f"] }
*/
const commandToKeys = flatten(keybindingGroups.map(group => group.keybindings)).reduce(
(acc, binding) => {
acc[binding.command] = binding.customKeys || binding.defaultKeys;
return acc;
},
{},
);
/**
* Gets keyboard shortcuts associated with a command
*
* @param {string} command The command string. All command
* strings are available as imports from this file.
*
* @returns {string[]} An array of keyboard shortcut strings bound to the command
*
* @example
* import { keysFor, TOGGLE_PERFORMANCE_BAR } from '~/behaviors/shortcuts/keybindings'
*
* Mousetrap.bind(keysFor(TOGGLE_PERFORMANCE_BAR), handler);
*/
export const keysFor = command => {
if (shouldDisableShortcuts()) {
return [];
}
return commandToKeys[command];
};

Some files were not shown because too many files have changed in this diff Show more