Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-14 03:07:47 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-14 03:07:47 +0300
commite2d4a6dedbb55154ff9389dbe061fa74ccbae287 (patch)
tree13f364616561701ce2cea0426cad72f7914c5128
parent30080dfe0a7475f37cdcf95ad1b04ce1ea060e3c (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/dev-fixtures.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/docs.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml46
-rw-r--r--.gitlab/ci/memory.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml244
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml22
-rw-r--r--.gitlab/ci/setup.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/test-metadata.gitlab-ci.yml24
-rw-r--r--.haml-lint_todo.yml1
-rw-r--r--CHANGELOG-EE.md7
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_list.vue23
-rw-r--r--app/assets/javascripts/boards/icons/fullscreen_collapse.svg1
-rw-r--r--app/assets/javascripts/boards/icons/fullscreen_expand.svg1
-rw-r--r--app/assets/javascripts/boards/index.js6
-rw-r--r--app/assets/javascripts/boards/toggle_focus.js46
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue2
-rw-r--r--app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue1
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue3
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue8
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue4
-rw-r--r--app/assets/javascripts/ide/components/resizable_panel.vue31
-rw-r--r--app/assets/javascripts/ide/services/index.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions.js8
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js10
-rw-r--r--app/assets/javascripts/ide/stores/state.js2
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js35
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue6
-rw-r--r--app/assets/stylesheets/pages/boards.scss24
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/concerns/notes_actions.rb2
-rw-r--r--app/controllers/projects/usage_ping_controller.rb6
-rw-r--r--app/controllers/user_callouts_controller.rb2
-rw-r--r--app/helpers/access_tokens_helper.rb7
-rw-r--r--app/views/shared/tokens/_scopes_form.html.haml2
-rw-r--r--changelogs/unreleased/212331-move-features-to-core-issue-board-focus-mode.yml5
-rw-r--r--changelogs/unreleased/al-214347-web-ide-pipelines-usage.yml5
-rw-r--r--changelogs/unreleased/update-doorkeeper-503.yml5
-rw-r--r--config/initializers/zz_metrics.rb5
-rw-r--r--config/locales/doorkeeper.en.yml13
-rw-r--r--config/routes/project.rb1
-rw-r--r--db/post_migrate/20200511092714_update_undefined_confidence_from_vulnerabilities.rb35
-rw-r--r--db/structure.sql3
-rw-r--r--doc/api/epics.md1
-rw-r--r--doc/api/features.md42
-rw-r--r--doc/development/auto_devops.md6
-rw-r--r--doc/development/documentation/index.md2
-rw-r--r--doc/development/pipelines.md17
-rw-r--r--doc/user/application_security/index.md16
-rw-r--r--doc/user/application_security/sast/index.md82
-rw-r--r--doc/user/project/settings/project_access_tokens.md2
-rw-r--r--lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb13
-rw-r--r--lib/gitlab/logging/cloudflare_helper.rb2
-rw-r--r--lib/gitlab/metrics.rb93
-rw-r--r--lib/gitlab/metrics/rack_middleware.rb4
-rw-r--r--lib/gitlab/metrics/sidekiq_middleware.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/action_view.rb8
-rw-r--r--lib/gitlab/metrics/transaction.rb52
-rw-r--r--lib/gitlab/usage_data.rb1
-rw-r--r--lib/gitlab/usage_data_counters/web_ide_counter.rb6
-rw-r--r--locale/gitlab.pot24
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb28
-rw-r--r--spec/controllers/admin/requests_profiles_controller_spec.rb6
-rw-r--r--spec/controllers/admin/users_controller_spec.rb2
-rw-r--r--spec/controllers/application_controller_spec.rb2
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb2
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb2
-rw-r--r--spec/controllers/projects/usage_ping_controller_spec.rb68
-rw-r--r--spec/features/boards/boards_spec.rb11
-rw-r--r--spec/features/boards/focus_mode_spec.rb17
-rw-r--r--spec/frontend/alert_management/components/alert_management_list_spec.js35
-rw-r--r--spec/frontend/ide/components/commit_sidebar/list_spec.js2
-rw-r--r--spec/frontend/ide/components/pipelines/list_spec.js17
-rw-r--r--spec/frontend/ide/components/repo_commit_section_spec.js1
-rw-r--r--spec/frontend/ide/services/index_spec.js30
-rw-r--r--spec/frontend/ide/stores/mutations_spec.js24
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js72
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js128
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/lib/viewer_utils_spec.js20
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/viewers/download_viewer_spec.js28
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js59
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js81
-rw-r--r--spec/helpers/access_tokens_helper_spec.rb18
-rw-r--r--spec/initializers/zz_metrics_spec.rb4
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js27
-rw-r--r--spec/lib/gitlab/logging/cloudflare_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/background_transaction_spec.rb6
-rw-r--r--spec/lib/gitlab/metrics/rack_middleware_spec.rb25
-rw-r--r--spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb7
-rw-r--r--spec/lib/gitlab/metrics/subscribers/action_view_spec.rb6
-rw-r--r--spec/lib/gitlab/metrics/transaction_spec.rb165
-rw-r--r--spec/lib/gitlab/metrics/web_transaction_spec.rb144
-rw-r--r--spec/lib/gitlab/metrics_spec.rb78
-rw-r--r--spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb6
-rw-r--r--spec/routing/project_routing_spec.rb4
-rw-r--r--spec/support/helpers/usage_data_helpers.rb1
100 files changed, 1021 insertions, 1166 deletions
diff --git a/.gitlab/ci/dev-fixtures.gitlab-ci.yml b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
index a642a80eb85..47acf8facc8 100644
--- a/.gitlab/ci/dev-fixtures.gitlab-ci.yml
+++ b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
@@ -3,9 +3,9 @@
- .default-retry
- .default-cache
- .default-before_script
- - .use-pg9
+ - .use-pg11
stage: test
- needs: ["setup-test-env pg9"]
+ needs: ["setup-test-env"]
variables:
FIXTURE_PATH: "db/fixtures/development"
SEED_CYCLE_ANALYTICS: "true"
@@ -26,7 +26,7 @@ run-dev-fixtures-ee:
extends:
- .run-dev-fixtures
- .dev-fixtures:rules:ee-only
- - .use-pg9-ee
+ - .use-pg11-ee
script:
- scripts/gitaly-test-spawn
- cp ee/db/fixtures/development/* $FIXTURE_PATH
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index b9348f46b69..50dbef44598 100644
--- a/.gitlab/ci/docs.gitlab-ci.yml
+++ b/.gitlab/ci/docs.gitlab-ci.yml
@@ -65,9 +65,9 @@ graphql-reference-verify:
- .default-cache
- .default-before_script
- .docs:rules:graphql-reference-verify
- - .use-pg9
+ - .use-pg11
stage: test
- needs: ["setup-test-env pg9"]
+ needs: ["setup-test-env"]
script:
- bundle exec rake gitlab:graphql:check_docs
- bundle exec rake gitlab:graphql:check_schema
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 13dd5967bf6..b47f969106d 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -138,9 +138,9 @@ compile-assets pull-cache as-if-foss:
- .default-retry
- .default-cache
- .default-before_script
- - .use-pg9
+ - .use-pg11
stage: fixtures
- needs: ["setup-test-env pg9", "compile-assets pull-cache"]
+ needs: ["setup-test-env", "compile-assets pull-cache"]
script:
- date
- scripts/gitaly-test-spawn
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index 303ca19d099..341fa131876 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -38,28 +38,6 @@
paths:
- node_modules/
-.use-pg9:
- image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34"
- services:
- - name: postgres:9.6.17
- command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- - name: redis:alpine
- variables:
- POSTGRES_HOST_AUTH_METHOD: trust
- cache:
- key: "debian-stretch-ruby-2.6.6-pg9-node-12.x"
-
-.use-pg10:
- image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
- services:
- - name: postgres:10.12
- command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- - name: redis:alpine
- variables:
- POSTGRES_HOST_AUTH_METHOD: trust
- cache:
- key: "debian-stretch-ruby-2.6.6-pg10-node-12.x"
-
.use-pg11:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
services:
@@ -71,30 +49,6 @@
cache:
key: "debian-stretch-ruby-2.6.6-pg11-node-12.x"
-.use-pg9-ee:
- image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34"
- services:
- - name: postgres:9.6.17
- command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- - name: redis:alpine
- - name: elasticsearch:6.4.2
- variables:
- POSTGRES_HOST_AUTH_METHOD: trust
- cache:
- key: "debian-stretch-ruby-2.6.6-pg9-node-12.x"
-
-.use-pg10-ee:
- image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
- services:
- - name: postgres:10.12
- command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- - name: redis:alpine
- - name: elasticsearch:6.4.2
- variables:
- POSTGRES_HOST_AUTH_METHOD: trust
- cache:
- key: "debian-stretch-ruby-2.6.6-pg10-node-12.x"
-
.use-pg11-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
services:
diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml
index a24801908f6..79dfc88d132 100644
--- a/.gitlab/ci/memory.gitlab-ci.yml
+++ b/.gitlab/ci/memory.gitlab-ci.yml
@@ -8,7 +8,7 @@
memory-static:
extends: .only-code-memory-job-base
stage: test
- needs: ["setup-test-env pg9"]
+ needs: ["setup-test-env"]
variables:
SETUP_DB: "false"
script:
@@ -37,9 +37,9 @@ memory-static:
memory-on-boot:
extends:
- .only-code-memory-job-base
- - .use-pg9
+ - .use-pg11
stage: test
- needs: ["setup-test-env pg9", "compile-assets pull-cache"]
+ needs: ["setup-test-env", "compile-assets pull-cache"]
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index cba45a009d5..a361274ab97 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -1,5 +1,5 @@
.rails:needs:setup-and-assets:
- needs: ["setup-test-env pg9", "compile-assets pull-cache"]
+ needs: ["setup-test-env", "compile-assets pull-cache"]
.rails-job-base:
extends:
@@ -25,23 +25,11 @@
cache:
policy: pull-push
-setup-test-env pg11:
- extends:
- - .base-setup-test-env
- - .rails:rules:master-push--master-schedule-2-hourly--code-backstage
- - .use-pg11
-
-setup-test-env pg10:
- extends:
- - .base-setup-test-env
- - .rails:rules:master-schedule-nightly--code-backstage
- - .use-pg10
-
-setup-test-env pg9:
+setup-test-env:
extends:
- .base-setup-test-env
- .rails:rules:default-refs-code-backstage-qa
- - .use-pg9
+ - .use-pg11
static-analysis:
extends:
@@ -55,7 +43,7 @@ static-analysis:
script:
- scripts/static-analysis
cache:
- key: "ruby-2.6.6-pg9-rubocop"
+ key: "ruby-2.6.6-pg11-rubocop"
paths:
- vendor/ruby
- tmp/rubocop_cache
@@ -75,7 +63,7 @@ downtime_check:
.rspec-base:
extends: .rails-job-base
stage: test
- needs: ["setup-test-env pg9", "retrieve-tests-metadata", "compile-assets pull-cache"]
+ needs: ["setup-test-env", "retrieve-tests-metadata", "compile-assets pull-cache"]
script:
- source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration"
@@ -93,37 +81,37 @@ downtime_check:
reports:
junit: junit_rspec.xml
-.rspec-base-pg9:
+.rspec-base-pg11:
extends:
- .rspec-base
- .rails:rules:ee-and-foss
- - .use-pg9
+ - .use-pg11
.rspec-base-migration:
script:
- source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag level:migration"
-rspec migration pg9:
+rspec migration pg11:
extends:
- - .rspec-base-pg9
+ - .rspec-base-pg11
- .rspec-base-migration
parallel: 5
-rspec unit pg9:
- extends: .rspec-base-pg9
+rspec unit pg11:
+ extends: .rspec-base-pg11
parallel: 20
-rspec integration pg9:
- extends: .rspec-base-pg9
+rspec integration pg11:
+ extends: .rspec-base-pg11
parallel: 8
-rspec system pg9:
- extends: .rspec-base-pg9
+rspec system pg11:
+ extends: .rspec-base-pg11
parallel: 24
rspec fast_spec_helper:
- extends: .rspec-base-pg9
+ extends: .rspec-base-pg11
script:
- bin/rspec spec/fast_spec_helper.rb
@@ -131,9 +119,9 @@ rspec fast_spec_helper:
extends:
- .rails-job-base
- .rails:rules:ee-and-foss
- - .use-pg9
+ - .use-pg11
stage: test
- needs: ["setup-test-env pg9"]
+ needs: ["setup-test-env"]
db:migrate:reset:
extends: .db-job-base
@@ -204,18 +192,18 @@ rspec:coverage:
# We cannot use needs since it would mean needing 84 jobs (since most are parallelized)
# so we use `dependencies` here.
dependencies:
- - setup-test-env pg9
- - rspec migration pg9
- - rspec unit pg9
- - rspec integration pg9
- - rspec system pg9
- - rspec-ee migration pg9
- - rspec-ee unit pg9
- - rspec-ee integration pg9
- - rspec-ee system pg9
- - rspec-ee unit pg9 geo
- - rspec-ee integration pg9 geo
- - rspec-ee system pg9 geo
+ - 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
variables:
@@ -243,52 +231,52 @@ rspec:coverage:
- .rspec-base
- .rails:rules:ee-only
-.rspec-base-pg9-as-if-foss:
+.rspec-base-pg11-as-if-foss:
extends:
- .rspec-base-ee
- .as-if-foss
- - .use-pg9
- needs: ["setup-test-env pg9", "retrieve-tests-metadata", "compile-assets pull-cache as-if-foss"]
+ - .use-pg11
+ needs: ["setup-test-env", "retrieve-tests-metadata", "compile-assets pull-cache as-if-foss"]
-.rspec-ee-base-pg9:
+.rspec-ee-base-pg11:
extends:
- .rspec-base-ee
- - .use-pg9-ee
+ - .use-pg11-ee
-rspec migration pg9-as-if-foss:
+rspec migration pg11-as-if-foss:
extends:
- - .rspec-base-pg9-as-if-foss
+ - .rspec-base-pg11-as-if-foss
- .rspec-base-migration
parallel: 5
-rspec unit pg9-as-if-foss:
- extends: .rspec-base-pg9-as-if-foss
+rspec unit pg11-as-if-foss:
+ extends: .rspec-base-pg11-as-if-foss
parallel: 20
-rspec integration pg9-as-if-foss:
- extends: .rspec-base-pg9-as-if-foss
+rspec integration pg11-as-if-foss:
+ extends: .rspec-base-pg11-as-if-foss
parallel: 8
-rspec system pg9-as-if-foss:
- extends: .rspec-base-pg9-as-if-foss
+rspec system pg11-as-if-foss:
+ extends: .rspec-base-pg11-as-if-foss
parallel: 24
-rspec-ee migration pg9:
+rspec-ee migration pg11:
extends:
- - .rspec-ee-base-pg9
+ - .rspec-ee-base-pg11
- .rspec-base-migration
parallel: 2
-rspec-ee unit pg9:
- extends: .rspec-ee-base-pg9
+rspec-ee unit pg11:
+ extends: .rspec-ee-base-pg11
parallel: 10
-rspec-ee integration pg9:
- extends: .rspec-ee-base-pg9
+rspec-ee integration pg11:
+ extends: .rspec-ee-base-pg11
parallel: 4
-rspec-ee system pg9:
- extends: .rspec-ee-base-pg9
+rspec-ee system pg11:
+ extends: .rspec-ee-base-pg11
parallel: 6
.rspec-ee-base-geo:
@@ -298,20 +286,20 @@ rspec-ee system pg9:
- scripts/prepare_postgres_fdw.sh
- rspec_paralellized_job "--tag ~quarantine --tag geo"
-.rspec-ee-base-geo-pg9:
+.rspec-ee-base-geo-pg11:
extends:
- .rspec-ee-base-geo
- - .use-pg9-ee
+ - .use-pg11-ee
-rspec-ee unit pg9 geo:
- extends: .rspec-ee-base-geo-pg9
+rspec-ee unit pg11 geo:
+ extends: .rspec-ee-base-geo-pg11
parallel: 2
-rspec-ee integration pg9 geo:
- extends: .rspec-ee-base-geo-pg9
+rspec-ee integration pg11 geo:
+ extends: .rspec-ee-base-geo-pg11
-rspec-ee system pg9 geo:
- extends: .rspec-ee-base-geo-pg9
+rspec-ee system pg11 geo:
+ extends: .rspec-ee-base-geo-pg11
db:rollback geo:
extends:
@@ -322,117 +310,3 @@ db:rollback geo:
- bundle exec rake geo:db:migrate
# EE: default refs (MRs, master, schedules) jobs #
##################################################
-
-####################################################################
-# EE/FOSS: master non-scheduled and master 2-hourly scheduled jobs #
-.rspec-base-pg11:
- extends:
- - .rspec-base
- - .rails:rules:master-push--master-schedule-2-hourly--code-backstage
- - .use-pg11
- needs: ["setup-test-env pg11", "retrieve-tests-metadata", "compile-assets pull-cache"]
-
-rspec migration pg11:
- extends:
- - .rspec-base-pg11
- - .rspec-base-migration
- parallel: 5
-
-rspec unit pg11:
- extends: .rspec-base-pg11
- parallel: 20
-
-rspec integration pg11:
- extends: .rspec-base-pg11
- parallel: 8
-
-rspec system pg11:
- extends: .rspec-base-pg11
- parallel: 24
-# EE/FOSS: master non-scheduled and master 2-hourly scheduled jobs #
-####################################################################
-
-###############################################################
-# EE: master non-scheduled and master 2-hourly scheduled jobs #
-.rspec-ee-base-pg11:
- extends:
- - .rspec-base-ee
- - .use-pg11-ee
- needs: ["setup-test-env pg11", "retrieve-tests-metadata", "compile-assets pull-cache"]
-
-rspec-ee migration pg11:
- extends:
- - .rspec-ee-base-pg11
- - .rspec-base-migration
- - .rails:rules:master-push--master-schedule-2-hourly--code-backstage
- parallel: 2
-
-rspec-ee unit pg11:
- extends:
- - .rspec-ee-base-pg11
- - .rails:rules:master-push--master-schedule-2-hourly--code-backstage
- parallel: 10
-
-rspec-ee integration pg11:
- extends:
- - .rspec-ee-base-pg11
- - .rails:rules:master-push--master-schedule-2-hourly--code-backstage
- parallel: 4
-
-rspec-ee system pg11:
- extends:
- - .rspec-ee-base-pg11
- - .rails:rules:master-push--master-schedule-2-hourly--code-backstage
- parallel: 6
-# EE: master non-scheduled and master 2-hourly scheduled jobs #
-###############################################################
-
-##########################################
-# EE/FOSS: master nightly scheduled jobs #
-.rspec-base-pg10:
- extends:
- - .rspec-base
- - .rails:rules:master-schedule-nightly--code-backstage
- - .use-pg10
- needs: ["setup-test-env pg10", "retrieve-tests-metadata", "compile-assets pull-cache"]
-
-rspec migration pg10:
- extends:
- - .rspec-base-pg10
- - .rspec-base-migration
- parallel: 5
-
-rspec unit pg10:
- extends: .rspec-base-pg10
- parallel: 20
-
-rspec integration pg10:
- extends: .rspec-base-pg10
- parallel: 8
-
-rspec system pg10:
- extends: .rspec-base-pg10
- parallel: 24
-# EE/FOSS: master nightly scheduled jobs #
-##########################################
-
-##############################
-# EE: nightly scheduled jobs #
-.rspec-ee-base-geo-pg10:
- extends:
- - .rspec-ee-base-geo
- - .use-pg10-ee
- - .rails:rules:master-schedule-nightly--code-backstage-ee-only
- needs: ["setup-test-env pg10", "retrieve-tests-metadata", "compile-assets pull-cache"]
-
-rspec-ee unit pg10 geo:
- extends: .rspec-ee-base-geo-pg10
- parallel: 2
-
-rspec-ee integration pg10 geo:
- extends: .rspec-ee-base-geo-pg10
-
-rspec-ee system pg10 geo:
- extends: .rspec-ee-base-geo-pg10
-# EE: nightly scheduled jobs #
-##############################
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 5a91dc6ccc5..2f48f211a78 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -384,28 +384,6 @@
- <<: *if-default-refs
changes: *code-backstage-qa-patterns
-.rails:rules:master-push--master-schedule-2-hourly--code-backstage:
- rules:
- - <<: *if-master-push
- changes: *code-backstage-patterns
- - <<: *if-master-schedule-2-hourly
- - <<: *if-merge-request
- changes: [".gitlab/ci/rails.gitlab-ci.yml"]
-
-.rails:rules:master-schedule-nightly--code-backstage:
- rules:
- - <<: *if-master-schedule-nightly
- - <<: *if-merge-request
- changes: [".gitlab/ci/rails.gitlab-ci.yml"]
-
-.rails:rules:master-schedule-nightly--code-backstage-ee-only:
- rules:
- - <<: *if-not-ee
- when: never
- - <<: *if-master-schedule-nightly
- - <<: *if-merge-request
- changes: [".gitlab/ci/rails.gitlab-ci.yml"]
-
.rails:rules:ee-only:
rules:
- <<: *if-not-ee
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml
index 69314eb665b..9be495f1ef2 100644
--- a/.gitlab/ci/setup.gitlab-ci.yml
+++ b/.gitlab/ci/setup.gitlab-ci.yml
@@ -7,7 +7,7 @@ cache gems:
- .default-before_script
- .setup:rules:cache-gems
stage: test
- needs: ["setup-test-env pg9"]
+ needs: ["setup-test-env"]
variables:
SETUP_DB: "false"
script:
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index 198c0ff1e46..65cce76fc48 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -32,18 +32,18 @@ update-tests-metadata:
- .test-metadata:rules:update-tests-metadata
stage: post-test
dependencies:
- - setup-test-env pg9
- - rspec migration pg9
- - rspec unit pg9
- - rspec integration pg9
- - rspec system pg9
- - rspec-ee migration pg9
- - rspec-ee unit pg9
- - rspec-ee integration pg9
- - rspec-ee system pg9
- - rspec-ee unit pg9 geo
- - rspec-ee integration pg9 geo
- - rspec-ee system pg9 geo
+ - 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
cache:
policy: push
script:
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index cd1a007c6eb..b46995dd63c 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -18,7 +18,6 @@ linters:
- "app/views/admin/application_settings/_abuse.html.haml"
- "app/views/admin/application_settings/_diff_limits.html.haml"
- "app/views/admin/application_settings/_gitaly.html.haml"
- - "app/views/admin/application_settings/_influx.html.haml"
- "app/views/admin/application_settings/_ip_limits.html.haml"
- "app/views/admin/application_settings/_performance.html.haml"
- "app/views/admin/application_settings/_plantuml.html.haml"
diff --git a/CHANGELOG-EE.md b/CHANGELOG-EE.md
index 6ed305c1a28..b48dce65463 100644
--- a/CHANGELOG-EE.md
+++ b/CHANGELOG-EE.md
@@ -1,5 +1,12 @@
Please view this file on the master branch, on stable branches it's out of date.
+## 12.10.5 (2020-05-13)
+
+### Fixed (1 change)
+
+- Remove check for user being an applicable code owner. !31809
+
+
## 12.10.4 (2020-05-05)
- No changes.
diff --git a/Gemfile b/Gemfile
index 6bf8d63d939..ffd7873e7ad 100644
--- a/Gemfile
+++ b/Gemfile
@@ -26,7 +26,7 @@ gem 'marginalia', '~> 1.8.0'
# Authentication libraries
gem 'devise', '~> 4.6'
-gem 'doorkeeper', '~> 5.0.2'
+gem 'doorkeeper', '~> 5.0.3'
gem 'doorkeeper-openid_connect', '~> 1.6.3'
gem 'omniauth', '~> 1.8'
gem 'omniauth-auth0', '~> 2.0.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 01431cb3cc0..7291d5748b0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -245,7 +245,7 @@ GEM
docile (1.3.2)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
- doorkeeper (5.0.2)
+ doorkeeper (5.0.3)
railties (>= 4.2)
doorkeeper-openid_connect (1.6.3)
doorkeeper (>= 5.0, < 5.2)
@@ -1205,7 +1205,7 @@ DEPENDENCIES
diff_match_patch (~> 0.1.0)
diffy (~> 3.3)
discordrb-webhooks-blackst0ne (~> 3.3)
- doorkeeper (~> 5.0.2)
+ doorkeeper (~> 5.0.3)
doorkeeper-openid_connect (~> 1.6.3)
ed25519 (~> 1.2)
elasticsearch-api (~> 6.8)
diff --git a/app/assets/javascripts/alert_management/components/alert_management_list.vue b/app/assets/javascripts/alert_management/components/alert_management_list.vue
index 741f04cfd9b..6b2cee10fbb 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_list.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_list.vue
@@ -16,7 +16,7 @@ import createFlash from '~/flash';
import { s__ } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import getAlerts from '../graphql/queries/getAlerts.query.graphql';
-import { ALERTS_STATUS, ALERTS_STATUS_TABS } from '../constants';
+import { ALERTS_STATUS, ALERTS_STATUS_TABS, ALERTS_SEVERITY_LABELS } from '../constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
@@ -34,6 +34,11 @@ export default {
},
fields: [
{
+ key: 'severity',
+ label: s__('AlertManagement|Severity'),
+ tdClass: `${tdClass} rounded-top text-capitalize`,
+ },
+ {
key: 'startedAt',
label: s__('AlertManagement|Start time'),
tdClass,
@@ -68,6 +73,7 @@ export default {
[ALERTS_STATUS.ACKNOWLEDGED]: s__('AlertManagement|Acknowledged'),
[ALERTS_STATUS.RESOLVED]: s__('AlertManagement|Resolved'),
},
+ severityLabels: ALERTS_SEVERITY_LABELS,
statusTabs: ALERTS_STATUS_TABS,
components: {
GlEmptyState,
@@ -203,6 +209,21 @@ export default {
fixed
stacked="md"
>
+ <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>
diff --git a/app/assets/javascripts/boards/icons/fullscreen_collapse.svg b/app/assets/javascripts/boards/icons/fullscreen_collapse.svg
new file mode 100644
index 00000000000..6bd773dc4c5
--- /dev/null
+++ b/app/assets/javascripts/boards/icons/fullscreen_collapse.svg
@@ -0,0 +1 @@
+<svg width="17" height="17" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg"><path d="M.147 15.496l2.146-2.146-1.286-1.286a.55.55 0 0 1-.125-.616c.101-.238.277-.357.527-.357h4a.55.55 0 0 1 .402.17.55.55 0 0 1 .17.401v4c0 .25-.12.426-.358.527-.232.101-.437.06-.616-.125l-1.286-1.286-2.146 2.146-1.428-1.428zM14.996.646l1.428 1.43-2.146 2.145 1.286 1.286c.185.179.226.384.125.616-.101.238-.277.357-.527.357h-4a.55.55 0 0 1-.402-.17.55.55 0 0 1-.17-.401v-4c0-.25.12-.426.358-.527a.553.553 0 0 1 .616.125l1.286 1.286L14.996.647zm-13.42 0L3.72 2.794l1.286-1.286a.55.55 0 0 1 .616-.125c.238.101.357.277.357.527v4a.55.55 0 0 1-.17.402.55.55 0 0 1-.401.17h-4c-.25 0-.426-.12-.527-.358-.101-.232-.06-.437.125-.616l1.286-1.286L.147 2.075 1.575.647zm14.848 14.85l-1.428 1.428-2.146-2.146-1.286 1.286c-.179.185-.384.226-.616.125-.238-.101-.357-.277-.357-.527v-4a.55.55 0 0 1 .17-.402.55.55 0 0 1 .401-.17h4c.25 0 .426.12.527.358a.553.553 0 0 1-.125.616l-1.286 1.286 2.146 2.146z" fill-rule="evenodd"/></svg>
diff --git a/app/assets/javascripts/boards/icons/fullscreen_expand.svg b/app/assets/javascripts/boards/icons/fullscreen_expand.svg
new file mode 100644
index 00000000000..306073b8af2
--- /dev/null
+++ b/app/assets/javascripts/boards/icons/fullscreen_expand.svg
@@ -0,0 +1 @@
+<svg width="15" height="15" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg"><path d="M8.591 5.056l2.147-2.146-1.286-1.286a.55.55 0 0 1-.125-.616c.101-.238.277-.357.527-.357h4a.55.55 0 0 1 .402.17.55.55 0 0 1 .17.401v4c0 .25-.12.426-.358.527-.232.101-.437.06-.616-.125l-1.286-1.286-2.146 2.147-1.429-1.43zM5.018 8.553l1.429 1.43L4.3 12.127l1.286 1.286c.185.179.226.384.125.616-.101.238-.277.357-.527.357h-4a.55.55 0 0 1-.402-.17.55.55 0 0 1-.17-.401v-4c0-.25.12-.426.358-.527a.553.553 0 0 1 .616.125L2.872 10.7l2.146-2.147zm4.964 0l2.146 2.147 1.286-1.286a.55.55 0 0 1 .616-.125c.238.101.357.277.357.527v4a.55.55 0 0 1-.17.402.55.55 0 0 1-.401.17h-4c-.25 0-.426-.12-.527-.358-.101-.232-.06-.437.125-.616l1.286-1.286-2.147-2.146 1.43-1.429zM6.447 5.018l-1.43 1.429L2.873 4.3 1.586 5.586c-.179.185-.384.226-.616.125-.238-.101-.357-.277-.357-.527v-4a.55.55 0 0 1 .17-.402.55.55 0 0 1 .401-.17h4c.25 0 .426.12.527.358a.553.553 0 0 1-.125.616L4.3 2.872l2.147 2.146z" fill-rule="evenodd"/></svg>
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 7c41182d554..9ff7575ae09 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -6,7 +6,6 @@ import 'ee_else_ce/boards/models/list';
import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar';
import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
-import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
import toggleLabels from 'ee_else_ce/boards/toggle_labels';
import {
setPromotionState,
@@ -24,6 +23,7 @@ import './models/label';
import './models/assignee';
import { BoardType } from './constants';
+import toggleFocusMode from '~/boards/toggle_focus';
import FilteredSearchBoards from '~/boards/filtered_search_boards';
import eventHub from '~/boards/eventhub';
import sidebarEventHub from '~/sidebar/event_hub';
@@ -305,7 +305,7 @@ export default () => {
return {
modal: ModalStore.store,
store: boardsStore.state,
- ...getBoardsModalData($boardApp),
+ ...getBoardsModalData(),
canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
};
},
@@ -369,7 +369,7 @@ export default () => {
});
}
- toggleFocusMode(ModalStore, boardsStore, $boardApp);
+ toggleFocusMode(ModalStore, boardsStore);
toggleLabels();
mountMultipleBoardsSwitcher();
};
diff --git a/app/assets/javascripts/boards/toggle_focus.js b/app/assets/javascripts/boards/toggle_focus.js
index 2d1ec238274..a437a34c948 100644
--- a/app/assets/javascripts/boards/toggle_focus.js
+++ b/app/assets/javascripts/boards/toggle_focus.js
@@ -1 +1,45 @@
-export default () => {};
+import $ from 'jquery';
+import Vue from 'vue';
+import collapseIcon from './icons/fullscreen_collapse.svg';
+import expandIcon from './icons/fullscreen_expand.svg';
+
+export default (ModalStore, boardsStore) => {
+ const issueBoardsContent = document.querySelector('.content-wrapper > .js-focus-mode-board');
+
+ return new Vue({
+ el: document.getElementById('js-toggle-focus-btn'),
+ data: {
+ modal: ModalStore.store,
+ store: boardsStore.state,
+ isFullscreen: false,
+ },
+ methods: {
+ toggleFocusMode() {
+ $(this.$refs.toggleFocusModeButton).tooltip('hide');
+ issueBoardsContent.classList.toggle('is-focused');
+
+ this.isFullscreen = !this.isFullscreen;
+ },
+ },
+ template: `
+ <div class="board-extra-actions">
+ <a
+ href="#"
+ class="btn btn-default has-tooltip prepend-left-10 js-focus-mode-btn"
+ data-qa-selector="focus_mode_button"
+ role="button"
+ aria-label="Toggle focus mode"
+ title="Toggle focus mode"
+ ref="toggleFocusModeButton"
+ @click="toggleFocusMode">
+ <span v-show="isFullscreen">
+ ${collapseIcon}
+ </span>
+ <span v-show="!isFullscreen">
+ ${expandIcon}
+ </span>
+ </a>
+ </div>
+ `,
+ });
+};
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 40cd2178e09..7cb31df85ce 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -37,7 +37,7 @@ export default {
</script>
<template>
- <resizable-panel :collapsible="false" :initial-width="340" side="left" class="flex-column">
+ <resizable-panel :initial-width="340" side="left" class="flex-column">
<template v-if="loading">
<div class="multi-file-commit-panel-inner">
<div v-for="n in 3" :key="n" class="multi-file-loading-container">
diff --git a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue
index 8adf0122fb4..ea744eab2cf 100644
--- a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue
+++ b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue
@@ -103,7 +103,6 @@ export default {
>
<resizable-panel
v-show="isOpen"
- :collapsible="false"
:initial-width="width"
:min-size="width"
:class="`ide-${side}-sidebar-${currentView}`"
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 1fc11de39d4..39d67b0762a 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -10,6 +10,8 @@ import Tab from '../../../vue_shared/components/tabs/tab.vue';
import EmptyState from '../../../pipelines/components/empty_state.vue';
import JobsList from '../jobs/list.vue';
+import IDEServices from '~/ide/services';
+
export default {
components: {
Icon,
@@ -47,6 +49,7 @@ export default {
},
created() {
this.fetchLatestPipeline();
+ IDEServices.pingUsage(this.currentProject.path_with_namespace);
},
methods: {
...mapActions('pipelines', ['fetchLatestPipeline']),
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 2e7e55a61c5..f716d884091 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -17,13 +17,7 @@ export default {
tooltip,
},
computed: {
- ...mapState([
- 'changedFiles',
- 'stagedFiles',
- 'rightPanelCollapsed',
- 'lastCommitMsg',
- 'unusedSeal',
- ]),
+ ...mapState(['changedFiles', 'stagedFiles', 'lastCommitMsg', 'unusedSeal']),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommittedChanges', 'activeFile']),
...mapGetters('commit', ['discardDraftButtonDisabled']),
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 559731dd248..a0305a8f52f 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -31,7 +31,6 @@ export default {
rightPaneIsOpen: 'isOpen',
}),
...mapState([
- 'rightPanelCollapsed',
'viewer',
'panelResizing',
'currentActivityView',
@@ -118,9 +117,6 @@ export default {
});
}
},
- rightPanelCollapsed() {
- this.refreshEditorDimensions();
- },
viewer() {
if (!this.file.pending) {
this.createEditorInstance();
diff --git a/app/assets/javascripts/ide/components/resizable_panel.vue b/app/assets/javascripts/ide/components/resizable_panel.vue
index 7277fcb7617..86a4622401c 100644
--- a/app/assets/javascripts/ide/components/resizable_panel.vue
+++ b/app/assets/javascripts/ide/components/resizable_panel.vue
@@ -1,5 +1,5 @@
<script>
-import { mapActions, mapState } from 'vuex';
+import { mapActions } from 'vuex';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
export default {
@@ -7,10 +7,6 @@ export default {
PanelResizer,
},
props: {
- collapsible: {
- type: Boolean,
- required: true,
- },
initialWidth: {
type: Number,
required: true,
@@ -31,11 +27,6 @@ export default {
};
},
computed: {
- ...mapState({
- collapsed(state) {
- return state[`${this.side}PanelCollapsed`];
- },
- }),
panelStyle() {
if (!this.collapsed) {
return {
@@ -47,33 +38,17 @@ export default {
},
},
methods: {
- ...mapActions(['setPanelCollapsedStatus', 'setResizingStatus']),
- toggleFullbarCollapsed() {
- if (this.collapsed && this.collapsible) {
- this.setPanelCollapsedStatus({
- side: this.side,
- collapsed: !this.collapsed,
- });
- }
- },
+ ...mapActions(['setResizingStatus']),
},
maxSize: window.innerWidth / 2,
};
</script>
<template>
- <div
- :class="{
- 'is-collapsed': collapsed && collapsible,
- }"
- :style="panelStyle"
- class="multi-file-commit-panel"
- @click="toggleFullbarCollapsed"
- >
+ <div :style="panelStyle" class="multi-file-commit-panel">
<slot></slot>
<panel-resizer
:size.sync="width"
- :enabled="!collapsed"
:start-size="initialWidth"
:min-size="minSize"
:max-size="$options.maxSize"
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index 3d11c683711..1767d961259 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -96,4 +96,8 @@ export default {
const commitSha = getters.lastCommit.id;
return Api.commitPipelines(getters.currentProject.path_with_namespace, commitSha);
},
+ pingUsage(projectPath) {
+ const url = `${gon.relative_url_root}/${projectPath}/usage_ping/web_ide_pipelines_count`;
+ return axios.post(url);
+ },
};
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 89422c8a526..e32b5ac7bdc 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -24,14 +24,6 @@ export const closeAllFiles = ({ state, dispatch }) => {
state.openFiles.forEach(file => dispatch('closeFile', file));
};
-export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => {
- if (side === 'left') {
- commit(types.SET_LEFT_PANEL_COLLAPSED, collapsed);
- } else {
- commit(types.SET_RIGHT_PANEL_COLLAPSED, collapsed);
- }
-};
-
export const setResizingStatus = ({ commit }, resizing) => {
commit(types.SET_RESIZING_STATUS, resizing);
};
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index d088d6d5cba..5c78bfefa04 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -2,8 +2,6 @@ export const SET_INITIAL_DATA = 'SET_INITIAL_DATA';
export const TOGGLE_LOADING = 'TOGGLE_LOADING';
export const SET_LAST_COMMIT_DATA = 'SET_LAST_COMMIT_DATA';
export const SET_LAST_COMMIT_MSG = 'SET_LAST_COMMIT_MSG';
-export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED';
-export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED';
export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS';
export const SET_EMPTY_STATE_SVGS = 'SET_EMPTY_STATE_SVGS';
export const SET_LINKS = 'SET_LINKS';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 8372c4c4de0..12ac10df206 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -29,16 +29,6 @@ export default {
});
}
},
- [types.SET_LEFT_PANEL_COLLAPSED](state, collapsed) {
- Object.assign(state, {
- leftPanelCollapsed: collapsed,
- });
- },
- [types.SET_RIGHT_PANEL_COLLAPSED](state, collapsed) {
- Object.assign(state, {
- rightPanelCollapsed: collapsed,
- });
- },
[types.SET_RESIZING_STATUS](state, resizing) {
Object.assign(state, {
panelResizing: resizing,
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index 0fd6a448283..0c95c22e8f8 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -15,8 +15,6 @@ export default () => ({
parentTreeUrl: '',
trees: {},
projects: {},
- leftPanelCollapsed: false,
- rightPanelCollapsed: false,
panelResizing: false,
entries: {},
viewer: viewerTypes.edit,
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index d2f2ceac16e..966e6d42b80 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -224,12 +224,45 @@ export function getBaseURL() {
}
/**
+ * Returns true if url is an absolute URL
+ *
+ * @param {String} url
+ */
+export function isAbsolute(url) {
+ return /^https?:\/\//.test(url);
+}
+
+/**
+ * Returns true if url is a root-relative URL
+ *
+ * @param {String} url
+ */
+export function isRootRelative(url) {
+ return /^\//.test(url);
+}
+
+/**
* Returns true if url is an absolute or root-relative URL
*
* @param {String} url
*/
export function isAbsoluteOrRootRelative(url) {
- return /^(https?:)?\//.test(url);
+ return isAbsolute(url) || isRootRelative(url);
+}
+
+/**
+ * Converts a relative path to an absolute or a root relative path depending
+ * on what is passed as a basePath.
+ *
+ * @param {String} path Relative path, eg. ../img/img.png
+ * @param {String} basePath Absolute or root relative path, eg. /user/project or
+ * https://gitlab.com/user/project
+ */
+export function relativePathToAbsolute(path, basePath) {
+ const absolute = isAbsolute(basePath);
+ const base = absolute ? basePath : `file:///${basePath}`;
+ const url = new URL(path, base);
+ return absolute ? url.href : decodeURIComponent(url.pathname);
}
/**
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js b/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js
index da0b45110e2..b7fa73bc197 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js
@@ -26,8 +26,8 @@ const fileExtensionViewers = {
export function viewerInformationForPath(path) {
if (!path) return null;
const name = path.split('/').pop();
- const viewerName =
- fileNameViewers[name] || fileExtensionViewers[name ? name.split('.').pop() : ''] || '';
+ const extension = name.includes('.') && name.split('.').pop();
+ const viewerName = fileNameViewers[name] || fileExtensionViewers[extension];
return viewers[viewerName];
}
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index cc45ad0e5ca..bfd13a4b10f 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -1,5 +1,7 @@
<script>
import $ from 'jquery';
+import '~/behaviors/markdown/render_gfm';
+
import { GlSkeletonLoading } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
@@ -78,7 +80,7 @@ export default {
this.isLoading = false;
this.$nextTick(() => {
- $(this.$refs['markdown-preview']).renderGFM();
+ $(this.$refs.markdownPreview).renderGFM();
});
})
.catch(() => {
@@ -92,7 +94,7 @@ export default {
</script>
<template>
- <div ref="markdown-preview" class="md-previewer">
+ <div ref="markdownPreview" class="md-previewer">
<gl-skeleton-loading v-if="isLoading" />
<div v-else class="md" v-html="previewContent"></div>
</div>
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 11291dad28b..d755170fe1f 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -548,3 +548,27 @@
*/
height: $input-height;
}
+
+.issue-boards-content.is-focused {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background: $white;
+ z-index: 9000;
+
+ @include media-breakpoint-down(sm) {
+ padding-top: 10px;
+ }
+
+ .boards-list {
+ height: calc(100vh - #{$issue-boards-filter-height});
+ overflow-x: scroll;
+ }
+
+ .issue-boards-sidebar {
+ height: 100%;
+ top: 0;
+ }
+}
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 8414095d454..ee42baa8326 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -145,7 +145,7 @@ class Admin::UsersController < Admin::ApplicationController
password_confirmation: params[:user][:password_confirmation]
}
- password_params[:password_expires_at] = Time.now unless changing_own_password?
+ password_params[:password_expires_at] = Time.current unless changing_own_password?
user_params_with_pass.merge!(password_params)
end
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index 7dd2f6e5706..d4b0d3b2674 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -13,7 +13,7 @@ module NotesActions
end
def index
- current_fetched_at = Time.now.to_i
+ current_fetched_at = Time.current.to_i
notes_json = { notes: [], last_fetched_at: current_fetched_at }
diff --git a/app/controllers/projects/usage_ping_controller.rb b/app/controllers/projects/usage_ping_controller.rb
index ebdf28bd59c..0e2c8f879b1 100644
--- a/app/controllers/projects/usage_ping_controller.rb
+++ b/app/controllers/projects/usage_ping_controller.rb
@@ -10,4 +10,10 @@ class Projects::UsagePingController < Projects::ApplicationController
head(200)
end
+
+ def web_ide_pipelines_count
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_pipelines_count
+
+ head(200)
+ end
end
diff --git a/app/controllers/user_callouts_controller.rb b/app/controllers/user_callouts_controller.rb
index 4ee75218db1..06f422b9d90 100644
--- a/app/controllers/user_callouts_controller.rb
+++ b/app/controllers/user_callouts_controller.rb
@@ -5,7 +5,7 @@ class UserCalloutsController < ApplicationController
callout = ensure_callout
if callout.persisted?
- callout.update(dismissed_at: Time.now)
+ callout.update(dismissed_at: Time.current)
respond_to do |format|
format.json { head :ok }
end
diff --git a/app/helpers/access_tokens_helper.rb b/app/helpers/access_tokens_helper.rb
new file mode 100644
index 00000000000..877ad6db576
--- /dev/null
+++ b/app/helpers/access_tokens_helper.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module AccessTokensHelper
+ def scope_description(prefix)
+ prefix == :project_access_token ? [:doorkeeper, :project_access_token_scope_desc] : [:doorkeeper, :scope_desc]
+ end
+end
diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml
index a5d3e1c8de0..82e32597c94 100644
--- a/app/views/shared/tokens/_scopes_form.html.haml
+++ b/app/views/shared/tokens/_scopes_form.html.haml
@@ -6,4 +6,4 @@
%fieldset.form-group.form-check
= check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}", class: "form-check-input qa-#{scope}-radio"
= label_tag ("#{prefix}_scopes_#{scope}"), scope, class: 'label-bold form-check-label'
- .text-secondary= t scope, scope: [:doorkeeper, :scope_desc]
+ .text-secondary= t scope, scope: scope_description(prefix)
diff --git a/changelogs/unreleased/212331-move-features-to-core-issue-board-focus-mode.yml b/changelogs/unreleased/212331-move-features-to-core-issue-board-focus-mode.yml
new file mode 100644
index 00000000000..436eef02d45
--- /dev/null
+++ b/changelogs/unreleased/212331-move-features-to-core-issue-board-focus-mode.yml
@@ -0,0 +1,5 @@
+---
+title: Moved issue board focus mode to Core and available for for everyone
+merge_request: 29200
+author:
+type: added
diff --git a/changelogs/unreleased/al-214347-web-ide-pipelines-usage.yml b/changelogs/unreleased/al-214347-web-ide-pipelines-usage.yml
new file mode 100644
index 00000000000..f60fe0e0748
--- /dev/null
+++ b/changelogs/unreleased/al-214347-web-ide-pipelines-usage.yml
@@ -0,0 +1,5 @@
+---
+title: Add Web IDE pipelines usage counter
+merge_request: 31658
+author:
+type: added
diff --git a/changelogs/unreleased/update-doorkeeper-503.yml b/changelogs/unreleased/update-doorkeeper-503.yml
new file mode 100644
index 00000000000..f11dbd74ed8
--- /dev/null
+++ b/changelogs/unreleased/update-doorkeeper-503.yml
@@ -0,0 +1,5 @@
+---
+title: Update doorkeeper to latest version 5.0.3
+merge_request: 31673
+author:
+type: other
diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb
index a71c729e7f2..26f6743f480 100644
--- a/config/initializers/zz_metrics.rb
+++ b/config/initializers/zz_metrics.rb
@@ -135,7 +135,6 @@ end
# loading of our custom migration templates.
if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && defined?(Rails::Generators))
require 'pathname'
- require 'influxdb'
require 'connection_pool'
require 'method_source'
@@ -193,10 +192,6 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d
GC::Profiler.enable
- Gitlab::Cluster::LifecycleEvents.on_worker_start do
- Gitlab::Metrics::Samplers::InfluxSampler.initialize_instance.start
- end
-
module TrackNewRedisConnections
def connect(*args)
val = super
diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
index c9dbde23d35..93293a0236c 100644
--- a/config/locales/doorkeeper.en.yml
+++ b/config/locales/doorkeeper.en.yml
@@ -88,6 +88,19 @@ en:
Grants read-only access to the user's profile data using OpenID Connect.
email:
Grants read-only access to the user's primary email address using OpenID Connect.
+ project_access_token_scope_desc:
+ api:
+ Grants complete read/write access to the scoped project API.
+ read_api:
+ Grants read access to the scoped project API.
+ read_repository:
+ Allows read-only access (pull) to the repository.
+ write_repository:
+ Allows read-write access (pull, push) to the repository.
+ read_registry:
+ Allows read-access (pull) to container registry images if the project is private and authorization is required.
+ write_registry:
+ Allows write-access (push) to container registry.
flash:
applications:
create:
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 0cd880a8c46..829249707ac 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -468,6 +468,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
scope :usage_ping, controller: :usage_ping do
post :web_ide_clientside_preview
+ post :web_ide_pipelines_count
end
# Deprecated unscoped routing.
diff --git a/db/post_migrate/20200511092714_update_undefined_confidence_from_vulnerabilities.rb b/db/post_migrate/20200511092714_update_undefined_confidence_from_vulnerabilities.rb
new file mode 100644
index 00000000000..d6611ddbd66
--- /dev/null
+++ b/db/post_migrate/20200511092714_update_undefined_confidence_from_vulnerabilities.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class UpdateUndefinedConfidenceFromVulnerabilities < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ INDEX_NAME = 'index_vulnerability_on_id_and_confidence_eq_zero'
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+ BATCH_SIZE = 1_000
+ INTERVAL = 2.minutes
+
+ # 87_602 records to be updated on GitLab.com
+ def up
+ # create temporary index for undefined vulnerabilities
+ add_concurrent_index(:vulnerabilities, :id, where: 'confidence = 0', name: INDEX_NAME)
+
+ return unless Gitlab.ee?
+
+ migration = Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilityConfidenceLevel
+ migration_name = migration.to_s.demodulize
+ relation = migration::Vulnerability.undefined_confidence
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ migration_name,
+ INTERVAL,
+ batch_size: BATCH_SIZE)
+ end
+
+ def down
+ # no-op
+ # temporary index is to be dropped in a different migration in an upcoming release
+ remove_concurrent_index(:vulnerabilities, :id, where: 'confidence = 0', name: INDEX_NAME)
+ # This migration can not be reversed because we can not know which records had undefined confidence
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 9406b024eee..ad50cae5ca2 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -10843,6 +10843,8 @@ CREATE UNIQUE INDEX index_vulnerability_occurrences_on_uuid ON public.vulnerabil
CREATE INDEX index_vulnerability_occurrences_on_vulnerability_id ON public.vulnerability_occurrences USING btree (vulnerability_id);
+CREATE INDEX index_vulnerability_on_id_and_confidence_eq_zero ON public.vulnerabilities USING btree (id) WHERE (confidence = 0);
+
CREATE UNIQUE INDEX index_vulnerability_scanners_on_project_id_and_external_id ON public.vulnerability_scanners USING btree (project_id, external_id);
CREATE UNIQUE INDEX index_vulnerability_user_mentions_on_note_id ON public.vulnerability_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
@@ -13764,6 +13766,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200506154421
20200507221434
20200508091106
+20200511092714
20200511145545
\.
diff --git a/doc/api/epics.md b/doc/api/epics.md
index 0cffc1bee85..6ca6f04b741 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -67,6 +67,7 @@ GET /groups/:id/epics?state=opened
| `updated_before` | datetime | no | Return epics updated on or before the given time |
| `include_ancestor_groups` | boolean | no | Include epics from the requested group's ancestors. Default is `false` |
| `include_descendant_groups` | boolean | no | Include epics from the requested group's descendants. Default is `true` |
+| `my_reaction_emoji` | string | no | Return epics reacted by the authenticated user by the given emoji. `None` returns epics not given a reaction. `Any` returns epics given at least one reaction. Introduced in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31479)|
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics
diff --git a/doc/api/features.md b/doc/api/features.md
index a43f2daa93f..78457076064 100644
--- a/doc/api/features.md
+++ b/doc/api/features.md
@@ -32,6 +32,16 @@ Example response:
]
},
{
+ "name": "my_user_feature",
+ "state": "on",
+ "gates": [
+ {
+ "key": "percentage_of_actors",
+ "value": 34
+ }
+ ]
+ },
+ {
"name": "new_library",
"state": "on",
"gates": [
@@ -58,6 +68,7 @@ POST /features/:name
| --------- | ---- | -------- | ----------- |
| `name` | string | yes | Name of the feature to create or update |
| `value` | integer/string | yes | `true` or `false` to enable/disable, or an integer for percentage of time |
+| `key` | string | no | `percentage_of_actors` or `percentage_of_time` (default) |
| `feature_group` | string | no | A Feature group name |
| `user` | string | no | A GitLab username |
| `group` | string | no | A GitLab group's path, for example `gitlab-org` |
@@ -89,6 +100,37 @@ Example response:
}
```
+### Set percentage of actors rollout
+
+Rollout to percentage of users.
+
+```plaintext
+POST https://gitlab.example.com/api/v4/features/my_user_feature?private_token=<your_access_token>
+Content-Type: application/x-www-form-urlencoded
+value=42&key=percentage_of_actors&
+```
+
+Example response:
+
+```json
+{
+ "name": "my_user_feature",
+ "state": "conditional",
+ "gates": [
+ {
+ "key": "boolean",
+ "value": false
+ },
+ {
+ "key": "percentage_of_time",
+ "value": 42
+ }
+ ]
+}
+```
+
+Rolls out the `my_user_feature` to `42%` of users.
+
## Delete a feature
Removes a feature gate. Response is equal when the gate exists, or doesn't.
diff --git a/doc/development/auto_devops.md b/doc/development/auto_devops.md
index dba68974dd5..7d0c020ef96 100644
--- a/doc/development/auto_devops.md
+++ b/doc/development/auto_devops.md
@@ -1,7 +1,11 @@
# Auto DevOps development guide
This document provides a development guide for contributors to
-[Auto DevOps](../topics/autodevops/index.md)
+[Auto DevOps](../topics/autodevops/index.md).
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+An [Auto DevOps technical walk-through](https://youtu.be/G7RTLeToz9E)
+is also available on YouTube.
## Development
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index ac96cfd77d8..9c42328810d 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -530,7 +530,7 @@ on or off when markdownlint was enabled on the docs.
#### Vale
-[Vale](https://errata-ai.github.io/vale/) is a grammar, style, and word usage linter
+[Vale](https://errata-ai.gitbook.io/vale/) is a grammar, style, and word usage linter
for the English language. Vale's configuration is stored in the
[`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/blob/master/.vale.ini) file
located in the root directory of the [GitLab repository](https://gitlab.com/gitlab-org/gitlab).
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index d15ac8d5320..6f46075c711 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -70,11 +70,7 @@ that are scoped to a single [configuration parameter](../ci/yaml/README.md#confi
| `.default-retry` | Allows a job to [retry](../ci/yaml/README.md#retry) upon `unknown_failure`, `api_failure`, `runner_system_failure`, `job_execution_timeout`, or `stuck_or_timeout_failure`. |
| `.default-before_script` | Allows a job to use a default `before_script` definition suitable for Ruby/Rails tasks that may need a database running (e.g. tests). |
| `.default-cache` | Allows a job to use a default `cache` definition suitable for Ruby/Rails and frontend tasks. |
-| `.use-pg9` | Allows a job to use the `postgres:9.6.17` and `redis:alpine` services. |
-| `.use-pg10` | Allows a job to use the `postgres:10.12` and `redis:alpine` services. |
| `.use-pg11` | Allows a job to use the `postgres:11.6` and `redis:alpine` services. |
-| `.use-pg9-ee` | Same as `.use-pg9` but also use the `docker.elastic.co/elasticsearch/elasticsearch:6.4.2` services. |
-| `.use-pg10-ee` | Same as `.use-pg10` but also use the `docker.elastic.co/elasticsearch/elasticsearch:6.4.2` services. |
| `.use-pg11-ee` | Same as `.use-pg11` but also use the `docker.elastic.co/elasticsearch/elasticsearch:6.4.2` services. |
| `.use-kaniko` | Allows a job to use the `kaniko` tool to build Docker images. |
| `.as-if-foss` | Simulate the FOSS project by setting the `FOSS_ONLY='1'` environment variable. |
@@ -159,10 +155,9 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
| Where? | PG version |
| ------ | ------ |
-| MRs | 9.6 |
-| `master` (non-scheduled pipelines) | 9.6 / 11 |
-| 2-hourly scheduled pipelines | 9.6 / 11 |
-| Nightly scheduled pipeline | 9.6 / 10 |
+| MRs | 11 |
+| `master` (non-scheduled pipelines) | 11 |
+| 2-hourly scheduled pipelines | 11 |
### Long-term plan
@@ -215,7 +210,7 @@ graph RL;
click 1-4 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8356616&udv=0"
1-5["gitlab:assets:compile pull-cache (22 minutes)"];
click 1-5 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914312&udv=0"
- 1-6["setup-test-env pg11 (9.6 minutes)"];
+ 1-6["setup-test-env (9.6 minutes)"];
click 1-6 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914315&udv=0"
1-7["review-stop-failed-deployment"];
1-8["dependency_scanning"];
@@ -329,7 +324,7 @@ graph RL;
click 1-4 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8356616&udv=0"
1-5["gitlab:assets:compile pull-cache (22 minutes)"];
click 1-5 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914312&udv=0"
- 1-6["setup-test-env pg11 (9.6 minutes)"];
+ 1-6["setup-test-env (9.6 minutes)"];
click 1-6 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914315&udv=0"
1-7["review-stop-failed-deployment"];
1-8["dependency_scanning"];
@@ -468,7 +463,7 @@ graph RL;
click 1-4 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8356616&udv=0"
1-5["gitlab:assets:compile pull-cache (22 minutes)"];
click 1-5 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914312&udv=0"
- 1-6["setup-test-env pg11 (9.6 minutes)"];
+ 1-6["setup-test-env (9.6 minutes)"];
click 1-6 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914315&udv=0"
1-7["review-stop-failed-deployment"];
1-8["dependency_scanning"];
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index ee69aa0cd39..572619f7630 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -321,7 +321,8 @@ You can do it quickly by following the hyperlink given to run a new pipeline.
### Getting error message `sast job: stage parameter should be [some stage name here]`
-When including a security job template like [`SAST`](sast/index.md#configuration),
+When [including](../../ci/yaml/README.md#includetemplate) a `.gitlab-ci.yml` template
+like [`SAST.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml),
the following error may occur, depending on your GitLab CI/CD configuration:
```plaintext
@@ -334,22 +335,23 @@ This error appears when the included job's stage (named `test`) isn't declared i
To fix this issue, you can either:
- Add a `test` stage in your `.gitlab-ci.yml`.
-- Change the default stage of the included security jobs. For example, with `SAST`:
+- Change the default stage of the included security jobs. For example, with SpotBugs (SAST):
```yaml
include:
template: SAST.gitlab-ci.yml
- sast:
+ spotbugs-sast:
stage: unit-tests
```
-[Learn more on overriding the SAST template](sast/index.md#overriding-the-sast-template).
+[Learn more on overriding SAST jobs](sast/index.md#overriding-sast-jobs).
All the security scanning tools define their stage, so this error can occur with all of them.
### Getting error message `sast job: config key may not be used with 'rules': only/except`
-When including a security job template like [`SAST`](sast/index.md#overriding-the-sast-template),
+When [including](../../ci/yaml/README.md#includetemplate) a `.gitlab-ci.yml` template
+like [`SAST.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml),
the following error may occur, depending on your GitLab CI/CD configuration:
```plaintext
@@ -358,14 +360,14 @@ Found errors in your .gitlab-ci.yml:
jobs:sast config key may not be used with `rules`: only/except
```
-This error appears when the included job's `rules` configuration has been [overridden](sast/index.md#overriding-the-sast-template)
+This error appears when the included job's `rules` configuration has been [overridden](sast/index.md#overriding-sast-jobs)
with [the deprecated `only` or `except` syntax.](../../ci/yaml/README.md#onlyexcept-basic)
To fix this issue, you must either:
- [Transition your `only/except` syntax to `rules`](#transitioning-your-onlyexcept-syntax-to-rules).
- (Temporarily) [Pin your templates to the deprecated versions](#pin-your-templates-to-the-deprecated-versions)
-[Learn more on overriding the SAST template](sast/index.md#overriding-the-sast-template).
+[Learn more on overriding SAST jobs](sast/index.md#overriding-sast-jobs).
#### Transitioning your `only/except` syntax to `rules`
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 698a96bf607..cd713db0797 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -48,14 +48,12 @@ A pipeline consists of multiple jobs, including SAST and DAST scanning. If any j
## Requirements
-To run a SAST job, by default, you need GitLab Runner with the
-[`docker`](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode) or
-[`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html#running-privileged-containers-for-the-runners)
-executor running in privileged mode. If you're using the shared Runners on GitLab.com,
-this is enabled by default.
+To run SAST jobs, by default, you need GitLab Runner with the
+[`docker`](https://docs.gitlab.com/runner/executors/docker.html) or
+[`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor.
+If you're using the shared Runners on GitLab.com, this is enabled by default.
-Privileged mode is not necessary if you've [disabled Docker in Docker
-for SAST](#disabling-docker-in-docker-for-sast).
+Beginning with GitLab 13.0, Docker privileged mode is necessary only if you've [enabled Docker-in-Docker for SAST](#enabling-docker-in-docker).
CAUTION: **Caution:** Our SAST jobs currently expect a Linux container type. Windows containers are not yet supported.
@@ -114,15 +112,13 @@ include:
- template: SAST.gitlab-ci.yml
```
-The included template will create a `sast` job in your CI/CD pipeline and scan
+The included template will create SAST jobs in your CI/CD pipeline and scan
your project's source code for possible vulnerabilities.
The results will be saved as a
[SAST report artifact](../../../ci/pipelines/job_artifacts.md#artifactsreportssast-ultimate)
that you can later download and analyze. Due to implementation limitations, we
-always take the latest SAST artifact available. Behind the scenes, the
-[GitLab SAST Docker image](https://gitlab.com/gitlab-org/security-products/sast)
-is used to detect the languages or frameworks used, and in turn runs the matching scan tools.
+always take the latest SAST artifact available.
### Customizing the SAST settings
@@ -143,23 +139,24 @@ variables:
Because the template is [evaluated before](../../../ci/yaml/README.md#include)
the pipeline configuration, the last mention of the variable takes precedence.
-### Overriding the SAST template
+### Overriding SAST jobs
CAUTION: **Deprecation:**
Beginning in GitLab 13.0, the use of [`only` and `except`](../../../ci/yaml/README.md#onlyexcept-basic)
is no longer supported. When overriding the template, you must use [`rules`](../../../ci/yaml/README.md#rules) instead.
-If you want to override the job definition (for example, change properties like
-`variables` or `dependencies`), you need to declare a `sast` job after the
-template inclusion and specify any additional keys under it. For example:
+If you want to override a job definition (for example, change properties like
+`variables` or `dependencies`), you need to declare a job with the same name as the SAST job to override, after the
+template inclusion and specify any additional keys under it.
+For example, this enables `FAIL_NEVER` for the `spotbugs` analyzer:
```yaml
include:
- template: SAST.gitlab-ci.yml
-sast:
+spotbugs-sast:
variables:
- CI_DEBUG_TRACE: "true"
+ FAIL_NEVER: 1
```
### Using environment variables to pass credentials for private repositories
@@ -177,44 +174,27 @@ you can use the `MAVEN_CLI_OPTS` environment variable.
Read more on [how to use private Maven repositories](../index.md#using-private-maven-repos).
-### Disabling Docker in Docker for SAST
+### Enabling Docker-in-Docker
-You can avoid the need for Docker in Docker by running the individual analyzers.
-This does not require running the executor in privileged mode. For example:
+If needed, you can restore the behavior of SAST prior to %13.0 by enabling back Docker-in-Docker.
+You need GitLab Runner with the [`docker`](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode), and the variable `SAST_DISABLE_DIND` set to `false`:
```yaml
include:
- template: SAST.gitlab-ci.yml
variables:
- SAST_DISABLE_DIND: "true"
+ SAST_DISABLE_DIND: "false"
```
-This will create individual `<analyzer-name>-sast` jobs for each analyzer that runs in your CI/CD pipeline.
-
-By removing Docker-in-Docker (DIND), GitLab relies on [Linguist](https://github.com/github/linguist)
-to start relevant analyzers depending on the detected repository language(s) instead of the
-[orchestrator](https://gitlab.com/gitlab-org/security-products/dependency-scanning/). However, there
-are some differences in the way repository languages are detected between DIND and non-DIND. You can
-observe these differences by checking both Linguist and the common library. For instance, Linguist
-looks for `*.java` files to spin up the [SpotBugs](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs)
-image, while orchestrator only looks for the existence of `pom.xml`, `build.xml`, `gradlew`,
-`grailsw`, or `mvnw`. GitLab uses Linguist to detect new file types in the default branch.
-When introducing files or dependencies for a new language or package manager, the
-corresponding scans won't be triggered in the MR, and will only run on the default branch once the
-MR is merged. This will be addressed by [#211702](https://gitlab.com/gitlab-org/gitlab/-/issues/211702).
-
-NOTE: **Note:**
-With the current language detection logic, any new languages or frameworks introduced within the
-context of a merge request don't trigger a corresponding scan. These scans only occur once the code
-is committed to the default branch.
+This will create a single `sast` job in your CI/CD pipeline
+instead of multiple `<analyzer-name>-sast` jobs.
#### Enabling Kubesec analyzer
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12752) in GitLab Ultimate 12.6.
-When [Docker in Docker is disabled](#disabling-docker-in-docker-for-sast),
-you will need to set `SCAN_KUBERNETES_MANIFESTS` to `"true"` to enable the
+You need to set `SCAN_KUBERNETES_MANIFESTS` to `"true"` to enable the
Kubesec analyzer. In `.gitlab-ci.yml`, define:
```yaml
@@ -222,7 +202,6 @@ include:
- template: SAST.gitlab-ci.yml
variables:
- SAST_DISABLE_DIND: "true"
SCAN_KUBERNETES_MANIFESTS: "true"
```
@@ -248,9 +227,6 @@ stages:
include:
- template: SAST.gitlab-ci.yml
-variables:
- SAST_DISABLE_DIND: "true"
-
build:
stage: build
script:
@@ -297,7 +273,7 @@ The following are Docker image-related variables.
| `SAST_ANALYZER_IMAGE_PREFIX` | **DEPRECATED**: Use `SECURE_ANALYZERS_PREFIX` instead. |
| `SAST_ANALYZER_IMAGE_TAG` | **DEPRECATED:** Override the Docker tag of the default images. Read more about [customizing analyzers](analyzers.md). |
| `SAST_DEFAULT_ANALYZERS` | Override the names of default images. Read more about [customizing analyzers](analyzers.md). |
-| `SAST_DISABLE_DIND` | Disable Docker in Docker and run analyzers [individually](#disabling-docker-in-docker-for-sast). |
+| `SAST_DISABLE_DIND` | Disable Docker-in-Docker and run analyzers [individually](#enabling-docker-in-docker). This variable is `true` by default. |
#### Vulnerability filters
@@ -317,26 +293,23 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre
#### Docker-in-Docker orchestrator
-The following variables configure the Docker-in-Docker orchestrator.
+The following variables configure the Docker-in-Docker orchestrator, and therefore are only used when the Docker-in-Docker mode is [enabled](#enabling-docker-in-docker).
| Environment variable | Default value | Description |
|------------------------------------------|---------------|-------------|
-| `SAST_ANALYZER_IMAGES` | | Comma-separated list of custom images. Default images are still enabled. Read more about [customizing analyzers](analyzers.md). Not available when [Docker-in-Docker is disabled](#disabling-docker-in-docker-for-sast). |
-| `SAST_PULL_ANALYZER_IMAGES` | 1 | Pull the images from the Docker registry (set to 0 to disable). Read more about [customizing analyzers](analyzers.md). Not available when [Docker-in-Docker is disabled](#disabling-docker-in-docker-for-sast). |
+| `SAST_ANALYZER_IMAGES` | | Comma-separated list of custom images. Default images are still enabled. Read more about [customizing analyzers](analyzers.md). |
+| `SAST_PULL_ANALYZER_IMAGES` | 1 | Pull the images from the Docker registry (set to 0 to disable). Read more about [customizing analyzers](analyzers.md). |
| `SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT` | 2m | Time limit for Docker client negotiation. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h` or `2h45m`. |
| `SAST_PULL_ANALYZER_IMAGE_TIMEOUT` | 5m | Time limit when pulling the image of an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h` or `2h45m`. |
| `SAST_RUN_ANALYZER_TIMEOUT` | 20m | Time limit when running an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h` or `2h45m`.|
-NOTE: **Note:**
-Timeout variables are not applicable for setups with [disabled Docker In Docker](index.md#disabling-docker-in-docker-for-sast).
-
#### Analyzer settings
Some analyzers can be customized with environment variables.
| Environment variable | Analyzer | Description |
|-----------------------------|----------|-------------|
-| `SCAN_KUBERNETES_MANIFESTS` | Kubesec | Set to `"true"` to scan Kubernetes manifests when [Docker in Docker](#disabling-docker-in-docker-for-sast) is disabled. |
+| `SCAN_KUBERNETES_MANIFESTS` | Kubesec | Set to `"true"` to scan Kubernetes manifests. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` environment variable. |
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
@@ -521,7 +494,7 @@ run successfully. For more information, see [Offline environments](../offline_de
To use SAST in an offline environment, you need:
-- To [disable Docker-In-Docker](#disabling-docker-in-docker-for-sast).
+- To keep Docker-In-Docker disabled (default).
- GitLab Runner with the [`docker` or `kubernetes` executor](#requirements).
- Docker Container Registry with locally available copies of SAST [analyzer](https://gitlab.com/gitlab-org/security-products/analyzers) images.
@@ -577,7 +550,6 @@ include:
variables:
SECURE_ANALYZERS_PREFIX: "localhost:5000/analyzers"
- SAST_DISABLE_DIND: "true"
```
The SAST job should now use local copies of the SAST analyzers to scan your code and generate
diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md
index 7f92d0c59fe..303a6f6d3be 100644
--- a/doc/user/project/settings/project_access_tokens.md
+++ b/doc/user/project/settings/project_access_tokens.md
@@ -47,7 +47,7 @@ the following table.
| Scope | Description |
| ------------------ | ----------- |
-| `api` | Grants complete read/write access to scoped project API. |
+| `api` | Grants complete read/write access to the scoped project API. |
| `read_api` | Grants read access to the scoped project API. |
| `read_registry` | Allows read-access (pull) to [container registry](../../packages/container_registry/index.md) images if a project is private and authorization is required. |
| `write_registry` | Allows write-access (push) to [container registry](../../packages/container_registry/index.md). |
diff --git a/lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb b/lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb
new file mode 100644
index 00000000000..f6ea61f4502
--- /dev/null
+++ b/lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class RemoveUndefinedVulnerabilityConfidenceLevel
+ def perform(start_id, stop_id)
+ end
+ end
+ end
+end
+
+Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilityConfidenceLevel.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilityConfidenceLevel')
diff --git a/lib/gitlab/logging/cloudflare_helper.rb b/lib/gitlab/logging/cloudflare_helper.rb
index 09a4cbc6146..5cffe335bb5 100644
--- a/lib/gitlab/logging/cloudflare_helper.rb
+++ b/lib/gitlab/logging/cloudflare_helper.rb
@@ -14,7 +14,7 @@ module Gitlab
def valid_cloudflare_header?(value)
return false unless value.present?
return false if value.length > 64
- return false if value.index(/[^[:alnum:]]/)
+ return false if value.index(/[^[A-Za-z0-9-]]/)
true
end
diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb
index d759ae24051..5fed3d38d7c 100644
--- a/lib/gitlab/metrics.rb
+++ b/lib/gitlab/metrics.rb
@@ -2,17 +2,106 @@
module Gitlab
module Metrics
- include Gitlab::Metrics::InfluxDb
include Gitlab::Metrics::Prometheus
+ include Gitlab::Metrics::Methods
+
+ EXECUTION_MEASUREMENT_BUCKETS = [0.001, 0.01, 0.1, 1].freeze
@error = false
def self.enabled?
- influx_metrics_enabled? || prometheus_metrics_enabled?
+ prometheus_metrics_enabled?
end
def self.error?
@error
end
+
+ # Tracks an event.
+ #
+ # See `Gitlab::Metrics::Transaction#add_event` for more details.
+ def self.add_event(*args)
+ current_transaction&.add_event(*args)
+ end
+
+ # Allow access from other metrics related middlewares
+ def self.current_transaction
+ Transaction.current
+ end
+
+ # Returns the prefix to use for the name of a series.
+ def self.series_prefix
+ @series_prefix ||= Gitlab::Runtime.sidekiq? ? 'sidekiq_' : 'rails_'
+ end
+
+ def self.settings
+ @settings ||= begin
+ current_settings = Gitlab::CurrentSettings.current_application_settings
+
+ {
+
+ method_call_threshold: current_settings[:metrics_method_call_threshold]
+
+ }
+ end
+ end
+
+ def self.method_call_threshold
+ # This is memoized since this method is called for every instrumented
+ # method. Loading data from an external cache on every method call slows
+ # things down too much.
+ # in milliseconds
+ @method_call_threshold ||= settings[:method_call_threshold]
+ end
+
+ # Measures the execution time of a block.
+ #
+ # Example:
+ #
+ # Gitlab::Metrics.measure(:find_by_username_duration) do
+ # UserFinder.new(some_username).find_by_username
+ # end
+ #
+ # name - The name of the field to store the execution time in.
+ #
+ # Returns the value yielded by the supplied block.
+ def self.measure(name)
+ trans = current_transaction
+
+ return yield unless trans
+
+ real_start = System.monotonic_time
+ cpu_start = System.cpu_time
+
+ retval = yield
+
+ cpu_stop = System.cpu_time
+ real_stop = System.monotonic_time
+
+ real_time = (real_stop - real_start)
+ cpu_time = cpu_stop - cpu_start
+
+ real_duration_seconds = fetch_histogram("gitlab_#{name}_real_duration_seconds".to_sym) do
+ docstring "Measure #{name}"
+ base_labels Transaction::BASE_LABELS
+ buckets EXECUTION_MEASUREMENT_BUCKETS
+ end
+
+ real_duration_seconds.observe(trans.labels, real_time)
+
+ cpu_duration_seconds = fetch_histogram("gitlab_#{name}_cpu_duration_seconds".to_sym) do
+ docstring "Measure #{name}"
+ base_labels Transaction::BASE_LABELS
+ buckets EXECUTION_MEASUREMENT_BUCKETS
+ with_feature "prometheus_metrics_measure_#{name}_cpu_duration"
+ end
+ cpu_duration_seconds.observe(trans.labels, cpu_time)
+
+ trans.increment("#{name}_real_time", real_time.in_milliseconds, false)
+ trans.increment("#{name}_cpu_time", cpu_time.in_milliseconds, false)
+ trans.increment("#{name}_call_count", 1, false)
+
+ retval
+ end
end
end
diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb
index 9aa97515961..c6a0457ffe5 100644
--- a/lib/gitlab/metrics/rack_middleware.rb
+++ b/lib/gitlab/metrics/rack_middleware.rb
@@ -20,10 +20,6 @@ module Gitlab
trans.add_event(:rails_exception)
raise error
- # Even in the event of an error we want to submit any metrics we
- # might've gathered up to this point.
- ensure
- trans.finish
end
retval
diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb
index 0b4485feea9..8dfb61046c4 100644
--- a/lib/gitlab/metrics/sidekiq_middleware.rb
+++ b/lib/gitlab/metrics/sidekiq_middleware.rb
@@ -17,8 +17,6 @@ module Gitlab
trans.add_event(:sidekiq_exception)
raise error
- ensure
- trans.finish
end
end
end
diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb
index 5bd21b8e5d1..24107e42aa9 100644
--- a/lib/gitlab/metrics/subscribers/action_view.rb
+++ b/lib/gitlab/metrics/subscribers/action_view.rb
@@ -26,23 +26,17 @@ module Gitlab
private
def track(event)
- values = values_for(event)
- tags = tags_for(event)
+ tags = tags_for(event)
self.class.gitlab_view_rendering_duration_seconds.observe(current_transaction.labels.merge(tags), event.duration)
current_transaction.increment(:view_duration, event.duration)
- current_transaction.add_metric(SERIES, values, tags)
end
def relative_path(path)
path.gsub(%r{^#{Rails.root}/?}, '')
end
- def values_for(event)
- { duration: event.duration }
- end
-
def tags_for(event)
path = relative_path(event.payload[:identifier])
diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb
index a7ccb2db777..b126efd2dd5 100644
--- a/lib/gitlab/metrics/transaction.rb
+++ b/lib/gitlab/metrics/transaction.rb
@@ -16,20 +16,18 @@ module Gitlab
# The series to store events (e.g. Git pushes) in.
EVENT_SERIES = 'events'
- attr_reader :tags, :values, :method, :metrics
+ attr_reader :tags, :method
def self.current
Thread.current[THREAD_KEY]
end
def initialize
- @metrics = []
@methods = {}
@started_at = nil
@finished_at = nil
- @values = Hash.new(0)
@tags = {}
@memory_before = 0
@@ -40,10 +38,6 @@ module Gitlab
@finished_at ? (@finished_at - @started_at) : 0.0
end
- def duration_milliseconds
- duration.in_milliseconds.to_i
- end
-
def thread_cpu_duration
System.thread_cpu_duration(@thread_cputime_start)
end
@@ -71,10 +65,6 @@ module Gitlab
Thread.current[THREAD_KEY] = nil
end
- def add_metric(series, values, tags = {})
- @metrics << Metric.new("#{::Gitlab::Metrics.series_prefix}#{series}", values, filter_tags(tags))
- end
-
# Tracks a business level event
#
# Business level events including events such as Git pushes, Emails being
@@ -85,7 +75,6 @@ module Gitlab
def add_event(event_name, tags = {})
filtered_tags = filter_tags(tags)
self.class.transaction_metric(event_name, :counter, prefix: 'event_', tags: filtered_tags).increment(filtered_tags.merge(labels))
- @metrics << Metric.new(EVENT_SERIES, { count: 1 }, filtered_tags.merge(event: event_name), :event)
end
# Returns a MethodCall object for the given name.
@@ -99,55 +88,16 @@ module Gitlab
def increment(name, value, use_prometheus = true)
self.class.transaction_metric(name, :counter).increment(labels, value) if use_prometheus
- @values[name] += value
end
def set(name, value, use_prometheus = true)
self.class.transaction_metric(name, :gauge).set(labels, value) if use_prometheus
- @values[name] = value
- end
-
- def finish
- track_self
- submit
- end
-
- def track_self
- values = { duration: duration_milliseconds, allocated_memory: allocated_memory }
-
- @values.each do |name, value|
- values[name] = value
- end
-
- add_metric('transactions', values, @tags)
- end
-
- def submit
- submit = @metrics.dup
-
- @methods.each do |name, method|
- submit << method.to_metric if method.above_threshold?
- end
-
- submit_hashes = submit.map do |metric|
- hash = metric.to_hash
- hash[:tags][:action] ||= action if action && !metric.event?
-
- hash
- end
-
- ::Gitlab::Metrics.submit_metrics(submit_hashes)
end
def labels
BASE_LABELS
end
- # returns string describing the action performed, usually the class plus method name.
- def action
- "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty?
- end
-
define_histogram :gitlab_transaction_cputime_seconds do
docstring 'Transaction thread cputime'
base_labels BASE_LABELS
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index f1297cccf6f..fef29a2ea54 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -172,7 +172,6 @@ module Gitlab
dependency_proxy_enabled: Gitlab.config.try(:dependency_proxy)&.enabled,
gitlab_shared_runners_enabled: alt_usage_data { Gitlab.config.gitlab_ci.shared_runners_enabled },
gravatar_enabled: alt_usage_data { Gitlab::CurrentSettings.gravatar_enabled? },
- influxdb_metrics_enabled: alt_usage_data { Gitlab::Metrics.influx_metrics_enabled? },
ldap_enabled: alt_usage_data { Gitlab.config.ldap.enabled },
mattermost_enabled: alt_usage_data { Gitlab.config.mattermost.enabled },
omniauth_enabled: alt_usage_data { Gitlab::Auth.omniauth_enabled? },
diff --git a/lib/gitlab/usage_data_counters/web_ide_counter.rb b/lib/gitlab/usage_data_counters/web_ide_counter.rb
index 14fe5d4e70b..00fcd42a9af 100644
--- a/lib/gitlab/usage_data_counters/web_ide_counter.rb
+++ b/lib/gitlab/usage_data_counters/web_ide_counter.rb
@@ -4,7 +4,7 @@ module Gitlab
module UsageDataCounters
class WebIdeCounter
extend RedisCounter
- KNOWN_EVENTS = %i[commits views merge_requests previews terminals].freeze
+ KNOWN_EVENTS = %i[commits views merge_requests previews terminals pipelines].freeze
PREFIX = 'web_ide'
class << self
@@ -24,6 +24,10 @@ module Gitlab
increment(redis_key('terminals'))
end
+ def increment_pipelines_count
+ increment(redis_key('pipelines'))
+ end
+
def increment_previews_count
return unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f55f06153fc..8cc3dc734a2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1787,6 +1787,9 @@ msgstr ""
msgid "AlertManagement|Service"
msgstr ""
+msgid "AlertManagement|Severity"
+msgstr ""
+
msgid "AlertManagement|Start time"
msgstr ""
@@ -2837,15 +2840,33 @@ msgstr ""
msgid "AuditLogs|Date"
msgstr ""
+msgid "AuditLogs|Failed to find %{type}. Please search for another %{type}."
+msgstr ""
+
+msgid "AuditLogs|Failed to find %{type}. Please try again."
+msgstr ""
+
+msgid "AuditLogs|Group Events"
+msgstr ""
+
msgid "AuditLogs|IP Address"
msgstr ""
+msgid "AuditLogs|No matching %{type} found."
+msgstr ""
+
msgid "AuditLogs|Object"
msgstr ""
+msgid "AuditLogs|Project Events"
+msgstr ""
+
msgid "AuditLogs|Target"
msgstr ""
+msgid "AuditLogs|User Events"
+msgstr ""
+
msgid "Aug"
msgstr ""
@@ -13809,6 +13830,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "NetworkPolicies|Something went wrong, unable to fetch policies"
+msgstr ""
+
msgid "Never"
msgstr ""
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb
new file mode 100644
index 00000000000..409c7d321f0
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Plan', :reliable do
+ describe 'Issue board focus mode' do
+ let(:project) do
+ QA::Resource::Project.fabricate_via_api! do |project|
+ project.name = 'sample-project-issue-board-focus-mode'
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ it 'focuses on issue board' do
+ project.visit!
+
+ Page::Project::Menu.perform(&:go_to_boards)
+ EE::Page::Component::IssueBoard::Show.perform do |show|
+ show.click_focus_mode_button
+
+ expect(show.focused_board).to be_visible
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/admin/requests_profiles_controller_spec.rb b/spec/controllers/admin/requests_profiles_controller_spec.rb
index 13123c8e486..629233b04e7 100644
--- a/spec/controllers/admin/requests_profiles_controller_spec.rb
+++ b/spec/controllers/admin/requests_profiles_controller_spec.rb
@@ -27,7 +27,7 @@ describe Admin::RequestsProfilesController do
end
context 'when loading HTML profile' do
- let(:basename) { "profile_#{Time.now.to_i}_execution.html" }
+ let(:basename) { "profile_#{Time.current.to_i}_execution.html" }
let(:sample_data) do
'<html> <body> <h1>Heading</h1> <p>paragraph.</p> </body> </html>'
@@ -42,7 +42,7 @@ describe Admin::RequestsProfilesController do
end
context 'when loading TXT profile' do
- let(:basename) { "profile_#{Time.now.to_i}_memory.txt" }
+ let(:basename) { "profile_#{Time.current.to_i}_memory.txt" }
let(:sample_data) do
<<~TXT
@@ -60,7 +60,7 @@ describe Admin::RequestsProfilesController do
end
context 'when loading PDF profile' do
- let(:basename) { "profile_#{Time.now.to_i}_anything.pdf" }
+ let(:basename) { "profile_#{Time.current.to_i}_anything.pdf" }
let(:sample_data) { 'mocked pdf content' }
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 387fc0407b6..7a7201a6454 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -296,7 +296,7 @@ describe Admin::UsersController do
it 'sets the new password to expire immediately' do
expect { update_password(user, 'AValidPassword1') }
- .to change { user.reload.password_expires_at }.to be_within(2.seconds).of(Time.now)
+ .to change { user.reload.password_expires_at }.to be_within(2.seconds).of(Time.current)
end
end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 2a913069acc..32dd67de368 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -25,7 +25,7 @@ describe ApplicationController do
end
it 'does not redirect if the user is under their password expiry' do
- user.password_expires_at = Time.now + 20010101
+ user.password_expires_at = Time.current + 20010101
expect(user.ldap_user?).to be_falsey
allow(controller).to receive(:current_user).and_return(user)
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index ab4f6d5054c..d44edb63635 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -27,7 +27,7 @@ describe Import::BitbucketController do
end
it "updates access token" do
- expires_at = Time.now + 1.day
+ expires_at = Time.current + 1.day
expires_in = 1.day
access_token = double(token: token,
secret: secret,
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 17d7e710614..f3357124501 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -609,7 +609,7 @@ describe Projects::IssuesController do
before do
project.add_developer(user)
- issue.update!(last_edited_by: deleted_user, last_edited_at: Time.now)
+ issue.update!(last_edited_by: deleted_user, last_edited_at: Time.current)
deleted_user.destroy
sign_in(user)
diff --git a/spec/controllers/projects/usage_ping_controller_spec.rb b/spec/controllers/projects/usage_ping_controller_spec.rb
index 43a2020b7aa..a68967c228f 100644
--- a/spec/controllers/projects/usage_ping_controller_spec.rb
+++ b/spec/controllers/projects/usage_ping_controller_spec.rb
@@ -6,45 +6,52 @@ describe Projects::UsagePingController do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
- describe 'POST #web_ide_clientside_preview' do
- subject { post :web_ide_clientside_preview, params: { namespace_id: project.namespace, project_id: project } }
+ before do
+ sign_in(user) if user
+ end
- before do
- sign_in(user) if user
- end
+ shared_examples 'counter is not increased' do
+ context 'when the user is not authenticated' do
+ let(:user) { nil }
- context 'when web ide clientside preview is enabled' do
- before do
- stub_application_setting(web_ide_clientside_preview_enabled: true)
- end
+ it 'returns 302' do
+ subject
- context 'when the user is not authenticated' do
- let(:user) { nil }
+ expect(response).to have_gitlab_http_status(:found)
+ end
+ end
- it 'returns 302' do
- subject
+ context 'when the user does not have access to the project' do
+ it 'returns 404' do
+ subject
- expect(response).to have_gitlab_http_status(:found)
- end
+ expect(response).to have_gitlab_http_status(:not_found)
end
+ end
+ end
- context 'when the user does not have access to the project' do
- it 'returns 404' do
- subject
+ shared_examples 'counter is increased' do |counter|
+ context 'when the authenticated user has access to the project' do
+ let(:user) { project.owner }
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it 'increments the usage counter' do
+ expect do
+ subject
+ end.to change { Gitlab::UsageDataCounters::WebIdeCounter.total_count(counter) }.by(1)
end
+ end
+ end
- context 'when the user has access to the project' do
- let(:user) { project.owner }
+ describe 'POST #web_ide_clientside_preview' do
+ subject { post :web_ide_clientside_preview, params: { namespace_id: project.namespace, project_id: project } }
- it 'increments the counter' do
- expect do
- subject
- end.to change { Gitlab::UsageDataCounters::WebIdeCounter.total_count('WEB_IDE_PREVIEWS_COUNT') }.by(1)
- end
+ context 'when web ide clientside preview is enabled' do
+ before do
+ stub_application_setting(web_ide_clientside_preview_enabled: true)
end
+
+ it_behaves_like 'counter is not increased'
+ it_behaves_like 'counter is increased', 'WEB_IDE_PREVIEWS_COUNT'
end
context 'when web ide clientside preview is not enabled' do
@@ -61,4 +68,11 @@ describe Projects::UsagePingController do
end
end
end
+
+ describe 'POST #web_ide_pipelines_count' do
+ subject { post :web_ide_pipelines_count, params: { namespace_id: project.namespace, project_id: project } }
+
+ it_behaves_like 'counter is not increased'
+ it_behaves_like 'counter is increased', 'WEB_IDE_PIPELINES_COUNT'
+ end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 0ac8e7c5fc8..cc0fdfbe278 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -549,6 +549,17 @@ describe 'Issue Boards', :js do
end
end
+ context 'issue board focus mode' do
+ before do
+ visit project_board_path(project, board)
+ wait_for_requests
+ end
+
+ it 'shows the button' do
+ expect(page).to have_link('Toggle focus mode')
+ end
+ end
+
context 'keyboard shortcuts' do
before do
visit project_board_path(project, board)
diff --git a/spec/features/boards/focus_mode_spec.rb b/spec/features/boards/focus_mode_spec.rb
new file mode 100644
index 00000000000..fff3cce3c1a
--- /dev/null
+++ b/spec/features/boards/focus_mode_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Issue Boards focus mode', :js do
+ let(:project) { create(:project, :public) }
+
+ before do
+ visit project_boards_path(project)
+
+ wait_for_requests
+ end
+
+ it 'shows focus mode button to guest users' do
+ expect(page).to have_selector('.board-extra-actions .js-focus-mode-btn')
+ end
+end
diff --git a/spec/frontend/alert_management/components/alert_management_list_spec.js b/spec/frontend/alert_management/components/alert_management_list_spec.js
index df845884b03..e47e2e97e28 100644
--- a/spec/frontend/alert_management/components/alert_management_list_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_list_spec.js
@@ -6,6 +6,7 @@ import {
GlLoadingIcon,
GlDropdown,
GlBadge,
+ GlIcon,
GlTab,
GlDropdownItem,
} from '@gitlab/ui';
@@ -30,6 +31,7 @@ describe('AlertManagementList', () => {
const findNumberOfAlertsBadge = () => wrapper.findAll(GlBadge);
const findDateFields = () => wrapper.findAll(TimeAgo);
const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem);
+ const findSeverityFields = () => wrapper.findAll('[data-testid="severityField"]');
function mountComponent({
props = {
@@ -189,6 +191,37 @@ describe('AlertManagementList', () => {
expect(findStatusDropdown().exists()).toBe(true);
});
+ it('shows correct severity icons', () => {
+ mountComponent({
+ props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
+ data: { alerts: mockAlerts, errored: false },
+ loading: false,
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.find(GlTable).exists()).toBe(true);
+ expect(
+ findAlertsTable()
+ .find(GlIcon)
+ .classes('icon-critical'),
+ ).toBe(true);
+ });
+ });
+
+ it('renders severity text', () => {
+ mountComponent({
+ props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
+ data: { alerts: mockAlerts, errored: false },
+ loading: false,
+ });
+
+ expect(
+ findSeverityFields()
+ .at(0)
+ .text(),
+ ).toBe('Critical');
+ });
+
describe('handle date fields', () => {
it('should display time ago dates when values provided', () => {
mountComponent({
@@ -200,6 +233,7 @@ describe('AlertManagementList', () => {
status: 'acknowledged',
startedAt: '2020-03-17T23:18:14.996Z',
endedAt: '2020-04-17T23:18:14.996Z',
+ severity: 'high',
},
],
errored: false,
@@ -219,6 +253,7 @@ describe('AlertManagementList', () => {
status: 'acknowledged',
startedAt: null,
endedAt: null,
+ severity: 'high',
},
],
errored: false,
diff --git a/spec/frontend/ide/components/commit_sidebar/list_spec.js b/spec/frontend/ide/components/commit_sidebar/list_spec.js
index ee209487665..2b5664ffc4e 100644
--- a/spec/frontend/ide/components/commit_sidebar/list_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/list_spec.js
@@ -21,8 +21,6 @@ describe('Multi-file editor commit sidebar list', () => {
keyPrefix: 'staged',
});
- vm.$store.state.rightPanelCollapsed = false;
-
vm.$mount();
});
diff --git a/spec/frontend/ide/components/pipelines/list_spec.js b/spec/frontend/ide/components/pipelines/list_spec.js
index 11e672b6685..d909a5e478e 100644
--- a/spec/frontend/ide/components/pipelines/list_spec.js
+++ b/spec/frontend/ide/components/pipelines/list_spec.js
@@ -7,10 +7,15 @@ import JobsList from '~/ide/components/jobs/list.vue';
import Tab from '~/vue_shared/components/tabs/tab.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { pipelines } from '../../../../javascripts/ide/mock_data';
+import IDEServices from '~/ide/services';
const localVue = createLocalVue();
localVue.use(Vuex);
+jest.mock('~/ide/services', () => ({
+ pingUsage: jest.fn(),
+}));
+
describe('IDE pipelines list', () => {
let wrapper;
@@ -25,14 +30,18 @@ describe('IDE pipelines list', () => {
};
const fetchLatestPipelineMock = jest.fn();
+ const pingUsageMock = jest.fn();
const failedStagesGetterMock = jest.fn().mockReturnValue([]);
+ const fakeProjectPath = 'alpha/beta';
const createComponent = (state = {}) => {
const { pipelines: pipelinesState, ...restOfState } = state;
const { defaultPipelines, ...defaultRestOfState } = defaultState;
const fakeStore = new Vuex.Store({
- getters: { currentProject: () => ({ web_url: 'some/url ' }) },
+ getters: {
+ currentProject: () => ({ web_url: 'some/url ', path_with_namespace: fakeProjectPath }),
+ },
state: {
...defaultRestOfState,
...restOfState,
@@ -46,6 +55,7 @@ describe('IDE pipelines list', () => {
},
actions: {
fetchLatestPipeline: fetchLatestPipelineMock,
+ pingUsage: pingUsageMock,
},
getters: {
jobsCount: () => 1,
@@ -77,6 +87,11 @@ describe('IDE pipelines list', () => {
expect(fetchLatestPipelineMock).toHaveBeenCalled();
});
+ it('pings pipeline usage', () => {
+ createComponent();
+ expect(IDEServices.pingUsage).toHaveBeenCalledWith(fakeProjectPath);
+ });
+
describe('when loading', () => {
let defaultPipelinesLoadingState;
beforeAll(() => {
diff --git a/spec/frontend/ide/components/repo_commit_section_spec.js b/spec/frontend/ide/components/repo_commit_section_spec.js
index 5ea03eb1593..237be018807 100644
--- a/spec/frontend/ide/components/repo_commit_section_spec.js
+++ b/spec/frontend/ide/components/repo_commit_section_spec.js
@@ -36,7 +36,6 @@ describe('RepoCommitSection', () => {
}),
);
- store.state.rightPanelCollapsed = false;
store.state.currentBranch = 'master';
store.state.changedFiles = [];
store.state.stagedFiles = [{ ...files[0] }, { ...files[1] }];
diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js
index f4d4122bd5a..3cb6e064aa2 100644
--- a/spec/frontend/ide/services/index_spec.js
+++ b/spec/frontend/ide/services/index_spec.js
@@ -254,4 +254,34 @@ describe('IDE services', () => {
});
});
});
+
+ describe('pingUsage', () => {
+ let mock;
+ let relativeUrlRoot;
+ const TEST_RELATIVE_URL_ROOT = 'blah-blah';
+
+ beforeEach(() => {
+ jest.spyOn(axios, 'post');
+ relativeUrlRoot = gon.relative_url_root;
+ gon.relative_url_root = TEST_RELATIVE_URL_ROOT;
+
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ gon.relative_url_root = relativeUrlRoot;
+ });
+
+ it('posts to usage endpoint', () => {
+ const TEST_PROJECT_PATH = 'foo/bar';
+ const axiosURL = `${TEST_RELATIVE_URL_ROOT}/${TEST_PROJECT_PATH}/usage_ping/web_ide_pipelines_count`;
+
+ mock.onPost(axiosURL).reply(200);
+
+ return services.pingUsage(TEST_PROJECT_PATH).then(() => {
+ expect(axios.post).toHaveBeenCalledWith(axiosURL);
+ });
+ });
+ });
});
diff --git a/spec/frontend/ide/stores/mutations_spec.js b/spec/frontend/ide/stores/mutations_spec.js
index bca355b820a..2eca9acb8d8 100644
--- a/spec/frontend/ide/stores/mutations_spec.js
+++ b/spec/frontend/ide/stores/mutations_spec.js
@@ -55,30 +55,6 @@ describe('Multi-file store mutations', () => {
});
});
- describe('SET_LEFT_PANEL_COLLAPSED', () => {
- it('sets left panel collapsed', () => {
- mutations.SET_LEFT_PANEL_COLLAPSED(localState, true);
-
- expect(localState.leftPanelCollapsed).toBeTruthy();
-
- mutations.SET_LEFT_PANEL_COLLAPSED(localState, false);
-
- expect(localState.leftPanelCollapsed).toBeFalsy();
- });
- });
-
- describe('SET_RIGHT_PANEL_COLLAPSED', () => {
- it('sets right panel collapsed', () => {
- mutations.SET_RIGHT_PANEL_COLLAPSED(localState, true);
-
- expect(localState.rightPanelCollapsed).toBeTruthy();
-
- mutations.SET_RIGHT_PANEL_COLLAPSED(localState, false);
-
- expect(localState.rightPanelCollapsed).toBeFalsy();
- });
- });
-
describe('CLEAR_STAGED_CHANGES', () => {
it('clears stagedFiles array', () => {
localState.stagedFiles.push('a');
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 54baabc3e81..c494033badd 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -323,20 +323,76 @@ describe('URL utility', () => {
});
});
- describe('isAbsoluteOrRootRelative', () => {
- const validUrls = ['https://gitlab.com/', 'http://gitlab.com/', '/users/sign_in'];
-
- const invalidUrls = [' https://gitlab.com/', './file/path', 'notanurl', '<a></a>'];
+ describe('isAbsolute', () => {
+ it.each`
+ url | valid
+ ${'https://gitlab.com/'} | ${true}
+ ${'http://gitlab.com/'} | ${true}
+ ${'/users/sign_in'} | ${false}
+ ${' https://gitlab.com'} | ${false}
+ ${'somepath.php?url=https://gitlab.com'} | ${false}
+ ${'notaurl'} | ${false}
+ ${'../relative_url'} | ${false}
+ ${'<a></a>'} | ${false}
+ `('returns $valid for $url', ({ url, valid }) => {
+ expect(urlUtils.isAbsolute(url)).toBe(valid);
+ });
+ });
- it.each(validUrls)(`returns true for %s`, url => {
- expect(urlUtils.isAbsoluteOrRootRelative(url)).toBe(true);
+ describe('isRootRelative', () => {
+ it.each`
+ url | valid
+ ${'https://gitlab.com/'} | ${false}
+ ${'http://gitlab.com/'} | ${false}
+ ${'/users/sign_in'} | ${true}
+ ${' https://gitlab.com'} | ${false}
+ ${'/somepath.php?url=https://gitlab.com'} | ${true}
+ ${'notaurl'} | ${false}
+ ${'../relative_url'} | ${false}
+ ${'<a></a>'} | ${false}
+ `('returns $valid for $url', ({ url, valid }) => {
+ expect(urlUtils.isRootRelative(url)).toBe(valid);
});
+ });
- it.each(invalidUrls)(`returns false for %s`, url => {
- expect(urlUtils.isAbsoluteOrRootRelative(url)).toBe(false);
+ describe('isAbsoluteOrRootRelative', () => {
+ it.each`
+ url | valid
+ ${'https://gitlab.com/'} | ${true}
+ ${'http://gitlab.com/'} | ${true}
+ ${'/users/sign_in'} | ${true}
+ ${' https://gitlab.com'} | ${false}
+ ${'/somepath.php?url=https://gitlab.com'} | ${true}
+ ${'notaurl'} | ${false}
+ ${'../relative_url'} | ${false}
+ ${'<a></a>'} | ${false}
+ `('returns $valid for $url', ({ url, valid }) => {
+ expect(urlUtils.isAbsoluteOrRootRelative(url)).toBe(valid);
});
});
+ describe('relativePathToAbsolute', () => {
+ it.each`
+ path | base | result
+ ${'./foo'} | ${'bar/'} | ${'/bar/foo'}
+ ${'../john.md'} | ${'bar/baz/foo.php'} | ${'/bar/john.md'}
+ ${'../images/img.png'} | ${'bar/baz/foo.php'} | ${'/bar/images/img.png'}
+ ${'../images/Image 1.png'} | ${'bar/baz/foo.php'} | ${'/bar/images/Image 1.png'}
+ ${'/images/img.png'} | ${'bar/baz/foo.php'} | ${'/images/img.png'}
+ ${'/images/img.png'} | ${'/bar/baz/foo.php'} | ${'/images/img.png'}
+ ${'../john.md'} | ${'/bar/baz/foo.php'} | ${'/bar/john.md'}
+ ${'../john.md'} | ${'///bar/baz/foo.php'} | ${'/bar/john.md'}
+ ${'/images/img.png'} | ${'https://gitlab.com/user/project/'} | ${'https://gitlab.com/images/img.png'}
+ ${'../images/img.png'} | ${'https://gitlab.com/user/project/'} | ${'https://gitlab.com/user/images/img.png'}
+ ${'../images/Image 1.png'} | ${'https://gitlab.com/user/project/'} | ${'https://gitlab.com/user/images/Image%201.png'}
+ `(
+ 'converts relative path "$path" with base "$base" to absolute path => "expected"',
+ ({ path, base, result }) => {
+ expect(urlUtils.relativePathToAbsolute(path, base)).toBe(result);
+ },
+ );
+ });
+
describe('isSafeUrl', () => {
const absoluteUrls = [
'http://example.org',
diff --git a/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js
index b0563f2f6de..16e7e4dd5cc 100644
--- a/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js
@@ -1,123 +1,21 @@
-import Vue from 'vue';
-import MockAdapter from 'axios-mock-adapter';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import waitForPromises from 'helpers/wait_for_promises';
+import { mount } from '@vue/test-utils';
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
-import axios from '~/lib/utils/axios_utils';
-import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
+import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import '~/behaviors/markdown/render_gfm';
describe('ContentViewer', () => {
- let vm;
- let mock;
-
- function createComponent(props) {
- const ContentViewer = Vue.extend(contentViewer);
- vm = mountComponent(ContentViewer, props);
- }
-
- afterEach(() => {
- vm.$destroy();
- if (mock) mock.restore();
- });
-
- it('markdown preview renders + loads rendered markdown from server', done => {
- mock = new MockAdapter(axios);
- mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).replyOnce(200, {
- body: '<b>testing</b>',
- });
-
- createComponent({
- path: 'test.md',
- content: '* Test',
- projectPath: 'testproject',
- type: 'markdown',
- });
-
- waitForPromises()
- .then(() => {
- expect(vm.$el.querySelector('.md-previewer').textContent).toContain('testing');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('renders image preview', done => {
- createComponent({
- path: GREEN_BOX_IMAGE_URL,
- fileSize: 1024,
- type: 'image',
- });
-
- vm.$nextTick()
- .then(() => {
- expect(vm.$el.querySelector('img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('renders fallback download control', done => {
- createComponent({
- path: 'somepath/test.abc',
- fileSize: 1024,
- });
-
- vm.$nextTick()
- .then(() => {
- expect(
- vm.$el
- .querySelector('.file-info')
- .textContent.trim()
- .replace(/\s+/, ' '),
- ).toEqual('test.abc (1.00 KiB)');
-
- expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toEqual('Download');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('renders fallback download control for file with a data URL path properly', done => {
- createComponent({
- path: 'data:application/octet-stream;base64,U0VMRUNUICfEhHNnc2cnIGZyb20gVGFibGVuYW1lOwoK',
- filePath: 'somepath/test.abc',
- });
-
- vm.$nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.file-info').textContent.trim()).toEqual('test.abc');
- expect(vm.$el.querySelector('.btn.btn-default')).toHaveAttr('download', 'test.abc');
- expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toEqual('Download');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('markdown preview receives the file path as a parameter', done => {
- mock = new MockAdapter(axios);
- jest.spyOn(axios, 'post');
- mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).reply(200, {
- body: '<b>testing</b>',
- });
-
- createComponent({
- path: 'test.md',
- content: '* Test',
- projectPath: 'testproject',
- type: 'markdown',
- filePath: 'foo/test.md',
+ let wrapper;
+
+ it.each`
+ path | type | selector | viewer
+ ${GREEN_BOX_IMAGE_URL} | ${'image'} | ${'img'} | ${'<image-viewer>'}
+ ${'myfile.md'} | ${'markdown'} | ${'.md-previewer'} | ${'<markdown-viewer>'}
+ ${'myfile.abc'} | ${undefined} | ${'[download]'} | ${'<download-viewer>'}
+ `('renders $viewer when file type="$type"', ({ path, type, selector }) => {
+ wrapper = mount(ContentViewer, {
+ propsData: { path, fileSize: 1024, type },
});
- vm.$nextTick()
- .then(() => {
- expect(axios.post).toHaveBeenCalledWith(
- `${gon.relative_url_root}/testproject/preview_markdown`,
- { path: 'foo/test.md', text: '* Test' },
- expect.any(Object),
- );
- })
- .then(done)
- .catch(done.fail);
+ expect(wrapper.find(selector).element).toExist();
});
});
diff --git a/spec/frontend/vue_shared/components/content_viewer/lib/viewer_utils_spec.js b/spec/frontend/vue_shared/components/content_viewer/lib/viewer_utils_spec.js
new file mode 100644
index 00000000000..facdaa86f84
--- /dev/null
+++ b/spec/frontend/vue_shared/components/content_viewer/lib/viewer_utils_spec.js
@@ -0,0 +1,20 @@
+import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
+
+describe('viewerInformationForPath', () => {
+ it.each`
+ path | type
+ ${'p/somefile.jpg'} | ${'image'}
+ ${'p/somefile.jpeg'} | ${'image'}
+ ${'p/somefile.bmp'} | ${'image'}
+ ${'p/somefile.ico'} | ${'image'}
+ ${'p/somefile.png'} | ${'image'}
+ ${'p/somefile.gif'} | ${'image'}
+ ${'p/somefile.md'} | ${'markdown'}
+ ${'p/md'} | ${undefined}
+ ${'p/png'} | ${undefined}
+ ${'p/md.png/a'} | ${undefined}
+ ${'p/some-file.php'} | ${undefined}
+ `('when path=$path, type=$type', ({ path, type }) => {
+ expect(viewerInformationForPath(path)?.id).toBe(type);
+ });
+});
diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/download_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/download_viewer_spec.js
new file mode 100644
index 00000000000..b83602e7bfc
--- /dev/null
+++ b/spec/frontend/vue_shared/components/content_viewer/viewers/download_viewer_spec.js
@@ -0,0 +1,28 @@
+import { mount } from '@vue/test-utils';
+import DownloadViewer from '~/vue_shared/components/content_viewer/viewers/download_viewer.vue';
+
+describe('DownloadViewer', () => {
+ let wrapper;
+
+ it.each`
+ path | filePath | fileSize | renderedName | renderedSize
+ ${'somepath/test.abc'} | ${undefined} | ${1024} | ${'test.abc'} | ${'1.00 KiB'}
+ ${'somepath/test.abc'} | ${undefined} | ${null} | ${'test.abc'} | ${''}
+ ${'data:application/unknown;base64,U0VMRUNU'} | ${'somepath/test.abc'} | ${2048} | ${'test.abc'} | ${'2.00 KiB'}
+ `(
+ 'renders the file name as "$renderedName" and shows size as "$renderedSize"',
+ ({ path, filePath, fileSize, renderedName, renderedSize }) => {
+ wrapper = mount(DownloadViewer, {
+ propsData: { path, filePath, fileSize },
+ });
+
+ const renderedFileInfo = wrapper.find('.file-info').text();
+
+ expect(renderedFileInfo).toContain(renderedName);
+ expect(renderedFileInfo).toContain(renderedSize);
+
+ expect(wrapper.find('.btn.btn-default').text()).toContain('Download');
+ expect(wrapper.find('.btn.btn-default').element).toHaveAttr('download', 'test.abc');
+ },
+ );
+});
diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js
index ef785b9f0f5..31e843297fa 100644
--- a/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js
@@ -1,45 +1,36 @@
-import { shallowMount } from '@vue/test-utils';
-
+import { mount } from '@vue/test-utils';
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
import ImageViewer from '~/vue_shared/components/content_viewer/viewers/image_viewer.vue';
describe('Image Viewer', () => {
- const requiredProps = {
- path: GREEN_BOX_IMAGE_URL,
- renderInfo: true,
- };
let wrapper;
- let imageInfo;
-
- function createElement({ props, includeRequired = true } = {}) {
- const data = includeRequired ? { ...requiredProps, ...props } : { ...props };
- wrapper = shallowMount(ImageViewer, {
- propsData: data,
+ it('renders image preview', () => {
+ wrapper = mount(ImageViewer, {
+ propsData: { path: GREEN_BOX_IMAGE_URL, fileSize: 1024 },
});
- imageInfo = wrapper.find('.image-info');
- }
-
- describe('file sizes', () => {
- it('should show the humanized file size when `renderInfo` is true and there is size info', () => {
- createElement({ props: { fileSize: 1024 } });
-
- expect(imageInfo.text()).toContain('1.00 KiB');
- });
-
- it('should not show the humanized file size when `renderInfo` is true and there is no size', () => {
- const FILESIZE_RE = /\d+(\.\d+)?\s*([KMGTP]i)*B/;
- createElement({ props: { fileSize: 0 } });
-
- // It shouldn't show any filesize info
- expect(imageInfo.text()).not.toMatch(FILESIZE_RE);
- });
-
- it('should not show any image information when `renderInfo` is false', () => {
- createElement({ props: { renderInfo: false } });
+ expect(wrapper.find('img').element).toHaveAttr('src', GREEN_BOX_IMAGE_URL);
+ });
- expect(imageInfo.exists()).toBe(false);
- });
+ describe('file sizes', () => {
+ it.each`
+ fileSize | renderInfo | elementExists | humanizedFileSize
+ ${1024} | ${true} | ${true} | ${'1.00 KiB'}
+ ${0} | ${true} | ${true} | ${''}
+ ${1024} | ${false} | ${false} | ${undefined}
+ `(
+ 'shows file size as "$humanizedFileSize", if fileSize=$fileSize and renderInfo=$renderInfo',
+ ({ fileSize, renderInfo, elementExists, humanizedFileSize }) => {
+ wrapper = mount(ImageViewer, {
+ propsData: { path: GREEN_BOX_IMAGE_URL, fileSize, renderInfo },
+ });
+
+ const imageInfo = wrapper.find('.image-info');
+
+ expect(imageInfo.exists()).toBe(elementExists);
+ expect(imageInfo.element?.textContent.trim()).toBe(humanizedFileSize);
+ },
+ );
});
});
diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
new file mode 100644
index 00000000000..587b9f07e53
--- /dev/null
+++ b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
@@ -0,0 +1,81 @@
+import $ from 'jquery';
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
+import { mount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import MarkdownViewer from '~/vue_shared/components/content_viewer/viewers/markdown_viewer.vue';
+
+describe('MarkdownViewer', () => {
+ let wrapper;
+ let mock;
+
+ const createComponent = props => {
+ wrapper = mount(MarkdownViewer, {
+ propsData: {
+ ...props,
+ path: 'test.md',
+ content: '* Test',
+ projectPath: 'testproject',
+ type: 'markdown',
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+
+ jest.spyOn(axios, 'post');
+ jest.spyOn($.fn, 'renderGFM');
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('success', () => {
+ beforeEach(() => {
+ mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).replyOnce(200, {
+ body: '<b>testing</b>',
+ });
+ });
+
+ it('renders an animation container while the markdown is loading', () => {
+ createComponent();
+
+ expect(wrapper.find('.animation-container')).toExist();
+ });
+
+ it('renders markdown preview preview renders and loads rendered markdown from server', () => {
+ createComponent();
+
+ return waitForPromises().then(() => {
+ expect(wrapper.find('.md-previewer').text()).toContain('testing');
+ });
+ });
+
+ it('receives the filePath as a parameter and passes it on to the server', () => {
+ createComponent({ filePath: 'foo/test.md' });
+
+ expect(axios.post).toHaveBeenCalledWith(
+ `${gon.relative_url_root}/testproject/preview_markdown`,
+ { path: 'foo/test.md', text: '* Test' },
+ expect.any(Object),
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).replyOnce(500, {
+ body: 'Internal Server Error',
+ });
+ });
+ it('renders an error message if loading the markdown preview fails', () => {
+ createComponent();
+
+ return waitForPromises().then(() => {
+ expect(wrapper.find('.md-previewer').text()).toContain('error');
+ });
+ });
+ });
+});
diff --git a/spec/helpers/access_tokens_helper_spec.rb b/spec/helpers/access_tokens_helper_spec.rb
new file mode 100644
index 00000000000..1d246d3f236
--- /dev/null
+++ b/spec/helpers/access_tokens_helper_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+describe AccessTokensHelper do
+ describe "#scope_description" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:prefix, :description_location) do
+ :personal_access_token | [:doorkeeper, :scope_desc]
+ :project_access_token | [:doorkeeper, :project_access_token_scope_desc]
+ end
+
+ with_them do
+ it { expect(helper.scope_description(prefix)).to eq(description_location) }
+ end
+ end
+end
diff --git a/spec/initializers/zz_metrics_spec.rb b/spec/initializers/zz_metrics_spec.rb
index b9a1919ceae..f41a807f1eb 100644
--- a/spec/initializers/zz_metrics_spec.rb
+++ b/spec/initializers/zz_metrics_spec.rb
@@ -5,15 +5,11 @@ require 'spec_helper'
describe 'instrument_classes' do
let(:config) { double(:config) }
- let(:influx_sampler) { double(:influx_sampler) }
-
before do
allow(config).to receive(:instrument_method)
allow(config).to receive(:instrument_methods)
allow(config).to receive(:instrument_instance_method)
allow(config).to receive(:instrument_instance_methods)
- allow(Gitlab::Metrics::Samplers::InfluxSampler).to receive(:initialize_instance).and_return(influx_sampler)
- allow(influx_sampler).to receive(:start)
allow(Gitlab::Application).to receive(:configure)
end
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index 44a75de4be2..8db29011da7 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -62,11 +62,6 @@ describe('RepoEditor', () => {
});
const findEditor = () => vm.$el.querySelector('.multi-file-editor-holder');
- const changeRightPanelCollapsed = () => {
- const { state } = vm.$store;
-
- state.rightPanelCollapsed = !state.rightPanelCollapsed;
- };
it('sets renderWhitespace to `all`', () => {
vm.$store.state.renderWhitespaceInCode = true;
@@ -319,17 +314,6 @@ describe('RepoEditor', () => {
spyOn(vm.editor, 'updateDiffView');
});
- it('calls updateDimensions when rightPanelCollapsed is changed', done => {
- changeRightPanelCollapsed();
-
- vm.$nextTick(() => {
- expect(vm.editor.updateDimensions).toHaveBeenCalled();
- expect(vm.editor.updateDiffView).toHaveBeenCalled();
-
- done();
- });
- });
-
it('calls updateDimensions when panelResizing is false', done => {
vm.$store.state.panelResizing = true;
@@ -407,17 +391,6 @@ describe('RepoEditor', () => {
expect(findEditor()).toHaveCss({ display: 'none' });
});
- it('should not update dimensions', done => {
- changeRightPanelCollapsed();
-
- vm.$nextTick()
- .then(() => {
- expect(vm.editor.updateDimensions).not.toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
- });
-
describe('when file view mode changes to editor', () => {
beforeEach(done => {
vm.file.viewMode = FILE_VIEW_MODE_EDITOR;
diff --git a/spec/lib/gitlab/logging/cloudflare_helper_spec.rb b/spec/lib/gitlab/logging/cloudflare_helper_spec.rb
index 8585943be3a..2b73fb7bc1c 100644
--- a/spec/lib/gitlab/logging/cloudflare_helper_spec.rb
+++ b/spec/lib/gitlab/logging/cloudflare_helper_spec.rb
@@ -19,7 +19,7 @@ describe Gitlab::Logging::CloudflareHelper do
end
context 'with normal headers' do
- let(:headers) { { 'Cf-Ray' => SecureRandom.hex, 'Cf-Request-Id' => SecureRandom.hex } }
+ let(:headers) { { 'Cf-Ray' => '592f0aa22b3dea38-IAD', 'Cf-Request-Id' => SecureRandom.hex } }
it 'adds Cf-Ray-Id and Cf-Request-Id' do
helper.store_cloudflare_headers!(payload, request)
diff --git a/spec/lib/gitlab/metrics/background_transaction_spec.rb b/spec/lib/gitlab/metrics/background_transaction_spec.rb
index d87d2c839ad..84f405d7369 100644
--- a/spec/lib/gitlab/metrics/background_transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/background_transaction_spec.rb
@@ -7,12 +7,6 @@ describe Gitlab::Metrics::BackgroundTransaction do
subject { described_class.new(test_worker_class) }
- describe '#action' do
- it 'returns transaction action name' do
- expect(subject.action).to eq('TestWorker#perform')
- end
- end
-
describe '#label' do
it 'returns labels based on class name' do
expect(subject.labels).to eq(controller: 'TestWorker', action: 'perform')
diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
index 1c1681cc5ab..dd1dbf7a1f4 100644
--- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
@@ -10,10 +10,6 @@ describe Gitlab::Metrics::RackMiddleware do
let(:env) { { 'REQUEST_METHOD' => 'GET', 'REQUEST_URI' => '/foo' } }
describe '#call' do
- before do
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
- end
-
it 'tracks a transaction' do
expect(app).to receive(:call).with(env).and_return('yay')
@@ -36,26 +32,5 @@ describe Gitlab::Metrics::RackMiddleware do
it 'returns a Transaction' do
expect(transaction).to be_an_instance_of(Gitlab::Metrics::WebTransaction)
end
-
- it 'stores the request method and URI in the transaction as values' do
- expect(transaction.values[:request_method]).to eq('GET')
- expect(transaction.values[:request_uri]).to eq('/foo')
- end
-
- context "when URI includes sensitive parameters" do
- let(:env) do
- {
- 'REQUEST_METHOD' => 'GET',
- 'REQUEST_URI' => '/foo?private_token=my-token',
- 'PATH_INFO' => '/foo',
- 'QUERY_STRING' => 'private_token=my_token',
- 'action_dispatch.parameter_filter' => [:private_token]
- }
- end
-
- it 'stores the request URI with the sensitive parameters filtered' do
- expect(transaction.values[:request_uri]).to eq('/foo?private_token=[FILTERED]')
- end
- end
end
end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
index bb95d5ab2ad..67336cf83e6 100644
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
@@ -17,8 +17,6 @@ describe Gitlab::Metrics::SidekiqMiddleware do
expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
.with(:sidekiq_queue_duration, instance_of(Float))
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
-
middleware.call(worker, message, :test) { nil }
end
@@ -32,8 +30,6 @@ describe Gitlab::Metrics::SidekiqMiddleware do
expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
.with(:sidekiq_queue_duration, instance_of(Float))
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
-
middleware.call(worker, {}, :test) { nil }
end
@@ -46,9 +42,6 @@ describe Gitlab::Metrics::SidekiqMiddleware do
expect_any_instance_of(Gitlab::Metrics::Transaction)
.to receive(:add_event).with(:sidekiq_exception)
- expect_any_instance_of(Gitlab::Metrics::Transaction)
- .to receive(:finish)
-
expect { middleware.call(worker, message, :test) }
.to raise_error(RuntimeError)
end
diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
index 25c0e7b695a..857e54d3432 100644
--- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
@@ -21,15 +21,9 @@ describe Gitlab::Metrics::Subscribers::ActionView do
describe '#render_template' do
it 'tracks rendering of a template' do
- values = { duration: 2.1 }
- tags = { view: 'app/views/x.html.haml' }
-
expect(transaction).to receive(:increment)
.with(:view_duration, 2.1)
- expect(transaction).to receive(:add_metric)
- .with(described_class::SERIES, values, tags)
-
subscriber.render_template(event)
end
diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb
index 08de2426c5a..cf46fa3e91c 100644
--- a/spec/lib/gitlab/metrics/transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/transaction_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
describe Gitlab::Metrics::Transaction do
let(:transaction) { described_class.new }
- let(:metric) { transaction.metrics[0] }
let(:sensitive_tags) do
{
@@ -13,12 +12,6 @@ describe Gitlab::Metrics::Transaction do
}
end
- shared_examples 'tag filter' do |sane_tags|
- it 'filters potentially sensitive tags' do
- expect(metric.tags).to eq(sane_tags)
- end
- end
-
describe '#duration' do
it 'returns the duration of a transaction in seconds' do
transaction.run { }
@@ -61,25 +54,6 @@ describe Gitlab::Metrics::Transaction do
end
end
- describe '#add_metric' do
- it 'adds a metric to the transaction' do
- transaction.add_metric('foo', value: 1)
-
- expect(metric.series).to eq('rails_foo')
- expect(metric.tags).to eq({})
- expect(metric.values).to eq(value: 1)
- end
-
- context 'with sensitive tags' do
- before do
- transaction
- .add_metric('foo', { value: 1 }, **sensitive_tags.merge(sane: 'yes'))
- end
-
- it_behaves_like 'tag filter', sane: 'yes'
- end
- end
-
describe '#method_call_for' do
it 'returns a MethodCall' do
method = transaction.method_call_for('Foo#bar', :Foo, '#bar')
@@ -88,133 +62,23 @@ describe Gitlab::Metrics::Transaction do
end
end
- describe '#increment' do
- it 'increments a counter' do
- transaction.increment(:time, 1)
- transaction.increment(:time, 2)
-
- values = metric_values(time: 3)
-
- expect(transaction).to receive(:add_metric)
- .with('transactions', values, {})
-
- transaction.track_self
- end
- end
-
- describe '#set' do
- it 'sets a value' do
- transaction.set(:number, 10)
-
- values = metric_values(number: 10)
-
- expect(transaction).to receive(:add_metric)
- .with('transactions', values, {})
-
- transaction.track_self
- end
- end
-
- describe '#finish' do
- it 'tracks the transaction details and submits them to Sidekiq' do
- expect(transaction).to receive(:track_self)
- expect(transaction).to receive(:submit)
-
- transaction.finish
- end
- end
-
- describe '#track_self' do
- it 'adds a metric for the transaction itself' do
- values = metric_values
-
- expect(transaction).to receive(:add_metric)
- .with('transactions', values, {})
-
- transaction.track_self
- end
- end
-
- describe '#submit' do
- it 'submits the metrics to Sidekiq' do
- transaction.track_self
-
- expect(Gitlab::Metrics).to receive(:submit_metrics)
- .with([an_instance_of(Hash)])
-
- transaction.submit
- end
-
- it 'adds the action as a tag for every metric' do
- allow(transaction)
- .to receive(:labels)
- .and_return(controller: 'Foo', action: 'bar')
-
- transaction.track_self
-
- hash = {
- series: 'rails_transactions',
- tags: { action: 'Foo#bar' },
- values: metric_values,
- timestamp: a_kind_of(Integer)
- }
-
- expect(Gitlab::Metrics).to receive(:submit_metrics)
- .with([hash])
-
- transaction.submit
- end
-
- it 'does not add an action tag for events' do
- allow(transaction)
- .to receive(:labels)
- .and_return(controller: 'Foo', action: 'bar')
-
- transaction.add_event(:meow)
-
- hash = {
- series: 'events',
- tags: { event: :meow },
- values: { count: 1 },
- timestamp: a_kind_of(Integer)
- }
-
- expect(Gitlab::Metrics).to receive(:submit_metrics)
- .with([hash])
-
- transaction.submit
- end
- end
-
describe '#add_event' do
- it 'adds a metric' do
- transaction.add_event(:meow)
+ let(:prometheus_metric) { instance_double(Prometheus::Client::Counter, increment: nil) }
- expect(metric).to be_an_instance_of(Gitlab::Metrics::Metric)
+ before do
+ allow(described_class).to receive(:transaction_metric).and_return(prometheus_metric)
end
- it "does not prefix the metric's series name" do
- transaction.add_event(:meow)
-
- expect(metric.series).to eq(described_class::EVENT_SERIES)
- end
-
- it 'tracks a counter for every event' do
- transaction.add_event(:meow)
-
- expect(metric.values).to eq(count: 1)
- end
+ it 'adds a metric' do
+ expect(prometheus_metric).to receive(:increment)
- it 'tracks the event name' do
transaction.add_event(:meow)
-
- expect(metric.tags).to eq(event: :meow)
end
it 'allows tracking of custom tags' do
- transaction.add_event(:bau, animal: 'dog')
+ expect(prometheus_metric).to receive(:increment).with(hash_including(animal: "dog"))
- expect(metric.tags).to eq(event: :bau, animal: 'dog')
+ transaction.add_event(:bau, animal: 'dog')
end
context 'with sensitive tags' do
@@ -222,16 +86,11 @@ describe Gitlab::Metrics::Transaction do
transaction.add_event(:baubau, **sensitive_tags.merge(sane: 'yes'))
end
- it_behaves_like 'tag filter', event: :baubau, sane: 'yes'
- end
- end
-
- private
+ it 'filters tags' do
+ expect(prometheus_metric).not_to receive(:increment).with(hash_including(sensitive_tags))
- def metric_values(opts = {})
- {
- duration: 0.0,
- allocated_memory: a_kind_of(Numeric)
- }.merge(opts)
+ transaction.add_event(:baubau, **sensitive_tags.merge(sane: 'yes'))
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/metrics/web_transaction_spec.rb b/spec/lib/gitlab/metrics/web_transaction_spec.rb
index 21a762dbf25..47f1bd3bd10 100644
--- a/spec/lib/gitlab/metrics/web_transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/web_transaction_spec.rb
@@ -5,6 +5,11 @@ require 'spec_helper'
describe Gitlab::Metrics::WebTransaction do
let(:env) { {} }
let(:transaction) { described_class.new(env) }
+ let(:prometheus_metric) { double("prometheus metric") }
+
+ before do
+ allow(described_class).to receive(:transaction_metric).and_return(prometheus_metric)
+ end
describe '#duration' do
it 'returns the duration of a transaction in seconds' do
@@ -40,15 +45,6 @@ describe Gitlab::Metrics::WebTransaction do
end
end
- describe '#add_metric' do
- it 'adds a metric to the transaction' do
- expect(Gitlab::Metrics::Metric).to receive(:new)
- .with('rails_foo', { number: 10 }, {})
-
- transaction.add_metric('foo', number: 10)
- end
- end
-
describe '#method_call_for' do
it 'returns a MethodCall' do
method = transaction.method_call_for('Foo#bar', :Foo, '#bar')
@@ -59,101 +55,17 @@ describe Gitlab::Metrics::WebTransaction do
describe '#increment' do
it 'increments a counter' do
- transaction.increment(:time, 1)
- transaction.increment(:time, 2)
-
- values = { duration: 0.0, time: 3, allocated_memory: a_kind_of(Numeric) }
+ expect(prometheus_metric).to receive(:increment).with({}, 1)
- expect(transaction).to receive(:add_metric)
- .with('transactions', values, {})
-
- transaction.track_self
+ transaction.increment(:time, 1)
end
end
describe '#set' do
it 'sets a value' do
- transaction.set(:number, 10)
-
- values = {
- duration: 0.0,
- number: 10,
- allocated_memory: a_kind_of(Numeric)
- }
-
- expect(transaction).to receive(:add_metric)
- .with('transactions', values, {})
-
- transaction.track_self
- end
- end
-
- describe '#finish' do
- it 'tracks the transaction details and submits them to Sidekiq' do
- expect(transaction).to receive(:track_self)
- expect(transaction).to receive(:submit)
-
- transaction.finish
- end
- end
-
- describe '#track_self' do
- it 'adds a metric for the transaction itself' do
- values = {
- duration: transaction.duration,
- allocated_memory: a_kind_of(Numeric)
- }
-
- expect(transaction).to receive(:add_metric)
- .with('transactions', values, {})
-
- transaction.track_self
- end
- end
-
- describe '#submit' do
- it 'submits the metrics to Sidekiq' do
- transaction.track_self
-
- expect(Gitlab::Metrics).to receive(:submit_metrics)
- .with([an_instance_of(Hash)])
-
- transaction.submit
- end
+ expect(prometheus_metric).to receive(:set).with({}, 10)
- it 'adds the action as a tag for every metric' do
- allow(transaction).to receive(:labels).and_return(controller: 'Foo', action: 'bar')
- transaction.track_self
-
- hash = {
- series: 'rails_transactions',
- tags: { action: 'Foo#bar' },
- values: { duration: 0.0, allocated_memory: a_kind_of(Numeric) },
- timestamp: a_kind_of(Integer)
- }
-
- expect(Gitlab::Metrics).to receive(:submit_metrics)
- .with([hash])
-
- transaction.submit
- end
-
- it 'does not add an action tag for events' do
- allow(transaction).to receive(:labels).and_return(controller: 'Foo', action: 'bar')
-
- transaction.add_event(:meow)
-
- hash = {
- series: 'events',
- tags: { event: :meow },
- values: { count: 1 },
- timestamp: a_kind_of(Integer)
- }
-
- expect(Gitlab::Metrics).to receive(:submit_metrics)
- .with([hash])
-
- transaction.submit
+ transaction.set(:number, 10)
end
end
@@ -167,7 +79,6 @@ describe Gitlab::Metrics::WebTransaction do
end
it 'provides labels with the method and path of the route in the grape endpoint' do
expect(transaction.labels).to eq({ controller: 'Grape', action: 'GET /projects/:id/archive' })
- expect(transaction.action).to eq('Grape#GET /projects/:id/archive')
end
it 'does not provide labels if route infos are missing' do
@@ -177,7 +88,6 @@ describe Gitlab::Metrics::WebTransaction do
env['api.endpoint'] = endpoint
expect(transaction.labels).to eq({})
- expect(transaction.action).to be_nil
end
end
@@ -193,7 +103,6 @@ describe Gitlab::Metrics::WebTransaction do
it 'tags a transaction with the name and action of a controller' do
expect(transaction.labels).to eq({ controller: 'TestController', action: 'show' })
- expect(transaction.action).to eq('TestController#show')
end
context 'when the request content type is not :html' do
@@ -201,7 +110,6 @@ describe Gitlab::Metrics::WebTransaction do
it 'appends the mime type to the transaction action' do
expect(transaction.labels).to eq({ controller: 'TestController', action: 'show.json' })
- expect(transaction.action).to eq('TestController#show.json')
end
end
@@ -210,54 +118,26 @@ describe Gitlab::Metrics::WebTransaction do
it 'does not append the MIME type to the transaction action' do
expect(transaction.labels).to eq({ controller: 'TestController', action: 'show' })
- expect(transaction.action).to eq('TestController#show')
end
end
end
it 'returns no labels when no route information is present in env' do
expect(transaction.labels).to eq({})
- expect(transaction.action).to eq(nil)
end
end
describe '#add_event' do
it 'adds a metric' do
- transaction.add_event(:meow)
+ expect(prometheus_metric).to receive(:increment)
- expect(transaction.metrics[0]).to be_an_instance_of(Gitlab::Metrics::Metric)
- end
-
- it "does not prefix the metric's series name" do
transaction.add_event(:meow)
-
- metric = transaction.metrics[0]
-
- expect(metric.series).to eq(described_class::EVENT_SERIES)
- end
-
- it 'tracks a counter for every event' do
- transaction.add_event(:meow)
-
- metric = transaction.metrics[0]
-
- expect(metric.values).to eq(count: 1)
- end
-
- it 'tracks the event name' do
- transaction.add_event(:meow)
-
- metric = transaction.metrics[0]
-
- expect(metric.tags).to eq(event: :meow)
end
it 'allows tracking of custom tags' do
- transaction.add_event(:bau, animal: 'dog')
-
- metric = transaction.metrics[0]
+ expect(prometheus_metric).to receive(:increment).with(animal: "dog")
- expect(metric.tags).to eq(event: :bau, animal: 'dog')
+ transaction.add_event(:bau, animal: 'dog')
end
end
end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index f0ba12c1cd0..2ebe1958487 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -53,60 +53,6 @@ describe Gitlab::Metrics do
end
end
- describe '.influx_metrics_enabled?' do
- it 'returns a boolean' do
- expect(described_class.influx_metrics_enabled?).to be_in([true, false])
- end
- end
-
- describe '.submit_metrics' do
- it 'prepares and writes the metrics to InfluxDB' do
- connection = double(:connection)
- pool = double(:pool)
-
- expect(pool).to receive(:with).and_yield(connection)
- expect(connection).to receive(:write_points).with(an_instance_of(Array))
- expect(described_class).to receive(:pool).and_return(pool)
-
- described_class.submit_metrics([{ 'series' => 'kittens', 'tags' => {} }])
- end
- end
-
- describe '.prepare_metrics' do
- it 'returns a Hash with the keys as Symbols' do
- metrics = described_class
- .prepare_metrics([{ 'values' => {}, 'tags' => {} }])
-
- expect(metrics).to eq([{ values: {}, tags: {} }])
- end
-
- it 'escapes tag values' do
- metrics = described_class.prepare_metrics([
- { 'values' => {}, 'tags' => { 'foo' => 'bar=' } }
- ])
-
- expect(metrics).to eq([{ values: {}, tags: { 'foo' => 'bar\\=' } }])
- end
-
- it 'drops empty tags' do
- metrics = described_class.prepare_metrics([
- { 'values' => {}, 'tags' => { 'cats' => '', 'dogs' => nil } }
- ])
-
- expect(metrics).to eq([{ values: {}, tags: {} }])
- end
- end
-
- describe '.escape_value' do
- it 'escapes an equals sign' do
- expect(described_class.escape_value('foo=')).to eq('foo\\=')
- end
-
- it 'casts values to Strings' do
- expect(described_class.escape_value(10)).to eq('10')
- end
- end
-
describe '.measure' do
context 'without a transaction' do
it 'returns the return value of the block' do
@@ -145,30 +91,6 @@ describe Gitlab::Metrics do
end
end
- describe '.action=' do
- context 'without a transaction' do
- it 'does nothing' do
- expect_any_instance_of(Gitlab::Metrics::Transaction)
- .not_to receive(:action=)
-
- described_class.action = 'foo'
- end
- end
-
- context 'with a transaction' do
- it 'sets the action of a transaction' do
- trans = Gitlab::Metrics::WebTransaction.new({})
-
- expect(described_class).to receive(:current_transaction)
- .and_return(trans)
-
- expect(trans).to receive(:action=).with('foo')
-
- described_class.action = 'foo'
- end
- end
- end
-
describe '#series_prefix' do
it 'returns a String' do
expect(described_class.series_prefix).to be_an_instance_of(String)
diff --git a/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
index ee6224eca68..42abbecead0 100644
--- a/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
@@ -30,6 +30,10 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
it_behaves_like 'counter examples', 'terminals'
end
+ describe 'pipelines counter' do
+ it_behaves_like 'counter examples', 'pipelines'
+ end
+
describe 'previews counter' do
let(:setting_enabled) { true }
@@ -61,6 +65,7 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
views = 2
previews = 4
terminals = 1
+ pipelines = 2
before do
stub_application_setting(web_ide_clientside_preview_enabled: true)
@@ -70,6 +75,7 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
views.times { described_class.increment_views_count }
previews.times { described_class.increment_previews_count }
terminals.times { described_class.increment_terminals_count }
+ pipelines.times { described_class.increment_pipelines_count }
end
it 'can report all totals' do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 4fe612c1d0e..579470b98a4 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -818,6 +818,10 @@ describe 'project routing' do
it 'routes to usage_ping#web_ide_clientside_preview' do
expect(post('/gitlab/gitlabhq/usage_ping/web_ide_clientside_preview')).to route_to('projects/usage_ping#web_ide_clientside_preview', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
+
+ it 'routes to usage_ping#web_ide_pipelines_count' do
+ expect(post('/gitlab/gitlabhq/usage_ping/web_ide_pipelines_count')).to route_to('projects/usage_ping#web_ide_pipelines_count', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
end
describe Projects::StaticSiteEditorController, 'routing' do
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index a8f743ed7d7..067ae342f57 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -133,7 +133,6 @@ module UsageDataHelpers
gitaly
database
avg_cycle_analytics
- influxdb_metrics_enabled
prometheus_metrics_enabled
web_ide_clientside_preview_enabled
ingress_modsecurity_enabled