summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml9
-rw-r--r--.gitlab/ci/notify.gitlab-ci.yml68
-rw-r--r--.gitlab/ci/review-apps/main.gitlab-ci.yml28
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml23
-rw-r--r--.rubocop_todo/layout/line_continuation_spacing.yml2
-rw-r--r--.rubocop_todo/layout/line_end_string_concatenation_indentation.yml2
-rw-r--r--app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue4
-rw-r--r--app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue4
-rw-r--r--app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue1
-rw-r--r--app/assets/javascripts/groups/index.js2
-rw-r--r--app/assets/javascripts/groups/init_overview_tabs.js2
-rw-r--r--app/controllers/projects/blame_controller.rb29
-rw-r--r--app/helpers/application_helper.rb4
-rw-r--r--app/helpers/blame_helper.rb10
-rw-r--r--app/helpers/groups_helper.rb1
-rw-r--r--app/mailers/emails/service_desk.rb13
-rw-r--r--app/mailers/previews/notify_preview.rb45
-rw-r--r--app/models/concerns/database_event_tracking.rb2
-rw-r--r--app/models/u2f_registration.rb24
-rw-r--r--app/services/packages/npm/create_package_service.rb2
-rw-r--r--app/views/admin/applications/index.html.haml2
-rw-r--r--app/views/dashboard/projects/_starred_empty_state.html.haml4
-rw-r--r--app/views/events/_events.html.haml2
-rw-r--r--app/views/layouts/_head.html.haml36
-rw-r--r--app/views/notify/service_desk_verification_triggered_email.html.haml18
-rw-r--r--app/views/notify/service_desk_verification_triggered_email.text.erb10
-rw-r--r--app/views/projects/blame/show.html.haml8
-rw-r--r--app/views/shared/empty_states/_snippets.html.haml4
-rw-r--r--app/views/shared/projects/_list.html.haml4
-rw-r--r--app/views/shared/users/index.html.haml4
-rw-r--r--app/views/snippets/_snippets.html.haml2
-rw-r--r--doc/architecture/blueprints/cells/images/term-cell.pngbin88371 -> 26613 bytes
-rw-r--r--doc/architecture/blueprints/cells/images/term-cluster.pngbin429127 -> 91814 bytes
-rw-r--r--doc/architecture/blueprints/cells/images/term-organization.pngbin156340 -> 31223 bytes
-rw-r--r--doc/architecture/blueprints/remote_development/img/remote_dev_15_7.pngbin392116 -> 112261 bytes
-rw-r--r--doc/ci/testing/code_quality.md2
-rw-r--r--doc/development/internal_api/index.md44
-rw-r--r--doc/development/pipelines/internals.md1
-rw-r--r--doc/development/sec/security_report_ingestion_overview.md2
-rw-r--r--doc/user/application_security/vulnerabilities/severities.md4
-rw-r--r--doc/user/group/epics/img/epic_board_v15_10.pngbin78145 -> 24915 bytes
-rw-r--r--doc/user/profile/img/user_profile_achievements_v15_11.pngbin74766 -> 24613 bytes
-rw-r--r--lib/gitlab/auth/u2f_webauthn_converter.rb40
-rw-r--r--lib/gitlab/background_migration/migrate_u2f_webauthn.rb28
-rw-r--r--lib/gitlab/tracking.rb32
-rw-r--r--lib/gitlab/tracking/destinations/database_events_snowplow.rb50
-rw-r--r--locale/gitlab.pot15
-rwxr-xr-xscripts/create-pipeline-failure-incident.rb303
-rwxr-xr-xscripts/generate-failed-pipeline-slack-message.rb188
-rw-r--r--spec/factories/member_roles.rb11
-rw-r--r--spec/features/dashboard/snippets_spec.rb3
-rw-r--r--spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js4
-rw-r--r--spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js4
-rw-r--r--spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js1
-rw-r--r--spec/frontend/groups/components/overview_tabs_spec.js1
-rw-r--r--spec/helpers/application_helper_spec.rb23
-rw-r--r--spec/helpers/groups_helper_spec.rb1
-rw-r--r--spec/lib/gitlab/auth/u2f_webauthn_converter_spec.rb29
-rw-r--r--spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb67
-rw-r--r--spec/lib/gitlab/tracking/destinations/database_events_snowplow_spec.rb113
-rw-r--r--spec/lib/gitlab/tracking_spec.rb123
-rw-r--r--spec/mailers/emails/service_desk_spec.rb39
-rw-r--r--spec/models/analytics/cycle_analytics/stage_spec.rb40
-rw-r--r--spec/models/concerns/database_event_tracking_spec.rb11
-rw-r--r--spec/models/u2f_registration_spec.rb66
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb43
-rw-r--r--spec/scripts/create_pipeline_failure_incident_spec.rb162
-rw-r--r--spec/scripts/generate_failed_pipeline_slack_message_spec.rb81
-rw-r--r--spec/support/helpers/snowplow_helpers.rb10
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb12
-rw-r--r--vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/agent.rb3
-rw-r--r--vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/looper.rb8
-rw-r--r--vendor/gems/cloud_profiler_agent/spec/cloud_profiler_agent/looper_spec.rb19
74 files changed, 680 insertions, 1273 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fbc0aa31341..6624d31a51d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -37,10 +37,6 @@ default:
OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY2"
.default-branch-pipeline-failure-variables: &default-branch-pipeline-failure-variables
- CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
- NOTIFY_PIPELINE_FAILURE_CHANNEL: "master-broken"
- BROKEN_BRANCH_INCIDENTS_PROJECT: "gitlab-org/quality/engineering-productivity/master-broken-incidents"
- BROKEN_BRANCH_INCIDENTS_PROJECT_TOKEN: "${BROKEN_MASTER_INCIDENTS_PROJECT_TOKEN}"
CREATE_ISSUES_FOR_FAILING_TESTS: "true"
workflow:
@@ -87,7 +83,6 @@ workflow:
- if: '$CI_COMMIT_BRANCH == "ruby2" && $CI_PIPELINE_SOURCE == "schedule"'
variables:
<<: *backcompat-ruby-variables
- NOTIFY_PIPELINE_FAILURE_CHANNEL: "f_ruby3"
PIPELINE_NAME: 'Scheduled Ruby $RUBY_VERSION $CI_COMMIT_BRANCH branch pipeline'
# This work around https://gitlab.com/gitlab-org/gitlab/-/issues/332411 whichs prevents usage of dependency proxy
# when pipeline is triggered by a project access token.
@@ -115,11 +110,7 @@ workflow:
- if: '$CI_COMMIT_BRANCH =~ /^[\d-]+-stable(-ee)?$/'
variables:
<<: *default-ruby-variables
- NOTIFY_PIPELINE_FAILURE_CHANNEL: "releases"
PIPELINE_NAME: 'Ruby $RUBY_VERSION $CI_COMMIT_BRANCH branch pipeline'
- CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
- BROKEN_BRANCH_INCIDENTS_PROJECT: "gitlab-org/release/tasks"
- BROKEN_BRANCH_INCIDENTS_PROJECT_TOKEN: "${BROKEN_STABLE_INCIDENTS_PROJECT_TOKEN}"
- if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
variables:
<<: *default-ruby-variables
diff --git a/.gitlab/ci/notify.gitlab-ci.yml b/.gitlab/ci/notify.gitlab-ci.yml
index 99408419d86..1afc4eb8c97 100644
--- a/.gitlab/ci/notify.gitlab-ci.yml
+++ b/.gitlab/ci/notify.gitlab-ci.yml
@@ -3,74 +3,6 @@
dependencies: []
cache: {}
-.notify-slack:
- extends:
- - .notify-defaults
- image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}alpine/curl
- variables:
- MERGE_REQUEST_URL: ${CI_MERGE_REQUEST_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}
- before_script:
- - apk update && apk add git bash
- - echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
- - echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
-
-notify-update-gitaly:
- extends:
- - .notify-slack
- rules:
- - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == $GITALY_UPDATE_BRANCH'
- when: on_failure
- allow_failure: true
- variables:
- NOTIFY_CHANNEL: g_gitaly
- GITALY_UPDATE_BRANCH: release-tools/update-gitaly
- script:
- - scripts/slack ${NOTIFY_CHANNEL} "☠️ \`${GITALY_UPDATE_BRANCH}\` failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab QA Bot"
-
-notify-security-pipeline:
- extends:
- - .notify-slack
- - .delivery:rules:security-pipeline-merge-result-failure
- variables:
- NOTIFY_CHANNEL: f_upcoming_release
- script:
- # <!subteam^S0127FU8PDE> mentions the `@release-managers` group
- - scripts/slack ${NOTIFY_CHANNEL} "<!subteam^S0127FU8PDE> ☠️ Pipeline for merged result failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab Release Tools Bot"
-
-notify-pipeline-failure:
- extends:
- - .notify-defaults
- - .notify:rules:notify-pipeline-failure
- image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}
- variables:
- INCIDENT_PROJECT: "${BROKEN_BRANCH_INCIDENTS_PROJECT}"
- BROKEN_BRANCH_PROJECT_TOKEN: "${BROKEN_BRANCH_INCIDENTS_PROJECT_TOKEN}"
- INCIDENT_JSON: "${CI_PROJECT_DIR}/incident.json"
- SLACK_CHANNEL: "${NOTIFY_PIPELINE_FAILURE_CHANNEL}"
- FAILED_PIPELINE_SLACK_MESSAGE_FILE: "${CI_PROJECT_DIR}/failed_pipeline_slack_message.json"
- before_script:
- - source scripts/utils.sh
- - apt-get update && apt-get install -y jq
- - install_gitlab_gem
- script:
- - |
- if [[ "${CREATE_INCIDENT_FOR_PIPELINE_FAILURE}" == "true" ]]; then
- scripts/create-pipeline-failure-incident.rb -p ${INCIDENT_PROJECT} -f ${INCIDENT_JSON} -t ${BROKEN_BRANCH_PROJECT_TOKEN} ||
- scripts/slack ${SLACK_CHANNEL} "☠️ Broken pipeline incident creation failed! ☠️ See ${CI_JOB_URL}" ci_failing "Failed pipeline reporter";
- echosuccess "Created incident $(jq '.web_url' ${INCIDENT_JSON})";
- fi
- - |
- scripts/generate-failed-pipeline-slack-message.rb -p ${INCIDENT_PROJECT} -i ${INCIDENT_JSON} -f ${FAILED_PIPELINE_SLACK_MESSAGE_FILE};
- curl -X POST -H 'Content-Type: application/json' --data @${FAILED_PIPELINE_SLACK_MESSAGE_FILE} "$CI_SLACK_WEBHOOK_URL" ||
- scripts/slack ${SLACK_CHANNEL} "☠️ Broken pipeline notification failed! ☠️ See ${CI_JOB_URL}" ci_failing "Failed pipeline reporter";
-
- artifacts:
- paths:
- - ${INCIDENT_JSON}
- - ${FAILED_PIPELINE_SLACK_MESSAGE_FILE}
- when: always
- expire_in: 2 days
-
create-issues-for-failing-tests:
extends:
- .notify-defaults
diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml
index bdb9b9a3151..766d819c89b 100644
--- a/.gitlab/ci/review-apps/main.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml
@@ -145,34 +145,6 @@ review-deploy:
expire_in: 7 days
when: always
-review-deploy-failure-notification:
- stage: post-deploy
- image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}
- rules:
- - when: on_failure
- allow_failure: true
- variables:
- BROKEN_REVIEW_APPS_INCIDENTS_PROJECT: "gitlab-org/quality/engineering-productivity/review-apps-broken-incidents"
- BROKEN_REVIEW_APPS_INCIDENT_JSON: "${CI_PROJECT_DIR}/incident.json"
- SLACK_CHANNEL: "review-apps-broken"
- FAILED_PIPELINE_SLACK_MESSAGE_FILE: "${CI_PROJECT_DIR}/failed_pipeline_slack_message.json"
- before_script:
- - source scripts/utils.sh
- - apt-get update && apt-get install -y jq
- - install_gitlab_gem
- script:
- # Check that the review-deploy failed. If not, we don't do anything.
- # It can be possible that other jobs were failing, but here we only
- # care about review-deploy
- - curl "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/jobs?scope=failed" | jq -e 'any(.name == "review-deploy")'
- - |
- scripts/create-pipeline-failure-incident.rb -p ${BROKEN_REVIEW_APPS_INCIDENTS_PROJECT} -f ${BROKEN_REVIEW_APPS_INCIDENT_JSON} -t ${BROKEN_REVIEW_APPS_INCIDENTS_PROJECT_TOKEN};
- echosuccess "Created incident $(jq '.web_url' ${BROKEN_REVIEW_APPS_INCIDENT_JSON})";
- - |
- scripts/generate-failed-pipeline-slack-message.rb -p ${BROKEN_REVIEW_APPS_INCIDENTS_PROJECT} -i ${BROKEN_REVIEW_APPS_INCIDENT_JSON} -f ${FAILED_PIPELINE_SLACK_MESSAGE_FILE};
- curl -X POST -H 'Content-Type: application/json' --data @${FAILED_PIPELINE_SLACK_MESSAGE_FILE} "$CI_SLACK_WEBHOOK_URL" ||
- scripts/slack ${SLACK_CHANNEL} "☠️ Broken pipeline notification failed! ☠️ See ${CI_JOB_URL}" ci_failing "Failed pipeline reporter"
-
review-deploy-sample-projects:
extends:
- .review-workflow-base
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index a1caa17d5ab..c8c79decd10 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -157,12 +157,6 @@
.if-ruby2-branch: &if-ruby2-branch
if: '$CI_COMMIT_BRANCH == "ruby2"'
-# For Security merge requests, the gitlab-release-tools-bot triggers a new
-# pipeline for the "Pipelines for merged results" feature. If the pipeline
-# fails, we notify release managers.
-.if-security-pipeline-merge-result: &if-security-pipeline-merge-result
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH && $CI_PROJECT_NAMESPACE == "gitlab-org/security" && $GITLAB_USER_LOGIN == "gitlab-release-tools-bot"'
-
####################
# Changes patterns #
####################
@@ -837,14 +831,6 @@
- changes:
- scripts/lint_templates_bash.rb
-##################
-# Delivery rules #
-##################
-.delivery:rules:security-pipeline-merge-result-failure:
- rules:
- - <<: *if-security-pipeline-merge-result
- when: on_failure
-
######################
# Dev fixtures rules #
######################
@@ -1126,15 +1112,6 @@
##########
# Notify #
##########
-.notify:rules:notify-pipeline-failure:
- rules:
- # Don't report child pipeline failures
- - if: '$CI_PIPELINE_SOURCE == "parent_pipeline"'
- when: never
- - if: '$CI_SLACK_WEBHOOK_URL && $NOTIFY_PIPELINE_FAILURE_CHANNEL'
- when: on_failure
- allow_failure: true
-
.notify:rules:create-issues-for-failing-tests:
rules:
# Don't report child pipeline failures
diff --git a/.rubocop_todo/layout/line_continuation_spacing.yml b/.rubocop_todo/layout/line_continuation_spacing.yml
index 027889488fe..0c256bce42b 100644
--- a/.rubocop_todo/layout/line_continuation_spacing.yml
+++ b/.rubocop_todo/layout/line_continuation_spacing.yml
@@ -141,8 +141,6 @@ Layout/LineContinuationSpacing:
- 'rubocop/cop/migration/background_migrations.rb'
- 'rubocop/cop/performance/ar_exists_and_present_blank.rb'
- 'rubocop/cop/redis_queue_usage.rb'
- - 'scripts/create-pipeline-failure-incident.rb'
- - 'scripts/generate-failed-pipeline-slack-message.rb'
- 'scripts/qa/testcases-check'
- 'spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb'
- 'spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb'
diff --git a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml
index 715b4d18f6e..06baecba0fd 100644
--- a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml
+++ b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml
@@ -238,8 +238,6 @@ Layout/LineEndStringConcatenationIndentation:
- 'rubocop/cop/rspec/have_gitlab_http_status.rb'
- 'rubocop/cop/sidekiq_api_usage.rb'
- 'rubocop/cop/user_admin.rb'
- - 'scripts/create-pipeline-failure-incident.rb'
- - 'scripts/generate-failed-pipeline-slack-message.rb'
- 'scripts/lib/glfm/parse_examples.rb'
- 'scripts/lib/glfm/update_example_snapshots.rb'
- 'scripts/lib/glfm/update_specification.rb'
diff --git a/app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue b/app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue
index 535758750f9..cba13c11c5d 100644
--- a/app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue
+++ b/app/assets/javascripts/groups/components/empty_states/archived_projects_empty_state.vue
@@ -8,14 +8,14 @@ export default {
i18n: {
title: s__('GroupsEmptyState|No archived projects.'),
},
- inject: ['newProjectIllustration'],
+ inject: ['emptyProjectsIllustration'],
};
</script>
<template>
<gl-empty-state
:title="$options.i18n.title"
- :svg-path="newProjectIllustration"
+ :svg-path="emptyProjectsIllustration"
:svg-height="100"
/>
</template>
diff --git a/app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue b/app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue
index 7223321bf3e..7c691b56a43 100644
--- a/app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue
+++ b/app/assets/javascripts/groups/components/empty_states/shared_projects_empty_state.vue
@@ -8,14 +8,14 @@ export default {
i18n: {
title: s__('GroupsEmptyState|No shared projects.'),
},
- inject: ['newProjectIllustration'],
+ inject: ['emptyProjectsIllustration'],
};
</script>
<template>
<gl-empty-state
:title="$options.i18n.title"
- :svg-path="newProjectIllustration"
+ :svg-path="emptyProjectsIllustration"
:svg-height="100"
/>
</template>
diff --git a/app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue b/app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue
index 763abaebebb..a2359082149 100644
--- a/app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue
+++ b/app/assets/javascripts/groups/components/empty_states/subgroups_and_projects_empty_state.vue
@@ -43,6 +43,7 @@ export default {
'newProjectPath',
'newSubgroupIllustration',
'newProjectIllustration',
+ 'emptyProjectsIllustration',
'emptySubgroupIllustration',
'canCreateSubgroups',
'canCreateProjects',
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index c3bf3f28509..f6711bde7d0 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -51,6 +51,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
newProjectPath,
newSubgroupIllustration,
newProjectIllustration,
+ emptyProjectsIllustration,
emptySubgroupIllustration,
canCreateSubgroups,
canCreateProjects,
@@ -63,6 +64,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
newProjectPath,
newSubgroupIllustration,
newProjectIllustration,
+ emptyProjectsIllustration,
emptySubgroupIllustration,
canCreateSubgroups: parseBoolean(canCreateSubgroups),
canCreateProjects: parseBoolean(canCreateProjects),
diff --git a/app/assets/javascripts/groups/init_overview_tabs.js b/app/assets/javascripts/groups/init_overview_tabs.js
index 664d07ca13d..4064520d1ca 100644
--- a/app/assets/javascripts/groups/init_overview_tabs.js
+++ b/app/assets/javascripts/groups/init_overview_tabs.js
@@ -44,6 +44,7 @@ export const initGroupOverviewTabs = () => {
newProjectPath,
newSubgroupIllustration,
newProjectIllustration,
+ emptyProjectsIllustration,
emptySubgroupIllustration,
canCreateSubgroups,
canCreateProjects,
@@ -62,6 +63,7 @@ export const initGroupOverviewTabs = () => {
newProjectPath,
newSubgroupIllustration,
newProjectIllustration,
+ emptyProjectsIllustration,
emptySubgroupIllustration,
canCreateSubgroups: parseBoolean(canCreateSubgroups),
canCreateProjects: parseBoolean(canCreateProjects),
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index d41b347dc5a..75d7dfd5813 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -19,20 +19,13 @@ class Projects::BlameController < Projects::ApplicationController
return redirect_to_tree_root_for_missing_path(@project, @ref, @path)
end
- environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
- environment_params[:find_latest] = true
- @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
+ load_environment
- permitted_params = params.permit(:page, :no_pagination, :streaming)
- blame_service = Projects::BlameService.new(@blob, @commit, permitted_params)
+ blame_service = Projects::BlameService.new(@blob, @commit, blame_params)
@blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate!
- @entire_blame_path = full_blame_path(no_pagination: true)
- @blame_pages_url = blame_pages_url(permitted_params)
- if blame_service.streaming_possible
- @entire_blame_path = full_blame_path(streaming: true)
- end
+ @streaming_possible = blame_service.streaming_possible
@streaming_enabled = blame_service.streaming_enabled
@blame_pagination = blame_service.pagination unless @streaming_enabled
@@ -45,11 +38,9 @@ class Projects::BlameController < Projects::ApplicationController
def page
@blob = @repository.blob_at(@commit.id, @path)
- environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
- environment_params[:find_latest] = true
- @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
+ load_environment
- blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page, :streaming))
+ blame_service = Projects::BlameService.new(@blob, @commit, blame_params)
@blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate!
@@ -58,12 +49,14 @@ class Projects::BlameController < Projects::ApplicationController
private
- def full_blame_path(params)
- namespace_project_blame_path(namespace_id: @project.namespace, project_id: @project, id: @id, **params)
+ def load_environment
+ environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
+ environment_params[:find_latest] = true
+ @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
end
- def blame_pages_url(params)
- namespace_project_blame_page_url(namespace_id: @project.namespace, project_id: @project, id: @id, **params)
+ def blame_params
+ params.permit(:page, :no_pagination, :streaming)
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index d0602952f9a..9e931f32e5f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -284,6 +284,10 @@ module ApplicationHelper
!params.has_key?(:no_startup_css)
end
+ def sign_in_with_redirect?
+ current_page?(new_user_session_path) && session[:user_return_to].present?
+ end
+
def outdated_browser?
browser.ie?
end
diff --git a/app/helpers/blame_helper.rb b/app/helpers/blame_helper.rb
index 5117f7c6d9c..92bee1795fa 100644
--- a/app/helpers/blame_helper.rb
+++ b/app/helpers/blame_helper.rb
@@ -39,4 +39,14 @@ module BlameHelper
row_height_exp = line_count == 1 ? COMMIT_BLOCK_HEIGHT_EXP : total_line_height_exp
"contain-intrinsic-size: 1px calc(#{row_height_exp})"
end
+
+ def blame_pages_streaming_url(id, project)
+ namespace_project_blame_page_url(namespace_id: project.namespace, project_id: project, id: id, streaming: true)
+ end
+
+ def entire_blame_path(id, project, streaming_possible)
+ params = streaming_possible ? { streaming: true } : { no_pagination: true }
+
+ namespace_project_blame_path(namespace_id: project.namespace, project_id: project, id: id, **params)
+ end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 186fb609b1b..85bbc796dab 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -161,6 +161,7 @@ module GroupsHelper
new_project_path: new_project_path(namespace_id: group.id),
new_subgroup_illustration: image_path('illustrations/subgroup-create-new-sm.svg'),
new_project_illustration: image_path('illustrations/project-create-new-sm.svg'),
+ empty_projects_illustration: image_path('illustrations/empty-state/empty-projects-md.svg'),
empty_subgroup_illustration: image_path('illustrations/empty-state/empty-subgroup-md.svg'),
render_empty_state: 'true',
can_create_subgroups: can?(current_user, :create_subgroup, group).to_s,
diff --git a/app/mailers/emails/service_desk.rb b/app/mailers/emails/service_desk.rb
index f343121e071..3bab788cd57 100644
--- a/app/mailers/emails/service_desk.rb
+++ b/app/mailers/emails/service_desk.rb
@@ -43,6 +43,19 @@ module Emails
inject_service_desk_custom_email(mail_answer_thread(@issue, options))
end
+ def service_desk_verification_triggered_email(service_desk_setting, recipient)
+ @service_desk_setting = service_desk_setting
+ @triggerer = @service_desk_setting.custom_email_verification.triggerer
+ @smtp_address = @service_desk_setting.custom_email_credential.smtp_address
+
+ subject = format(s_("Notify|Verification for custom email %{email} for %{project_name} triggered"),
+ email: @service_desk_setting.custom_email,
+ project_name: @service_desk_setting.project.name
+ )
+
+ email_with_layout(to: recipient, subject: subject)
+ end
+
private
def setup_service_desk_mail(issue_id)
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 17b225c5e9b..61d77d82d9a 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -213,6 +213,14 @@ class NotifyPreview < ActionMailer::Preview
Notify.service_desk_thank_you_email(issue.id).message
end
+ def service_desk_verification_triggered_email
+ cleanup do
+ setup_service_desk_custom_email_objects
+
+ Notify.service_desk_verification_triggered_email(service_desk_setting, 'owner@example.com').message
+ end
+ end
+
def merge_when_pipeline_succeeds_email
Notify.merge_when_pipeline_succeeds_email(user.id, merge_request.id, user.id).message
end
@@ -247,6 +255,43 @@ class NotifyPreview < ActionMailer::Preview
@project ||= Project.first
end
+ def setup_service_desk_custom_email_objects
+ # Call accessors to ensure objects have been created
+ custom_email_credential
+ custom_email_verification
+
+ # Update associations in projects, because we access
+ # custom_email_credential and custom_email_verification via project
+ project.reset
+ end
+
+ def custom_email_verification
+ @custom_email_verification ||= project.service_desk_custom_email_verification || ServiceDesk::CustomEmailVerification.create!(
+ project: project,
+ token: 'XXXXXXXXXXXX',
+ triggerer: user,
+ triggered_at: Time.current,
+ state: 0
+ )
+ end
+
+ def custom_email_credential
+ @custom_email_credential ||= project.service_desk_custom_email_credential || ServiceDesk::CustomEmailCredential.create!(
+ project: project,
+ smtp_address: 'smtp.gmail.com', # Use gmail, because Gitlab::UrlBlocker resolves DNS
+ smtp_port: 587,
+ smtp_username: 'user@gmail.com',
+ smtp_password: 'supersecret'
+ )
+ end
+
+ def service_desk_setting
+ @service_desk_setting ||= project.service_desk_setting || ServiceDeskSetting.create!(
+ project: project,
+ custom_email: 'user@gmail.com'
+ )
+ end
+
def issue
@issue ||= project.issues.first
end
diff --git a/app/models/concerns/database_event_tracking.rb b/app/models/concerns/database_event_tracking.rb
index 9f75b3ed4d8..37b479d5237 100644
--- a/app/models/concerns/database_event_tracking.rb
+++ b/app/models/concerns/database_event_tracking.rb
@@ -30,7 +30,7 @@ module DatabaseEventTracking
# that reports data asynchronously and does not impact performance nor carries a risk of
# rollback in case of error
- Gitlab::Tracking.event(
+ Gitlab::Tracking.database_event(
self.class.to_s,
"database_event_#{name}",
label: self.class.table_name,
diff --git a/app/models/u2f_registration.rb b/app/models/u2f_registration.rb
index ba6c1ee6af1..81415eb383b 100644
--- a/app/models/u2f_registration.rb
+++ b/app/models/u2f_registration.rb
@@ -5,9 +5,6 @@
class U2fRegistration < ApplicationRecord
belongs_to :user
- after_create :create_webauthn_registration
- after_update :update_webauthn_registration, if: :saved_change_to_counter?
-
def self.register(user, app_id, params, challenges)
u2f = U2F::U2F.new(app_id)
registration = self.new
@@ -43,25 +40,4 @@ class U2fRegistration < ApplicationRecord
rescue JSON::ParserError, NoMethodError, ArgumentError, U2F::Error
false
end
-
- private
-
- def create_webauthn_registration
- converter = Gitlab::Auth::U2fWebauthnConverter.new(self)
- WebauthnRegistration.create!(converter.convert)
- rescue StandardError => e
- Gitlab::ErrorTracking.track_exception(e, u2f_registration_id: self.id)
- end
-
- def update_webauthn_registration
- # When we update the sign count of this registration
- # we need to update the sign count of the corresponding webauthn registration
- # as well if it exists already
- WebauthnRegistration.find_by_credential_xid(webauthn_credential_xid)
- &.update_attribute(:counter, counter)
- end
-
- def webauthn_credential_xid
- Base64.strict_encode64(Base64.urlsafe_decode64(key_handle))
- end
end
diff --git a/app/services/packages/npm/create_package_service.rb b/app/services/packages/npm/create_package_service.rb
index b64771e4c58..5fc10488747 100644
--- a/app/services/packages/npm/create_package_service.rb
+++ b/app/services/packages/npm/create_package_service.rb
@@ -5,7 +5,7 @@ module Packages
include Gitlab::Utils::StrongMemoize
include ExclusiveLeaseGuard
- PACKAGE_JSON_NOT_ALLOWED_FIELDS = %w[readme readmeFilename].freeze
+ PACKAGE_JSON_NOT_ALLOWED_FIELDS = %w[readme readmeFilename licenseText].freeze
DEFAULT_LEASE_TIMEOUT = 1.hour.to_i
def execute
diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml
index d6a0974d10f..4f7eead71f3 100644
--- a/app/views/admin/applications/index.html.haml
+++ b/app/views/admin/applications/index.html.haml
@@ -10,7 +10,7 @@
- if @applications.empty?
%section.empty-state.gl-text-center.gl-display-flex.gl-flex-direction-column
.svg-content.svg-150
- = image_tag 'illustrations/empty-state/empty-admin-apps.svg', class: 'gl-max-w-full'
+ = image_tag 'illustrations/empty-state/empty-admin-apps-md.svg', class: 'gl-max-w-full'
.gl-max-w-full.gl-m-auto
%h1.h4.gl-font-size-h-display= s_('AdminArea|No applications found')
diff --git a/app/views/dashboard/projects/_starred_empty_state.html.haml b/app/views/dashboard/projects/_starred_empty_state.html.haml
index 6db018d72da..dafa3b4dc8d 100644
--- a/app/views/dashboard/projects/_starred_empty_state.html.haml
+++ b/app/views/dashboard/projects/_starred_empty_state.html.haml
@@ -1,7 +1,7 @@
.row.empty-state
.col-12
- .svg-content.svg-250
- = image_tag 'illustrations/starred_empty.svg'
+ .svg-content.svg-150
+ = image_tag 'illustrations/empty-state/empty-projects-starred-md.svg'
.text-content
%h4.gl-text-center
= s_("StarredProjectsEmptyState|You don't have starred projects yet.")
diff --git a/app/views/events/_events.html.haml b/app/views/events/_events.html.haml
index e1b7804c5a7..eb6154a446e 100644
--- a/app/views/events/_events.html.haml
+++ b/app/views/events/_events.html.haml
@@ -1,4 +1,4 @@
-- illustration_path = 'illustrations/profile-page/activity.svg'
+- illustration_path = 'illustrations/empty-state/empty-activity-md.svg'
- current_user_empty_message_header = s_('UserProfile|Join or create a group to start contributing by commenting on issues or submitting merge requests!')
- primary_button_label = _('New group')
- primary_button_link = new_group_path
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 2dd6eab2e17..c608569e22c 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -1,6 +1,7 @@
- page_description brand_title unless page_description
- site_name = _('GitLab')
-%head{ prefix: "og: http://ogp.me/ns#" }
+- omit_og = sign_in_with_redirect?
+%head{ omit_og ? { } : { prefix: "og: http://ogp.me/ns#" } }
%meta{ charset: "utf-8" }
%title= page_title(site_name)
@@ -64,22 +65,23 @@
= yield :project_javascripts
- -# Open Graph - http://ogp.me/
- %meta{ property: 'og:type', content: "object" }
- %meta{ property: 'og:site_name', content: site_name }
- %meta{ property: 'og:title', content: page_title }
- %meta{ property: 'og:description', content: page_description }
- %meta{ property: 'og:image', content: page_image }
- %meta{ property: 'og:image:width', content: '64' }
- %meta{ property: 'og:image:height', content: '64' }
- %meta{ property: 'og:url', content: request.base_url + request.fullpath }
-
- -# Twitter Card - https://dev.twitter.com/cards/types/summary
- %meta{ property: 'twitter:card', content: "summary" }
- %meta{ property: 'twitter:title', content: page_title }
- %meta{ property: 'twitter:description', content: page_description }
- %meta{ property: 'twitter:image', content: page_image }
- = page_card_meta_tags
+ - unless omit_og
+ -# Open Graph - http://ogp.me/
+ %meta{ property: 'og:type', content: "object" }
+ %meta{ property: 'og:site_name', content: site_name }
+ %meta{ property: 'og:title', content: page_title }
+ %meta{ property: 'og:description', content: page_description }
+ %meta{ property: 'og:image', content: page_image }
+ %meta{ property: 'og:image:width', content: '64' }
+ %meta{ property: 'og:image:height', content: '64' }
+ %meta{ property: 'og:url', content: request.base_url + request.fullpath }
+
+ -# Twitter Card - https://dev.twitter.com/cards/types/summary
+ %meta{ property: 'twitter:card', content: "summary" }
+ %meta{ property: 'twitter:title', content: page_title }
+ %meta{ property: 'twitter:description', content: page_description }
+ %meta{ property: 'twitter:image', content: page_image }
+ = page_card_meta_tags
%meta{ name: "description", content: page_description }
diff --git a/app/views/notify/service_desk_verification_triggered_email.html.haml b/app/views/notify/service_desk_verification_triggered_email.html.haml
new file mode 100644
index 00000000000..f2174af9615
--- /dev/null
+++ b/app/views/notify/service_desk_verification_triggered_email.html.haml
@@ -0,0 +1,18 @@
+- user_name = '@' + @triggerer.username
+- project_link = @service_desk_setting.project.web_url
+- project_link_start = '<a href="%{project_link}" target="_blank" rel="noopener noreferrer" class="highlight">'.html_safe % { project_link: project_link}
+- project_name = @service_desk_setting.project.human_name
+- project_link_end = '</a>'.html_safe
+- strong_open = '<strong>'.html_safe
+- strong_close = '</strong>'.html_safe
+- email_address = @service_desk_setting.custom_email
+- smtp_host = @smtp_address
+
+%tr
+ %td.text-content
+ %p
+ = html_escape(s_('Notify|%{strong_open}%{user_name}%{strong_close} updated the custom email address credentials for the Service Desk of %{project_link_start}%{project_name}%{project_link_end} and triggered the verification process.')) % { user_name: user_name, project_link_start: project_link_start, project_name: project_name, project_link_end: project_link_end, strong_open: strong_open, strong_close: strong_close }
+ %p
+ = html_escape(s_('Notify|The provided custom email address is %{strong_open}%{email_address}%{strong_close} and uses the SMTP host %{strong_open}%{smtp_host}%{strong_close}.')) % { email_address: email_address, smtp_host: smtp_host, strong_open: strong_open, strong_close: strong_close }
+ %p
+ = s_('Notify|If this was a mistake you can change these settings or deactivate the custom email address in the project settings.')
diff --git a/app/views/notify/service_desk_verification_triggered_email.text.erb b/app/views/notify/service_desk_verification_triggered_email.text.erb
new file mode 100644
index 00000000000..98c79e2d2f1
--- /dev/null
+++ b/app/views/notify/service_desk_verification_triggered_email.text.erb
@@ -0,0 +1,10 @@
+<% user_name = '@' + @triggerer.username %>
+<% project_name = @service_desk_setting.project.human_name %>
+<% email_address = @service_desk_setting.custom_email %>
+<% smtp_host = @smtp_address %>
+
+<%= s_('Notify|%{strong_open}%{user_name}%{strong_close} updated the custom email address credentials for the Service Desk of %{project_link_start}%{project_name}%{project_link_end} and triggered the verification process.') % { user_name: user_name, project_link_start: '', project_name: project_name, project_link_end: '', strong_open: '', strong_close: '' } %>
+
+<%= s_('Notify|The provided custom email address is %{strong_open}%{email_address}%{strong_close} and uses the SMTP host %{strong_open}%{smtp_host}%{strong_close}.') % { email_address: email_address, smtp_host: smtp_host, strong_open: '', strong_close: '' } %>
+
+<%= s_('Notify|If this was a mistake you can change these settings or deactivate the custom email address in the project settings.') %>
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index ee7ca9cd351..413a89f6399 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,15 +1,17 @@
- page_title _("Blame"), @blob.path, @ref
- add_page_specific_style 'page_bundles/tree'
+- blame_streaming_url = blame_pages_streaming_url(@id, @project)
+
- if @streaming_enabled && total_extra_pages > 0
- content_for :startup_js do
= javascript_tag do
:plain
window.blamePageStream = (() => {
- const url = new URL("#{@blame_pages_url}");
+ const url = new URL("#{blame_streaming_url}");
url.searchParams.set('page', 2);
return fetch(url).then(response => response.body);
})();
-- dataset = { testid: 'blob-content-holder', qa_selector: 'blame_file_content', per_page: @blame_per_page, total_extra_pages: total_extra_pages - 1, pages_url: @blame_pages_url }
+- dataset = { testid: 'blob-content-holder', qa_selector: 'blame_file_content', per_page: @blame_per_page, total_extra_pages: total_extra_pages - 1, pages_url: blame_streaming_url }
#blob-content-holder.tree-holder.js-per-page{ data: dataset }
= render "projects/blob/breadcrumb", blob: @blob, blame: true
@@ -40,7 +42,7 @@
- if @blame_pagination && @blame_pagination.total_pages > 1
.gl-display-flex.gl-justify-content-center.gl-flex-direction-column.gl-align-items-center.gl-p-3.gl-bg-gray-50.gl-border-t-solid.gl-border-t-1.gl-border-gray-100
- = render Pajamas::ButtonComponent.new(href: @entire_blame_path, size: :small, button_options: { class: 'gl-mt-3' }) do |c|
+ = render Pajamas::ButtonComponent.new(href: entire_blame_path(@id, @project, @streaming_possible), size: :small, button_options: { class: 'gl-mt-3' }) do |c|
= _('Show full blame')
- if @streaming_enabled
diff --git a/app/views/shared/empty_states/_snippets.html.haml b/app/views/shared/empty_states/_snippets.html.haml
index e34166bac6c..87de756093d 100644
--- a/app/views/shared/empty_states/_snippets.html.haml
+++ b/app/views/shared/empty_states/_snippets.html.haml
@@ -2,8 +2,8 @@
.row.empty-state
.col-12
- .svg-content{ data: { qa_selector: 'svg_content' } }
- = image_tag 'illustrations/snippets_empty.svg'
+ .svg-content.svg-150{ data: { qa_selector: 'svg_content' } }
+ = image_tag 'illustrations/empty-state/empty-snippets-md.svg'
.text-content.gl-text-center.gl-pt-0
- if current_user
%h4
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 09d63347ed6..0c18b2421f1 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -13,10 +13,10 @@
- contributed_projects_illustration_path = 'illustrations/profile-page/contributed-projects.svg'
- contributed_projects_current_user_empty_message_header = s_('UserProfile|Explore public groups to find projects to contribute to.')
- contributed_projects_visitor_empty_message = s_('UserProfile|This user hasn\'t contributed to any projects')
-- starred_projects_illustration_path = 'illustrations/starred_empty.svg'
+- starred_projects_illustration_path = 'illustrations/empty-state/empty-projects-starred-md.svg'
- starred_projects_current_user_empty_message_header = s_('UserProfile|Star projects to track their progress and show your appreciation.')
- starred_projects_visitor_empty_message = s_('UserProfile|This user hasn\'t starred any projects')
-- own_projects_illustration_path = 'illustrations/profile-page/personal-project.svg'
+- own_projects_illustration_path = 'illustrations/empty-state/empty-projects-md.svg'
- own_projects_current_user_empty_message_header = s_('UserProfile|You haven\'t created any personal projects.')
- own_projects_current_user_empty_message_description = s_('UserProfile|Your projects can be available publicly, internally, or privately, at your choice.')
- own_projects_visitor_empty_message = s_('UserProfile|There are no projects available to be displayed here.')
diff --git a/app/views/shared/users/index.html.haml b/app/views/shared/users/index.html.haml
index dd6b14d6be2..c6a61e1c4df 100644
--- a/app/views/shared/users/index.html.haml
+++ b/app/views/shared/users/index.html.haml
@@ -1,7 +1,7 @@
-- followers_illustration_path = 'illustrations/starred_empty.svg'
+- followers_illustration_path = 'illustrations/empty-state/empty-projects-starred-md.svg'
- followers_visitor_empty_message = s_('UserProfile|This user doesn\'t have any followers.')
- followers_current_user_empty_message_header = s_('UserProfile|You do not have any followers.')
-- following_illustration_path = 'illustrations/starred_empty.svg'
+- following_illustration_path = 'illustrations/empty-state/empty-projects-starred-md.svg'
- following_visitor_empty_message = s_('UserProfile|This user isn\'t following other users.')
- following_current_user_empty_message_header = s_('UserProfile|You are not following other users.')
diff --git a/app/views/snippets/_snippets.html.haml b/app/views/snippets/_snippets.html.haml
index 1d22575803b..eeea8a34002 100644
--- a/app/views/snippets/_snippets.html.haml
+++ b/app/views/snippets/_snippets.html.haml
@@ -1,5 +1,5 @@
- link_project = local_assigns.fetch(:link_project, false)
-- illustration_path = 'illustrations/profile-page/activity.svg'
+- illustration_path = 'illustrations/empty-state/empty-snippets-md.svg'
- current_user_empty_message_header = s_('UserProfile|You haven\'t created any snippets.')
- current_user_empty_message_description = s_('UserProfile|Snippets in GitLab can either be private, internal, or public.')
- primary_button_label = _('New snippet')
diff --git a/doc/architecture/blueprints/cells/images/term-cell.png b/doc/architecture/blueprints/cells/images/term-cell.png
index 40e4a3e29c3..799b2eccd95 100644
--- a/doc/architecture/blueprints/cells/images/term-cell.png
+++ b/doc/architecture/blueprints/cells/images/term-cell.png
Binary files differ
diff --git a/doc/architecture/blueprints/cells/images/term-cluster.png b/doc/architecture/blueprints/cells/images/term-cluster.png
index d54a3569a08..03c92850b64 100644
--- a/doc/architecture/blueprints/cells/images/term-cluster.png
+++ b/doc/architecture/blueprints/cells/images/term-cluster.png
Binary files differ
diff --git a/doc/architecture/blueprints/cells/images/term-organization.png b/doc/architecture/blueprints/cells/images/term-organization.png
index 172ff7e415a..20485587b8b 100644
--- a/doc/architecture/blueprints/cells/images/term-organization.png
+++ b/doc/architecture/blueprints/cells/images/term-organization.png
Binary files differ
diff --git a/doc/architecture/blueprints/remote_development/img/remote_dev_15_7.png b/doc/architecture/blueprints/remote_development/img/remote_dev_15_7.png
index 6382885336b..f36bfa24998 100644
--- a/doc/architecture/blueprints/remote_development/img/remote_dev_15_7.png
+++ b/doc/architecture/blueprints/remote_development/img/remote_dev_15_7.png
Binary files differ
diff --git a/doc/ci/testing/code_quality.md b/doc/ci/testing/code_quality.md
index ebcaffd0434..1802303ad35 100644
--- a/doc/ci/testing/code_quality.md
+++ b/doc/ci/testing/code_quality.md
@@ -74,7 +74,7 @@ tab of the pipeline's details page.
![Code Quality Report](img/code_quality_report_13_11.png)
-### Project quality view
+### Project quality view **(ULTIMATE)**
The project quality view displays an overview of the code quality findings. The view can be found under **Analytics > CI/CD**, and requires [`project_quality_summary_page`](../../user/feature_flags.md) feature flag to be enabled for this particular project.
diff --git a/doc/development/internal_api/index.md b/doc/development/internal_api/index.md
index aa10bbeda9c..ea4846f966a 100644
--- a/doc/development/internal_api/index.md
+++ b/doc/development/internal_api/index.md
@@ -809,6 +809,50 @@ Example response:
- CustomersDot
+## Storage limit exclusions
+
+The namespace storage limit exclusions endpoint applies storage limit exclusions to top-level namespaces on GitLab.com.
+This endpoint can only be consumed in the Admin Area of GitLab.com.
+
+### Create a storage limit exclusion
+
+Use a POST request to create an `Namespaces::Storage::LimitExclusion`.
+
+```plaintext
+POST /namespaces/:id/storage/limit_exclusion
+```
+
+| Attribute | Type | Required | Description |
+|:------------|:--------|:---------|:------------|
+| `reason` | string | yes | The reason to exclude the namespace. |
+
+Example request:
+
+```shell
+curl --request POST \
+ --url "https://gitlab.com/v4/namespaces/123/storage/limit_exclusion" \
+ --header 'Content-Type: application/json' \
+ --header 'PRIVATE-TOKEN: <admin access token>' \
+ --data '{
+ "reason": "a reason to exclude the Namespace"
+ }'
+```
+
+Example response:
+
+```json
+{
+ "id": 1,
+ "namespace_id": 1234,
+ "namespace_name": "A Namespace Name",
+ "reason": "a reason to exclude the Namespace"
+}
+```
+
+### Known consumers
+
+- GitLab.com Admin Area
+
## CI/CD minutes provisioning
The CI/CD Minutes endpoints are used by [CustomersDot](https://gitlab.com/gitlab-org/customers-gitlab-com) (`customers.gitlab.com`)
diff --git a/doc/development/pipelines/internals.md b/doc/development/pipelines/internals.md
index 81b95eb204f..678297eb3e5 100644
--- a/doc/development/pipelines/internals.md
+++ b/doc/development/pipelines/internals.md
@@ -187,7 +187,6 @@ and included in `rules` definitions via [YAML anchors](../../ci/yaml/yaml_optimi
| `if-dot-com-gitlab-org-and-security-merge-request` | Limit jobs creation to merge requests for the `gitlab-org` and `gitlab-org/security` groups on GitLab.com. | |
| `if-dot-com-gitlab-org-and-security-tag` | Limit jobs creation to tags for the `gitlab-org` and `gitlab-org/security` groups on GitLab.com. | |
| `if-dot-com-ee-schedule` | Limits jobs to scheduled pipelines for the `gitlab-org/gitlab` project on GitLab.com. | |
-| `if-security-pipeline-merge-result` | Matches if the pipeline is for a security merge request triggered by `@gitlab-release-tools-bot`. | |
<!-- vale gitlab.Substitutions = YES -->
diff --git a/doc/development/sec/security_report_ingestion_overview.md b/doc/development/sec/security_report_ingestion_overview.md
index a9afe26411b..492d840e800 100644
--- a/doc/development/sec/security_report_ingestion_overview.md
+++ b/doc/development/sec/security_report_ingestion_overview.md
@@ -81,7 +81,7 @@ At this point, the following things can happen to the `Security::Finding` which
### Scan runs in a pipeline for the default branch
-If the pipeline ran on the default branch then the following, then in addition to the steps in [#Scan-is-executed-again-a-non-default-branch], these additional steps are executed:
+If the pipeline ran on the default branch then the following steps, in addition to the steps in [#scan-runs-in-a-pipeline-for-a-non-default-branch], are executed:
1. `Security::StoreScansService` gets called and schedules `StoreSecurityReportsWorker`.
1. `StoreSecurityReportsWorker` executes `Security::Ingestion::IngestReportsService`.
diff --git a/doc/user/application_security/vulnerabilities/severities.md b/doc/user/application_security/vulnerabilities/severities.md
index e75d0a45f7d..ab90ac18b8e 100644
--- a/doc/user/application_security/vulnerabilities/severities.md
+++ b/doc/user/application_security/vulnerabilities/severities.md
@@ -56,7 +56,7 @@ the following tables:
|------------------------------------------------------------------------------------------|------------------------------|----------------------------|-------------------------------------|
| [`gemnasium`](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium) | **{check-circle}** Yes | CVSS v2.0 Rating and CVSS v3.1 Qualitative Severity Rating <sup>1</sup> | `(AV:N/AC:L/Au:S/C:P/I:P/A:N)`, `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H` |
-1. The CVSS v3.1 rating is used to calculate the severity level. If it's not available, the CVSS v2.0 rating is used instead.
+The CVSS v3.1 rating is used to calculate the severity level. If it's not available, the CVSS v2.0 rating is used instead.
## Container Scanning
@@ -64,6 +64,8 @@ the following tables:
|------------------------------------------------------------------------|--------------------------|----------------------------|--------------------------------------------------------------|
| [`container-scanning`](https://gitlab.com/gitlab-org/security-products/analyzers/container-scanning)| **{check-circle}** Yes | String | `Unknown`, `Low`, `Medium`, `High`, `Critical` |
+When available, the vendor severity level takes precedence and is used by the analyzer. If that is not available then it falls back on the CVSS v3.1 rating. If that is also not available, then the CVSS v2.0 rating is used instead. Details on this implementation are available on the respective issues for [trivy](https://github.com/aquasecurity/trivy/issues/310) and [grype](https://github.com/anchore/grype/issues/287).
+
## Fuzz Testing
All fuzz testing results are reported as Unknown. They should be reviewed and triaged manually to find exploitable faults to prioritize for fixing.
diff --git a/doc/user/group/epics/img/epic_board_v15_10.png b/doc/user/group/epics/img/epic_board_v15_10.png
index d836abdbb59..03bc96d9623 100644
--- a/doc/user/group/epics/img/epic_board_v15_10.png
+++ b/doc/user/group/epics/img/epic_board_v15_10.png
Binary files differ
diff --git a/doc/user/profile/img/user_profile_achievements_v15_11.png b/doc/user/profile/img/user_profile_achievements_v15_11.png
index 28c5661dd54..cf675cd6b06 100644
--- a/doc/user/profile/img/user_profile_achievements_v15_11.png
+++ b/doc/user/profile/img/user_profile_achievements_v15_11.png
Binary files differ
diff --git a/lib/gitlab/auth/u2f_webauthn_converter.rb b/lib/gitlab/auth/u2f_webauthn_converter.rb
deleted file mode 100644
index 20b5d2ddc88..00000000000
--- a/lib/gitlab/auth/u2f_webauthn_converter.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-require 'webauthn/u2f_migrator'
-
-module Gitlab
- module Auth
- class U2fWebauthnConverter
- def initialize(u2f_registration)
- @u2f_registration = u2f_registration
- end
-
- def convert
- now = Time.current
-
- converted_credential = WebAuthn::U2fMigrator.new(
- app_id: Gitlab.config.gitlab.url,
- certificate: u2f_registration.certificate,
- key_handle: u2f_registration.key_handle,
- public_key: u2f_registration.public_key,
- counter: u2f_registration.counter
- ).credential
-
- {
- credential_xid: Base64.strict_encode64(converted_credential.id),
- public_key: Base64.strict_encode64(converted_credential.public_key),
- counter: u2f_registration.counter || 0,
- name: u2f_registration.name || '',
- user_id: u2f_registration.user_id,
- u2f_registration_id: u2f_registration.id,
- created_at: now,
- updated_at: now
- }
- end
-
- private
-
- attr_reader :u2f_registration
- end
- end
-end
diff --git a/lib/gitlab/background_migration/migrate_u2f_webauthn.rb b/lib/gitlab/background_migration/migrate_u2f_webauthn.rb
deleted file mode 100644
index 83aa36a11e6..00000000000
--- a/lib/gitlab/background_migration/migrate_u2f_webauthn.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-# rubocop:disable Style/Documentation
-
-module Gitlab
- module BackgroundMigration
- class MigrateU2fWebauthn
- class U2fRegistration < ActiveRecord::Base
- self.table_name = 'u2f_registrations'
- end
-
- class WebauthnRegistration < ActiveRecord::Base
- self.table_name = 'webauthn_registrations'
- end
-
- def perform(start_id, end_id)
- old_registrations = U2fRegistration.where(id: start_id..end_id)
- old_registrations.each_slice(100) do |slice|
- values = slice.map do |u2f_registration|
- converter = Gitlab::Auth::U2fWebauthnConverter.new(u2f_registration)
- converter.convert
- end
-
- WebauthnRegistration.insert_all(values, unique_by: :credential_xid, returning: false)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
index 45f836f10d3..ef86c9d6007 100644
--- a/lib/gitlab/tracking.rb
+++ b/lib/gitlab/tracking.rb
@@ -8,13 +8,30 @@ module Gitlab
end
def event(category, action, label: nil, property: nil, value: nil, context: [], project: nil, user: nil, namespace: nil, **extra) # rubocop:disable Metrics/ParameterLists
- contexts = [Tracking::StandardContext.new(project: project, user: user, namespace: namespace, **extra).to_context, *context]
+ action = action.to_s
+ contexts = [
+ Tracking::StandardContext.new(
+ project: project,
+ user: user,
+ namespace: namespace,
+ **extra).to_context, *context
+ ]
+
+ track_struct_event(tracker, category, action, label: label, property: property, value: value, contexts: contexts)
+ end
+ def database_event(category, action, label: nil, property: nil, value: nil, context: [], project: nil, user: nil, namespace: nil, **extra) # rubocop:disable Metrics/ParameterLists
action = action.to_s
+ destination = Gitlab::Tracking::Destinations::DatabaseEventsSnowplow.new
+ contexts = [
+ Tracking::StandardContext.new(
+ project: project,
+ user: user,
+ namespace: namespace,
+ **extra).to_context, *context
+ ]
- tracker.event(category, action, label: label, property: property, value: value, context: contexts)
- rescue StandardError => error
- Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error, snowplow_category: category, snowplow_action: action)
+ track_struct_event(destination, category, action, label: label, property: property, value: value, contexts: contexts)
end
def definition(basename, category: nil, action: nil, label: nil, property: nil, value: nil, context: [], project: nil, user: nil, namespace: nil, **extra) # rubocop:disable Metrics/ParameterLists
@@ -48,6 +65,13 @@ module Gitlab
private
+ def track_struct_event(destination, category, action, label:, property:, value:, contexts:) # rubocop:disable Metrics/ParameterLists
+ destination
+ .event(category, action, label: label, property: property, value: value, context: contexts)
+ rescue StandardError => error
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error, snowplow_category: category, snowplow_action: action)
+ end
+
def tracker
@tracker ||= if snowplow_micro_enabled?
Gitlab::Tracking::Destinations::SnowplowMicro.new
diff --git a/lib/gitlab/tracking/destinations/database_events_snowplow.rb b/lib/gitlab/tracking/destinations/database_events_snowplow.rb
new file mode 100644
index 00000000000..4f9cd2167f7
--- /dev/null
+++ b/lib/gitlab/tracking/destinations/database_events_snowplow.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Tracking
+ module Destinations
+ class DatabaseEventsSnowplow < Snowplow
+ extend ::Gitlab::Utils::Override
+
+ HOSTNAME = 'localhost:9091'
+
+ override :enabled?
+ # database events are only collected for SaaS instance
+ def enabled?
+ ::Gitlab.dev_or_test_env? || ::Gitlab.com?
+ end
+
+ override :hostname
+ def hostname
+ HOSTNAME
+ end
+
+ private
+
+ override :increment_failed_events_emissions
+ def increment_failed_events_emissions(value)
+ Gitlab::Metrics.counter(
+ :gitlab_db_events_snowplow_failed_events_total,
+ 'Number of failed Snowplow events emissions'
+ ).increment({}, value.to_i)
+ end
+
+ override :increment_successful_events_emissions
+ def increment_successful_events_emissions(value)
+ Gitlab::Metrics.counter(
+ :gitlab_db_events_snowplow_successful_events_total,
+ 'Number of successful Snowplow events emissions'
+ ).increment({}, value.to_i)
+ end
+
+ override :increment_total_events_counter
+ def increment_total_events_counter
+ Gitlab::Metrics.counter(
+ :gitlab_db_events_snowplow_events_total,
+ 'Number of Snowplow events'
+ ).increment
+ end
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8ae1865dbff..8eab8cf8596 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -29611,6 +29611,9 @@ msgstr ""
msgid "Notify|%{singular_or_plural_line} %{error_lines}: Work item type is not available. Please check your license and permissions."
msgstr ""
+msgid "Notify|%{strong_open}%{user_name}%{strong_close} updated the custom email address credentials for the Service Desk of %{project_link_start}%{project_name}%{project_link_end} and triggered the verification process."
+msgstr ""
+
msgid "Notify|%{update_at_start} Last update at %{update_at_mid} %{last_update_at} %{update_at_end}"
msgstr ""
@@ -29689,6 +29692,9 @@ msgstr ""
msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
msgstr ""
+msgid "Notify|If this was a mistake you can change these settings or deactivate the custom email address in the project settings."
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -29794,6 +29800,9 @@ msgstr ""
msgid "Notify|The project is now located under %{project_full_name_link_start}%{project_full_name}%{link_end}."
msgstr ""
+msgid "Notify|The provided custom email address is %{strong_open}%{email_address}%{strong_close} and uses the SMTP host %{strong_open}%{smtp_host}%{strong_close}."
+msgstr ""
+
msgid "Notify|The push did not contain any new commits, but force pushed to delete the commits and changes below."
msgstr ""
@@ -29806,6 +29815,9 @@ msgstr ""
msgid "Notify|Unless you verify your domain by %{time_start}%{time}%{time_end} it will be removed from your GitLab project."
msgstr ""
+msgid "Notify|Verification for custom email %{email} for %{project_name} triggered"
+msgstr ""
+
msgid "Notify|You don't have access to the project."
msgstr ""
@@ -48601,6 +48613,9 @@ msgstr ""
msgid "Vulnerability|Actual received response is the one received when this fault was detected"
msgstr ""
+msgid "Vulnerability|Actual response:"
+msgstr ""
+
msgid "Vulnerability|Add another identifier"
msgstr ""
diff --git a/scripts/create-pipeline-failure-incident.rb b/scripts/create-pipeline-failure-incident.rb
deleted file mode 100755
index 05cba98d2fc..00000000000
--- a/scripts/create-pipeline-failure-incident.rb
+++ /dev/null
@@ -1,303 +0,0 @@
-#!/usr/bin/env ruby
-
-# frozen_string_literal: true
-
-require 'optparse'
-require 'json'
-
-require_relative 'api/commit_merge_requests'
-require_relative 'api/create_issue'
-require_relative 'api/create_issue_discussion'
-require_relative 'api/pipeline_failed_jobs'
-
-class CreatePipelineFailureIncident
- DEFAULT_OPTIONS = {
- project: nil,
- incident_json_file: 'incident.json'
- }.freeze
-
- def initialize(options)
- @project = options.delete(:project)
- @api_token = options.delete(:api_token)
- end
-
- def execute
- payload = {
- issue_type: 'incident',
- title: title,
- description: description,
- labels: incident_labels
- }
-
- payload[:assignee_ids] = assignee_ids if stable_branch_incident?
-
- CreateIssue.new(project: project, api_token: api_token).execute(payload).tap do |incident|
- CreateIssueDiscussion.new(project: project, api_token: api_token)
- .execute(issue_iid: incident.iid, body: "## Root Cause Analysis")
- CreateIssueDiscussion.new(project: project, api_token: api_token)
- .execute(issue_iid: incident.iid, body: "## Investigation Steps")
- end
- end
-
- private
-
- attr_reader :project, :api_token
-
- def stable_branch_incident?
- ENV['CI_COMMIT_REF_NAME'] =~ /^[\d-]+-stable(-ee)?$/
- end
-
- def review_apps_incident?
- project.end_with?('review-apps-broken-incidents')
- end
-
- def failed_jobs
- @failed_jobs ||= PipelineFailedJobs.new(API::DEFAULT_OPTIONS.merge(exclude_allowed_to_fail_jobs: true)).execute
- end
-
- def merge_request
- @merge_request ||= CommitMergeRequests.new(
- API::DEFAULT_OPTIONS.merge(sha: ENV['CI_COMMIT_SHA'])
- ).execute.first
- end
-
- def now
- @now ||= Time.now.utc
- end
-
- def title
- @title ||= begin
- full_title = "#{now.strftime('%A %F %R UTC')} - `#{ENV['CI_PROJECT_PATH']}` " \
- "broken `#{ENV['CI_COMMIT_REF_NAME']}` with #{failed_jobs.map(&:name).join(', ')}"
-
- if full_title.size >= 255
- "#{full_title[...252]}..." # max title length is 255, and we add an elipsis
- else
- full_title
- end
- end
- end
-
- def description
- if stable_branch_incident?
- broken_stable_description_content
- elsif review_apps_incident?
- broken_review_apps_description_content
- else
- broken_master_description_content
- end
- end
-
- def broken_master_description_content
- <<~MARKDOWN
- ## #{project_link} pipeline #{pipeline_link} failed
-
- **Branch: #{branch_link}**
-
- **Commit: #{commit_link}**
-
- **Triggered by** #{triggered_by_link} • **Source:** #{source} • **Duration:** #{pipeline_duration} minutes
-
- **Failed jobs (#{failed_jobs.size}):**
-
- #{failed_jobs_list}
-
- ### General guidelines
-
- Follow the [Broken `master` handbook guide](https://about.gitlab.com/handbook/engineering/workflow/#broken-master).
-
- ### Investigation
-
- **Be sure to fill the `Timeline` for this incident.**
-
- 1. If the failure is new, and looks like a potential flaky failure, you can retry the failing job.
- Make sure to mention the retry in the `Timeline` and leave a link to the retried job.
- 1. If the failure looks like a broken `master`, communicate the broken `master` in Slack using the "Broadcast Master Broken" workflow:
- - Click the Shortcut lightning bolt icon in the `#master-broken` channel and select "Broadcast Master Broken".
- - Click "Continue the broadcast" after the automated message in `#master-broken`.
-
- ### Pre-resolution
-
- If you believe that there's an easy resolution by either:
-
- - Reverting a particular merge request.
- - Making a quick fix (for example, one line or a few similar simple changes in a few lines).
- You can create a merge request, assign to any available maintainer, and ping people that were involved/related to the introduction of the failure.
- Additionally, a message can be posted in `#backend_maintainers` or `#frontend_maintainers` to get a maintainer take a look at the fix ASAP.
-
- In both cases, make sure to add the ~"pipeline:expedite" label, and `master:broken` or `master:foss-broken` label, to speed up the `master`-fixing pipelines.
-
- ### Resolution
-
- Follow [the Resolution steps from the handbook](https://about.gitlab.com/handbook/engineering/workflow/#responsibilities-of-the-resolution-dri).
- MARKDOWN
- end
-
- def broken_stable_description_content
- <<~MARKDOWN
- ## #{project_link} pipeline #{pipeline_link} failed
-
- **Branch: #{branch_link}**
-
- **Commit: #{commit_link}**
-
- **Merge Request: #{merge_request_link}**
-
- **Triggered by** #{triggered_by_link} • **Source:** #{source} • **Duration:** #{pipeline_duration} minutes
-
- **Failed jobs (#{failed_jobs.size}):**
-
- #{failed_jobs_list}
-
- ### General guidelines
-
- A broken stable branch prevents patch releases from being built.
- Fixing the pipeline is a priority to prevent any delays in releases.
-
- The process in the [Broken `master` handbook guide](https://about.gitlab.com/handbook/engineering/workflow/#broken-master) can be referenced since much of that process also applies here.
-
- ### Investigation
-
- **Be sure to fill the `Timeline` for this incident.**
-
- 1. If the failure is new, and looks like a potential flaky failure, you can retry the failing job.
- Make sure to mention the retry in the `Timeline` and leave a link to the retried job.
- 1. Search for similar master-broken issues in https://gitlab.com/gitlab-org/quality/engineering-productivity/master-broken-incidents/-/issues
- 1. If one exists, ask the DRI of the master-broken issue to cherry-pick any resulting merge requests into the stable branch
-
- @gitlab-org/release/managers if the merge request author or maintainer is not available, this can be escalated using the dev-on-call process in the [#dev-escalation slack channel](https://gitlab.slack.com/archives/CLKLMSUR4).
-
- ### Pre-resolution
-
- If you believe that there's an easy resolution by either:
-
- - Reverting a particular merge request.
- - Making a quick fix (for example, one line or a few similar simple changes in a few lines).
- You can create a merge request, assign to any available maintainer, and ping people that were involved/related to the introduction of the failure.
- Additionally, a message can be posted in `#backend_maintainers` or `#frontend_maintainers` to get a maintainer take a look at the fix ASAP.
- - Cherry picking a change that was used to fix a similar master-broken issue.
-
- ### Resolution
-
- Add a comment to this issue describing how this incident could have been prevented earlier in the Merge Request pipeline (rather than the merge commit pipeline).
-
- MARKDOWN
- end
-
- def broken_review_apps_description_content
- <<~MARKDOWN
- ## #{project_link} pipeline #{pipeline_link} failed
-
- **Branch: #{branch_link}**
-
- **Commit: #{commit_link}**
-
- **Triggered by** #{triggered_by_link} • **Source:** #{source} • **Duration:** #{pipeline_duration} minutes
-
- **Failed jobs (#{failed_jobs.size}):**
-
- #{failed_jobs_list}
-
- ### General guidelines
-
- Please refer to [the review-apps triaging process](https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/review-apps.md#review-apps-broken-slack-channel-isnt-empty).
- MARKDOWN
- end
-
- def incident_labels
- if stable_branch_incident?
- ['release-blocker']
- elsif review_apps_incident?
- ['review-apps-broken', 'Engineering Productivity', 'ep::review-apps']
- else
- master_broken_label =
- if ENV['CI_PROJECT_NAME'] == 'gitlab-foss'
- 'master:foss-broken'
- else
- 'master:broken'
- end
-
- [master_broken_label, 'Engineering Productivity', 'master-broken::undetermined']
- end
- end
-
- def assignee_ids
- ids = [ENV['GITLAB_USER_ID'].to_i]
- ids << merge_request['author']['id'].to_i if merge_request
- ids
- end
-
- def pipeline_link
- "[##{ENV['CI_PIPELINE_ID']}](#{ENV['CI_PIPELINE_URL']})"
- end
-
- def branch_link
- "[`#{ENV['CI_COMMIT_REF_NAME']}`](#{ENV['CI_PROJECT_URL']}/-/commits/#{ENV['CI_COMMIT_REF_NAME']})"
- end
-
- def pipeline_duration
- ((Time.now - Time.parse(ENV['CI_PIPELINE_CREATED_AT'])) / 60.to_f).round(2)
- end
-
- def commit_link
- "[#{ENV['CI_COMMIT_TITLE']}](#{ENV['CI_PROJECT_URL']}/-/commit/#{ENV['CI_COMMIT_SHA']})"
- end
-
- def merge_request_link
- return 'N/A' unless merge_request
-
- "[#{merge_request['title']}](#{merge_request['web_url']})"
- end
-
- def source
- "`#{ENV['CI_PIPELINE_SOURCE']}`"
- end
-
- def project_link
- "[#{ENV['CI_PROJECT_PATH']}](#{ENV['CI_PROJECT_URL']})"
- end
-
- def triggered_by_link
- "[#{ENV['GITLAB_USER_NAME']}](#{ENV['CI_SERVER_URL']}/#{ENV['GITLAB_USER_LOGIN']})"
- end
-
- def failed_jobs_list_for_title
- failed_jobs.map(&:name).join(', ')
- end
-
- def failed_jobs_list
- failed_jobs.map { |job| "- [#{job.name}](#{job.web_url})" }.join("\n")
- end
-end
-
-if $PROGRAM_NAME == __FILE__
- options = CreatePipelineFailureIncident::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String, "Project where to create the incident (defaults to "\
- "`#{CreatePipelineFailureIncident::DEFAULT_OPTIONS[:project]}`)") do |value|
- options[:project] = value
- end
-
- opts.on("-f", "--incident-json-file file_path", String, "Path to a file where to save the incident JSON data "\
- "(defaults to `#{CreatePipelineFailureIncident::DEFAULT_OPTIONS[:incident_json_file] || 'nil'}`)") do |value|
- options[:incident_json_file] = value
- end
-
- opts.on("-t", "--api-token API_TOKEN", String, "A valid Project token with the `Reporter` role and `api` scope "\
- "to create the incident") do |value|
- options[:api_token] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- incident_json_file = options.delete(:incident_json_file)
-
- CreatePipelineFailureIncident.new(options).execute.tap do |incident|
- File.write(incident_json_file, JSON.pretty_generate(incident.to_h)) if incident_json_file
- end
-end
diff --git a/scripts/generate-failed-pipeline-slack-message.rb b/scripts/generate-failed-pipeline-slack-message.rb
deleted file mode 100755
index 2a406ad7e23..00000000000
--- a/scripts/generate-failed-pipeline-slack-message.rb
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/usr/bin/env ruby
-
-# frozen_string_literal: true
-
-require 'optparse'
-require 'json'
-
-require_relative 'api/pipeline_failed_jobs'
-
-class GenerateFailedPipelineSlackMessage
- DEFAULT_OPTIONS = {
- project: nil,
- failed_pipeline_slack_message_file: 'failed_pipeline_slack_message.json',
- incident_json_file: 'incident.json'
- }.freeze
-
- def initialize(options)
- @project = options.delete(:project)
- @incident_json_file = options.delete(:incident_json_file)
- end
-
- def execute
- {
- channel: ENV['SLACK_CHANNEL'],
- username: "Failed pipeline reporter",
- icon_emoji: ":boom:",
- text: "*#{title}*",
- blocks: [
- {
- type: "section",
- text: {
- type: "mrkdwn",
- text: "*#{title}*"
- },
- accessory: {
- type: "button",
- text: {
- type: "plain_text",
- text: incident_button_text
- },
- url: incident_button_link
- }
- },
- {
- type: "section",
- text: {
- type: "mrkdwn",
- text: "*Branch*: #{branch_link}"
- }
- },
- {
- type: "section",
- text: {
- type: "mrkdwn",
- text: "*Commit*: #{commit_link}"
- }
- },
- {
- type: "section",
- text: {
- type: "mrkdwn",
- text: "*Triggered by* #{triggered_by_link} • *Source:* #{source} • *Duration:* #{pipeline_duration} minutes"
- }
- },
- {
- type: "section",
- text: {
- type: "mrkdwn",
- text: "*Failed jobs (#{failed_jobs.size}):* #{failed_jobs_list}"
- }
- }
- ]
- }
- end
-
- private
-
- attr_reader :project, :incident_json_file
-
- def failed_jobs
- @failed_jobs ||= PipelineFailedJobs.new(API::DEFAULT_OPTIONS.dup.merge(exclude_allowed_to_fail_jobs: true)).execute
- end
-
- def title
- "#{project_link} pipeline #{pipeline_link} failed"
- end
-
- def incident_exist?
- return @incident_exist if defined?(@incident_exist)
-
- @incident_exist = File.exist?(incident_json_file)
- end
-
- def incident
- return unless incident_exist?
-
- @incident ||= JSON.parse(File.read(incident_json_file))
- end
-
- def incident_button_text
- if incident_exist?
- "View incident ##{incident['iid']}"
- else
- 'Create incident'
- end
- end
-
- def incident_button_link
- if incident_exist?
- incident['web_url']
- else
- "#{ENV['CI_SERVER_URL']}/#{project}/-/issues/new?" \
- "issuable_template=incident&issue%5Bissue_type%5D=incident"
- end
- end
-
- def pipeline_link
- "<#{ENV['CI_PIPELINE_URL']}|##{ENV['CI_PIPELINE_ID']}>"
- end
-
- def branch_link
- "<#{ENV['CI_PROJECT_URL']}/-/commits/#{ENV['CI_COMMIT_REF_NAME']}|`#{ENV['CI_COMMIT_REF_NAME']}`>"
- end
-
- def pipeline_duration
- ((Time.now - Time.parse(ENV['CI_PIPELINE_CREATED_AT'])) / 60.to_f).round(2)
- end
-
- def commit_link
- "<#{ENV['CI_PROJECT_URL']}/-/commit/#{ENV['CI_COMMIT_SHA']}|#{ENV['CI_COMMIT_TITLE']}>"
- end
-
- def source
- "`#{ENV['CI_PIPELINE_SOURCE']}#{schedule_type}`"
- end
-
- def schedule_type
- ENV['CI_PIPELINE_SOURCE'] == 'schedule' ? ": #{ENV['SCHEDULE_TYPE']}" : ''
- end
-
- def project_link
- "<#{ENV['CI_PROJECT_URL']}|#{ENV['CI_PROJECT_PATH']}>"
- end
-
- def triggered_by_link
- "<#{ENV['CI_SERVER_URL']}/#{ENV['GITLAB_USER_LOGIN']}|#{ENV['GITLAB_USER_NAME']}>"
- end
-
- def failed_jobs_list
- failed_jobs.map { |job| "<#{job.web_url}|#{job.name}>" }.join(', ')
- end
-end
-
-if $PROGRAM_NAME == __FILE__
- options = GenerateFailedPipelineSlackMessage::DEFAULT_OPTIONS.dup
-
- OptionParser.new do |opts|
- opts.on("-p", "--project PROJECT", String, "Full project path where the incidents are stored (defaults to "\
- "`#{GenerateFailedPipelineSlackMessage::DEFAULT_OPTIONS[:project]}`)") do |value|
- options[:project] = value
- end
-
- opts.on("-i", "--incident-json-file file_path", String, "Path to a file where the incident JSON data "\
- "can be found (defaults to "\
- "`#{GenerateFailedPipelineSlackMessage::DEFAULT_OPTIONS[:incident_json_file]}`)") do |value|
- options[:incident_json_file] = value
- end
-
- opts.on("-f", "--failed-pipeline-slack-message-file file_path", String, "Path to a file where to save the Slack "\
- "message (defaults to "\
- "`#{GenerateFailedPipelineSlackMessage::DEFAULT_OPTIONS[:failed_pipeline_slack_message_file]}`)") do |value|
- options[:failed_pipeline_slack_message_file] = value
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- failed_pipeline_slack_message_file = options.delete(:failed_pipeline_slack_message_file)
-
- GenerateFailedPipelineSlackMessage.new(options).execute.tap do |message_payload|
- if failed_pipeline_slack_message_file
- File.write(failed_pipeline_slack_message_file, JSON.pretty_generate(message_payload))
- end
- end
-end
diff --git a/spec/factories/member_roles.rb b/spec/factories/member_roles.rb
deleted file mode 100644
index 503438d2521..00000000000
--- a/spec/factories/member_roles.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :member_role do
- namespace { association(:group) }
- base_access_level { Gitlab::Access::DEVELOPER }
-
- trait(:developer) { base_access_level { Gitlab::Access::DEVELOPER } }
- trait(:guest) { base_access_level { Gitlab::Access::GUEST } }
- end
-end
diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb
index 24e47dc43ae..da985c6dc07 100644
--- a/spec/features/dashboard/snippets_spec.rb
+++ b/spec/features/dashboard/snippets_spec.rb
@@ -44,7 +44,8 @@ RSpec.describe 'Dashboard snippets', feature_category: :source_code_management d
element = page.find('.row.empty-state')
expect(element).to have_content("Code snippets")
- expect(element.find('.svg-content img.js-lazy-loaded')['src']).to have_content('illustrations/snippets_empty')
+ expect(element.find('.svg-content img.js-lazy-loaded')['src'])
+ .to have_content('illustrations/empty-state/empty-snippets-md')
end
it 'shows new snippet button in main content area' do
diff --git a/spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js b/spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js
index be61ffa92b4..bb3c0bc1526 100644
--- a/spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js
+++ b/spec/frontend/groups/components/empty_states/archived_projects_empty_state_spec.js
@@ -6,7 +6,7 @@ import ArchivedProjectsEmptyState from '~/groups/components/empty_states/archive
let wrapper;
const defaultProvide = {
- newProjectIllustration: '/assets/illustrations/project-create-new-sm.svg',
+ emptyProjectsIllustration: '/assets/llustrations/empty-state/empty-projects-md.svg',
};
const createComponent = () => {
@@ -21,7 +21,7 @@ describe('ArchivedProjectsEmptyState', () => {
expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
title: ArchivedProjectsEmptyState.i18n.title,
- svgPath: defaultProvide.newProjectIllustration,
+ svgPath: defaultProvide.emptyProjectsIllustration,
});
});
});
diff --git a/spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js b/spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js
index c4ace1be1f3..8ba1c480d5e 100644
--- a/spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js
+++ b/spec/frontend/groups/components/empty_states/shared_projects_empty_state_spec.js
@@ -6,7 +6,7 @@ import SharedProjectsEmptyState from '~/groups/components/empty_states/shared_pr
let wrapper;
const defaultProvide = {
- newProjectIllustration: '/assets/illustrations/project-create-new-sm.svg',
+ emptyProjectsIllustration: '/assets/illustrations/empty-state/empty-projects-md.svg',
};
const createComponent = () => {
@@ -21,7 +21,7 @@ describe('SharedProjectsEmptyState', () => {
expect(wrapper.findComponent(GlEmptyState).props()).toMatchObject({
title: SharedProjectsEmptyState.i18n.title,
- svgPath: defaultProvide.newProjectIllustration,
+ svgPath: defaultProvide.emptyProjectsIllustration,
});
});
});
diff --git a/spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js b/spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js
index dc4271b98ee..5ae4d0be7d6 100644
--- a/spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js
+++ b/spec/frontend/groups/components/empty_states/subgroups_and_projects_empty_state_spec.js
@@ -10,6 +10,7 @@ const defaultProvide = {
newProjectPath: '/projects/new?namespace_id=231',
newSubgroupIllustration: '/assets/illustrations/group-new.svg',
newSubgroupPath: '/groups/new?parent_id=231',
+ emptyProjectsIllustration: '/assets/illustrations/empty-state/empty-projects-md.svg',
emptySubgroupIllustration: '/assets/illustrations/empty-state/empty-subgroup-md.svg',
canCreateSubgroups: true,
canCreateProjects: true,
diff --git a/spec/frontend/groups/components/overview_tabs_spec.js b/spec/frontend/groups/components/overview_tabs_spec.js
index 906609c97f9..101dd06d578 100644
--- a/spec/frontend/groups/components/overview_tabs_spec.js
+++ b/spec/frontend/groups/components/overview_tabs_spec.js
@@ -39,6 +39,7 @@ describe('OverviewTabs', () => {
newProjectPath: 'projects/new',
newSubgroupIllustration: '',
newProjectIllustration: '',
+ emptyProjectsIllustration: '',
emptySubgroupIllustration: '',
canCreateSubgroups: false,
canCreateProjects: false,
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index bb1a4d57cc0..00d5308d552 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -706,4 +706,27 @@ RSpec.describe ApplicationHelper do
expect(helper.stylesheet_link_tag_defer('test')).to eq( '<link rel="stylesheet" media="screen" href="/stylesheets/test.css" />')
end
end
+
+ describe 'sign_in_with_redirect?' do
+ context 'when on the sign-in page that redirects afterwards' do
+ before do
+ allow(helper).to receive(:current_page?).and_return(true)
+ session[:user_return_to] = true
+ end
+
+ it 'returns true' do
+ expect(helper.sign_in_with_redirect?).to be_truthy
+ end
+ end
+
+ context 'when on a non sign-in page' do
+ before do
+ allow(helper).to receive(:current_page?).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(helper.sign_in_with_redirect?).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index ce439e5bcdd..f66f9a8a58e 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -497,6 +497,7 @@ RSpec.describe GroupsHelper do
new_project_path: including("/projects/new?namespace_id=#{group.id}"),
new_subgroup_illustration: including('illustrations/subgroup-create-new-sm'),
new_project_illustration: including('illustrations/project-create-new-sm'),
+ empty_projects_illustration: including('illustrations/empty-state/empty-projects-md'),
empty_subgroup_illustration: including('illustrations/empty-state/empty-subgroup-md'),
render_empty_state: 'true',
can_create_subgroups: 'true',
diff --git a/spec/lib/gitlab/auth/u2f_webauthn_converter_spec.rb b/spec/lib/gitlab/auth/u2f_webauthn_converter_spec.rb
deleted file mode 100644
index deddc7f5294..00000000000
--- a/spec/lib/gitlab/auth/u2f_webauthn_converter_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Auth::U2fWebauthnConverter do
- let_it_be(:u2f_registration) do
- device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5))
- create(:u2f_registration, name: 'u2f_device',
- certificate: Base64.strict_encode64(device.cert_raw),
- key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
- public_key: Base64.strict_encode64(device.origin_public_key_raw))
- end
-
- it 'converts u2f registration' do
- webauthn_credential = WebAuthn::U2fMigrator.new(
- app_id: Gitlab.config.gitlab.url,
- certificate: u2f_registration.certificate,
- key_handle: u2f_registration.key_handle,
- public_key: u2f_registration.public_key,
- counter: u2f_registration.counter
- ).credential
-
- converted_webauthn = described_class.new(u2f_registration).convert
-
- expect(converted_webauthn).to(
- include(user_id: u2f_registration.user_id,
- credential_xid: Base64.strict_encode64(webauthn_credential.id)))
- end
-end
diff --git a/spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb b/spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb
deleted file mode 100644
index 6554ee9d7b5..00000000000
--- a/spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-require 'webauthn/u2f_migrator'
-
-RSpec.describe Gitlab::BackgroundMigration::MigrateU2fWebauthn, :migration, schema: 20211202041233 do
- let(:users) { table(:users) }
-
- let(:user) { users.create!(email: 'email@email.com', name: 'foo', username: 'foo', projects_limit: 0) }
-
- let(:u2f_registrations) { table(:u2f_registrations) }
- let(:webauthn_registrations) { table(:webauthn_registrations) }
-
- let!(:u2f_registration_not_migrated) { create_u2f_registration(1, 'reg1') }
- let!(:u2f_registration_not_migrated_no_name) { create_u2f_registration(2, nil, 2) }
- let!(:u2f_registration_migrated) { create_u2f_registration(3, 'reg3') }
-
- subject { described_class.new.perform(1, 3) }
-
- before do
- converted_credential = convert_credential_for(u2f_registration_migrated)
- webauthn_registrations.create!(converted_credential)
- end
-
- it 'migrates all records' do
- expect { subject }.to change { webauthn_registrations.count }.from(1).to(3)
-
- all_webauthn_registrations = webauthn_registrations.all.map(&:attributes)
-
- [u2f_registration_not_migrated, u2f_registration_not_migrated_no_name].each do |u2f_registration|
- expected_credential = convert_credential_for(u2f_registration).except(:created_at).stringify_keys
- expect(all_webauthn_registrations).to include(a_hash_including(expected_credential))
- end
- end
-
- def create_u2f_registration(id, name, counter = 5)
- device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5))
- u2f_registrations.create!({ id: id,
- certificate: Base64.strict_encode64(device.cert_raw),
- key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
- public_key: Base64.strict_encode64(device.origin_public_key_raw),
- counter: counter,
- name: name,
- user_id: user.id })
- end
-
- def convert_credential_for(u2f_registration)
- converted_credential = WebAuthn::U2fMigrator.new(
- app_id: Gitlab.config.gitlab.url,
- certificate: u2f_registration.certificate,
- key_handle: u2f_registration.key_handle,
- public_key: u2f_registration.public_key,
- counter: u2f_registration.counter
- ).credential
-
- {
- credential_xid: Base64.strict_encode64(converted_credential.id),
- public_key: Base64.strict_encode64(converted_credential.public_key),
- counter: u2f_registration.counter,
- name: u2f_registration.name || '',
- user_id: u2f_registration.user_id,
- u2f_registration_id: u2f_registration.id,
- created_at: u2f_registration.created_at
- }
- end
-end
diff --git a/spec/lib/gitlab/tracking/destinations/database_events_snowplow_spec.rb b/spec/lib/gitlab/tracking/destinations/database_events_snowplow_spec.rb
new file mode 100644
index 00000000000..0f2082c1f25
--- /dev/null
+++ b/spec/lib/gitlab/tracking/destinations/database_events_snowplow_spec.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Tracking::Destinations::DatabaseEventsSnowplow, :do_not_stub_snowplow_by_default, feature_category: :application_instrumentation do
+ let(:emitter) { SnowplowTracker::Emitter.new(endpoint: 'localhost', options: { buffer_size: 1 }) }
+
+ let(:tracker) do
+ SnowplowTracker::Tracker
+ .new(
+ emitters: [emitter],
+ subject: SnowplowTracker::Subject.new,
+ namespace: 'namespace',
+ app_id: 'app_id'
+ )
+ end
+
+ before do
+ stub_application_setting(snowplow_app_id: '_abc123_')
+ end
+
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ context 'when snowplow is enabled' do
+ before do
+ allow(SnowplowTracker::AsyncEmitter)
+ .to receive(:new)
+ .with(endpoint: 'localhost:9091',
+ options:
+ {
+ protocol: 'https',
+ on_success: subject.method(:increment_successful_events_emissions),
+ on_failure: subject.method(:failure_callback)
+ }
+ ).and_return(emitter)
+
+ allow(SnowplowTracker::Tracker)
+ .to receive(:new)
+ .with(
+ emitters: [emitter],
+ subject: an_instance_of(SnowplowTracker::Subject),
+ namespace: described_class::SNOWPLOW_NAMESPACE,
+ app_id: '_abc123_'
+ ).and_return(tracker)
+ end
+
+ describe '#event' do
+ it 'sends event to tracker' do
+ allow(tracker).to receive(:track_struct_event).and_call_original
+
+ subject.event('category', 'action', label: 'label', property: 'property', value: 1.5)
+
+ expect(tracker)
+ .to have_received(:track_struct_event)
+ .with(category: 'category', action: 'action', label: 'label', property: 'property', value: 1.5, context: nil,
+ tstamp: (Time.now.to_f * 1000).to_i)
+ end
+
+ it 'increase total snowplow events counter' do
+ counter = double
+
+ expect(counter).to receive(:increment)
+ expect(Gitlab::Metrics).to receive(:counter)
+ .with(:gitlab_db_events_snowplow_events_total, 'Number of Snowplow events')
+ .and_return(counter)
+
+ subject.event('category', 'action', label: 'label', property: 'property', value: 1.5)
+ end
+ end
+ end
+
+ context 'for callbacks' do
+ describe 'on success' do
+ it 'increase gitlab_successful_snowplow_events_total counter' do
+ counter = double
+
+ expect(counter).to receive(:increment).with({}, 2)
+ expect(Gitlab::Metrics).to receive(:counter)
+ .with(
+ :gitlab_db_events_snowplow_successful_events_total,
+ 'Number of successful Snowplow events emissions').and_return(counter)
+
+ subject.method(:increment_successful_events_emissions).call(2)
+ end
+ end
+
+ describe 'on failure' do
+ it 'increase gitlab_failed_snowplow_events_total counter and logs failures', :aggregate_failures do
+ counter = double
+ error_message = "Issue database_event_update failed to be reported to collector at localhost:9091"
+ failures = [{ "e" => "se",
+ "se_ca" => "Issue",
+ "se_la" => "issues",
+ "se_ac" => "database_event_update" }]
+ allow(Gitlab::Metrics).to receive(:counter)
+ .with(
+ :gitlab_db_events_snowplow_successful_events_total,
+ 'Number of successful Snowplow events emissions').and_call_original
+
+ expect(Gitlab::AppLogger).to receive(:error).with(error_message)
+ expect(counter).to receive(:increment).with({}, 1)
+ expect(Gitlab::Metrics).to receive(:counter)
+ .with(
+ :gitlab_db_events_snowplow_failed_events_total,
+ 'Number of failed Snowplow events emissions').and_return(counter)
+
+ subject.method(:failure_callback).call(2, failures)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index e79bb2ef129..56be80678e9 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Gitlab::Tracking do
+RSpec.describe Gitlab::Tracking, feature_category: :application_instrumentation do
include StubENV
before do
@@ -102,12 +102,28 @@ RSpec.describe Gitlab::Tracking do
end
end
- describe '.event' do
+ context 'event tracking' do
let(:namespace) { create(:namespace) }
- shared_examples 'delegates to destination' do |klass|
+ shared_examples 'rescued error raised by destination class' do
+ it 'rescues error' do
+ error = StandardError.new("something went wrong")
+ allow_any_instance_of(destination_class).to receive(:event).and_raise(error)
+
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
+ .with(
+ error,
+ snowplow_category: category,
+ snowplow_action: action
+ )
+
+ expect { tracking_method }.not_to raise_error
+ end
+ end
+
+ shared_examples 'delegates to destination' do |klass, method|
before do
- allow_any_instance_of(Gitlab::Tracking::Destinations::Snowplow).to receive(:event)
+ allow_any_instance_of(klass).to receive(:event)
end
it "delegates to #{klass} destination" do
@@ -118,8 +134,8 @@ RSpec.describe Gitlab::Tracking do
expect(Gitlab::Tracking::StandardContext)
.to receive(:new)
- .with(project: project, user: user, namespace: namespace, extra_key_1: 'extra value 1', extra_key_2: 'extra value 2')
- .and_call_original
+ .with(project: project, user: user, namespace: namespace, extra_key_1: 'extra value 1', extra_key_2: 'extra value 2')
+ .and_call_original
expect_any_instance_of(klass).to receive(:event) do |_, category, action, args|
expect(category).to eq('category')
@@ -132,7 +148,7 @@ RSpec.describe Gitlab::Tracking do
expect(args[:context].last).to eq(other_context)
end
- described_class.event('category', 'action',
+ described_class.method(method).call('category', 'action',
label: 'label',
property: 'property',
value: 1.5,
@@ -141,44 +157,95 @@ RSpec.describe Gitlab::Tracking do
user: user,
namespace: namespace,
extra_key_1: 'extra value 1',
- extra_key_2: 'extra value 2')
+ extra_key_2: 'extra value 2'
+ )
end
end
- context 'when the action is not passed in as a string' do
- it 'allows symbols' do
- expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+ describe '.database_event' do
+ context 'when the action is not passed in as a string' do
+ it 'allows symbols' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
- described_class.event('category', :some_action)
- end
+ described_class.database_event('category', :some_action)
+ end
+
+ it 'allows nil' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.database_event('category', nil)
+ end
- it 'allows nil' do
- expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+ it 'allows integers' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
- described_class.event('category', nil)
+ described_class.database_event('category', 1)
+ end
end
- it 'allows integers' do
- expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+ it_behaves_like 'rescued error raised by destination class' do
+ let(:category) { 'Issue' }
+ let(:action) { 'created' }
+ let(:destination_class) { Gitlab::Tracking::Destinations::DatabaseEventsSnowplow }
- described_class.event('category', 1)
+ subject(:tracking_method) { described_class.database_event(category, action) }
end
+
+ it_behaves_like 'delegates to destination', Gitlab::Tracking::Destinations::DatabaseEventsSnowplow, :database_event
end
- context 'when destination is Snowplow' do
- before do
- allow(Rails.env).to receive(:development?).and_return(true)
+ describe '.event' do
+ context 'when the action is not passed in as a string' do
+ it 'allows symbols' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', :some_action)
+ end
+
+ it 'allows nil' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', nil)
+ end
+
+ it 'allows integers' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', 1)
+ end
end
- it_behaves_like 'delegates to destination', Gitlab::Tracking::Destinations::Snowplow
- end
+ context 'when destination is Snowplow' do
+ before do
+ allow(Rails.env).to receive(:development?).and_return(true)
+ end
- context 'when destination is SnowplowMicro' do
- before do
- allow(Rails.env).to receive(:development?).and_return(true)
+ it_behaves_like 'rescued error raised by destination class' do
+ let(:category) { 'category' }
+ let(:action) { 'action' }
+ let(:destination_class) { Gitlab::Tracking::Destinations::Snowplow }
+
+ subject(:tracking_method) { described_class.event(category, action) }
+ end
+
+ it_behaves_like 'delegates to destination', Gitlab::Tracking::Destinations::Snowplow, :event
end
- it_behaves_like 'delegates to destination', Gitlab::Tracking::Destinations::SnowplowMicro
+ context 'when destination is SnowplowMicro' do
+ before do
+ allow(Rails.env).to receive(:development?).and_return(true)
+ end
+
+ it_behaves_like 'rescued error raised by destination class' do
+ let(:category) { 'category' }
+ let(:action) { 'action' }
+ let(:destination_class) { Gitlab::Tracking::Destinations::Snowplow }
+
+ subject(:tracking_method) { described_class.event(category, action) }
+ end
+
+ it_behaves_like 'delegates to destination', Gitlab::Tracking::Destinations::SnowplowMicro, :event
+ end
end
end
diff --git a/spec/mailers/emails/service_desk_spec.rb b/spec/mailers/emails/service_desk_spec.rb
index 25afa8b48ce..b81c0ec8617 100644
--- a/spec/mailers/emails/service_desk_spec.rb
+++ b/spec/mailers/emails/service_desk_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'email_spec'
-RSpec.describe Emails::ServiceDesk do
+RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
include EmailSpec::Helpers
include EmailSpec::Matchers
include EmailHelpers
@@ -16,6 +16,9 @@ RSpec.describe Emails::ServiceDesk do
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:email) { 'someone@gitlab.com' }
let_it_be(:expected_unsubscribe_url) { unsubscribe_sent_notification_url('b7721fc7e8419911a8bea145236a0519') }
+ let_it_be(:credential) { create(:service_desk_custom_email_credential, project: project) }
+ let_it_be(:verification) { create(:service_desk_custom_email_verification, project: project) }
+ let_it_be(:service_desk_setting) { create(:service_desk_setting, project: project, custom_email: 'user@example.com') }
let(:template) { double(content: template_content) }
@@ -81,6 +84,24 @@ RSpec.describe Emails::ServiceDesk do
end
end
+ shared_examples 'a custom email verification process email' do
+ it 'contains custom email and project in subject' do
+ expect(subject.subject).to include(service_desk_setting.custom_email)
+ expect(subject.subject).to include(service_desk_setting.project.name)
+ end
+ end
+
+ shared_examples 'a custom email verification process notification email' do
+ it 'has correct recipient' do
+ expect(subject.to).to eq(['owner@example.com'])
+ end
+
+ it 'contains custom email and project in body' do
+ is_expected.to have_body_text(service_desk_setting.custom_email)
+ is_expected.to have_body_text(service_desk_setting.project.name)
+ end
+ end
+
describe '.service_desk_thank_you_email' do
let_it_be(:reply_in_subject) { true }
let_it_be(:default_text) do
@@ -305,4 +326,20 @@ RSpec.describe Emails::ServiceDesk do
end
end
end
+
+ describe '.service_desk_verification_triggered_email' do
+ before do
+ service_desk_setting.custom_email_verification.triggerer = user
+ end
+
+ subject { Notify.service_desk_verification_triggered_email(service_desk_setting, 'owner@example.com') }
+
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'a custom email verification process email'
+ it_behaves_like 'a custom email verification process notification email'
+
+ it 'contains triggerer username' do
+ is_expected.to have_body_text("@#{user.username}")
+ end
+ end
end
diff --git a/spec/models/analytics/cycle_analytics/stage_spec.rb b/spec/models/analytics/cycle_analytics/stage_spec.rb
index 57748f8942e..44c0be68fe3 100644
--- a/spec/models/analytics/cycle_analytics/stage_spec.rb
+++ b/spec/models/analytics/cycle_analytics/stage_spec.rb
@@ -105,30 +105,36 @@ RSpec.describe Analytics::CycleAnalytics::Stage, feature_category: :value_stream
}
end
- describe '#create' do
- it_behaves_like 'Snowplow event tracking' do
- let(:property) { 'create' }
- let(:extra) { record_tracked_attributes }
+ context 'with database event tracking' do
+ before do
+ allow(Gitlab::Tracking).to receive(:database_event).and_call_original
+ end
+
+ describe '#create' do
+ it_behaves_like 'Snowplow event tracking', overrides: { tracking_method: :database_event } do
+ let(:property) { 'create' }
+ let(:extra) { record_tracked_attributes }
- subject(:new_group_stage) { stage }
+ subject(:new_group_stage) { stage }
+ end
end
- end
- describe '#update', :freeze_time do
- it_behaves_like 'Snowplow event tracking' do
- subject(:create_group_stage) { stage.update!(name: 'st 2') }
+ describe '#update', :freeze_time do
+ it_behaves_like 'Snowplow event tracking', overrides: { tracking_method: :database_event } do
+ subject(:create_group_stage) { stage.update!(name: 'st 2') }
- let(:extra) { record_tracked_attributes.merge('name' => 'st 2') }
- let(:property) { 'update' }
+ let(:extra) { record_tracked_attributes.merge('name' => 'st 2') }
+ let(:property) { 'update' }
+ end
end
- end
- describe '#destroy' do
- it_behaves_like 'Snowplow event tracking' do
- subject(:delete_stage_group) { stage.destroy! }
+ describe '#destroy' do
+ it_behaves_like 'Snowplow event tracking', overrides: { tracking_method: :database_event } do
+ subject(:delete_stage_group) { stage.destroy! }
- let(:extra) { record_tracked_attributes }
- let(:property) { 'destroy' }
+ let(:extra) { record_tracked_attributes }
+ let(:property) { 'destroy' }
+ end
end
end
end
diff --git a/spec/models/concerns/database_event_tracking_spec.rb b/spec/models/concerns/database_event_tracking_spec.rb
index 87aa8275635..cad82f971b3 100644
--- a/spec/models/concerns/database_event_tracking_spec.rb
+++ b/spec/models/concerns/database_event_tracking_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe DatabaseEventTracking, :snowplow do
+ before do
+ allow(Gitlab::Tracking).to receive(:database_event).and_call_original
+ end
+
let(:test_class) do
Class.new(ActiveRecord::Base) do
include DatabaseEventTracking
@@ -17,7 +21,7 @@ RSpec.describe DatabaseEventTracking, :snowplow do
context 'if event emmiter failed' do
before do
- allow(Gitlab::Tracking).to receive(:event).and_raise(StandardError) # rubocop:disable RSpec/ExpectGitlabTracking
+ allow(Gitlab::Tracking).to receive(:database_event).and_raise(StandardError) # rubocop:disable RSpec/ExpectGitlabTracking
end
it 'tracks the exception' do
@@ -35,7 +39,7 @@ RSpec.describe DatabaseEventTracking, :snowplow do
it 'does not track the event' do
create_test_class_record
- expect_no_snowplow_event
+ expect_no_snowplow_event(tracking_method: :database_event)
end
end
@@ -47,6 +51,7 @@ RSpec.describe DatabaseEventTracking, :snowplow do
create_test_class_record
expect_snowplow_event(
+ tracking_method: :database_event,
category: category,
action: "#{event}_create",
label: 'application_setting_terms',
@@ -61,6 +66,7 @@ RSpec.describe DatabaseEventTracking, :snowplow do
test_class.first.update!(id: 3)
expect_snowplow_event(
+ tracking_method: :database_event,
category: category,
action: "#{event}_update",
label: 'application_setting_terms',
@@ -75,6 +81,7 @@ RSpec.describe DatabaseEventTracking, :snowplow do
test_class.first.destroy!
expect_snowplow_event(
+ tracking_method: :database_event,
category: category,
action: "#{event}_destroy",
label: 'application_setting_terms',
diff --git a/spec/models/u2f_registration_spec.rb b/spec/models/u2f_registration_spec.rb
index 1fab3882c2a..9c8d786ecb1 100644
--- a/spec/models/u2f_registration_spec.rb
+++ b/spec/models/u2f_registration_spec.rb
@@ -62,72 +62,6 @@ RSpec.describe U2fRegistration do
end
end
- describe 'callbacks' do
- describe 'after create' do
- shared_examples_for 'creates webauthn registration' do
- it 'creates webauthn registration' do
- u2f_registration = create_u2f_registration
- webauthn_registration = WebauthnRegistration.where(u2f_registration_id: u2f_registration.id)
- expect(webauthn_registration).to exist
- end
- end
-
- it_behaves_like 'creates webauthn registration'
-
- context 'when the u2f_registration has a blank name' do
- let(:u2f_registration_name) { '' }
-
- it_behaves_like 'creates webauthn registration'
- end
-
- context 'when the u2f_registration has the name as `nil`' do
- let(:u2f_registration_name) { nil }
-
- it_behaves_like 'creates webauthn registration'
- end
-
- it 'logs error' do
- allow(Gitlab::Auth::U2fWebauthnConverter).to receive(:new).and_raise('boom!')
-
- allow_next_instance_of(U2fRegistration) do |u2f_registration|
- allow(u2f_registration).to receive(:id).and_return(123)
- end
-
- expect(Gitlab::ErrorTracking).to(
- receive(:track_exception).with(kind_of(StandardError),
- u2f_registration_id: 123))
-
- create_u2f_registration
- end
- end
-
- describe 'after update' do
- context 'when counter is updated' do
- it 'updates the webauthn registration counter to be the same value' do
- u2f_registration = create_u2f_registration
- new_counter = u2f_registration.counter + 1
- webauthn_registration = WebauthnRegistration.find_by(u2f_registration_id: u2f_registration.id)
-
- u2f_registration.update!(counter: new_counter)
-
- expect(u2f_registration.reload.counter).to eq(new_counter)
- expect(webauthn_registration.reload.counter).to eq(new_counter)
- end
- end
-
- context 'when sign count of registration is not updated' do
- it 'does not update the counter' do
- u2f_registration = create_u2f_registration
- webauthn_registration = WebauthnRegistration.find_by(u2f_registration_id: u2f_registration.id)
-
- expect do
- u2f_registration.update!(name: 'a new name')
- end.not_to change { webauthn_registration.counter }
- end
- end
- end
- end
-
def create_u2f_registration
create(
:u2f_registration,
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index c738269fce0..c4e0d5ed27b 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -226,15 +226,7 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
context 'with access token' do
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'push_package'
- it 'creates npm package with file' do
- expect { subject }
- .to change { project.packages.count }.by(1)
- .and change { Packages::PackageFile.count }.by(1)
- .and change { Packages::Tag.count }.by(1)
- .and change { Packages::Npm::Metadatum.count }.by(1)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
+ it_behaves_like 'a successful package creation'
end
it 'creates npm package with file with job token' do
@@ -391,20 +383,35 @@ RSpec.describe API::NpmProjectPackages, feature_category: :package_registry do
context 'with a too large metadata structure' do
let(:package_name) { "@#{group.path}/my_package_name" }
- let(:params) do
- upload_params(package_name: package_name, package_version: '1.2.3').tap do |h|
- h['versions']['1.2.3']['test'] = 'test' * 10000
+
+ ::Packages::Npm::CreatePackageService::PACKAGE_JSON_NOT_ALLOWED_FIELDS.each do |field|
+ context "when a large value for #{field} is set" do
+ let(:params) do
+ upload_params(package_name: package_name, package_version: '1.2.3').tap do |h|
+ h['versions']['1.2.3'][field] = 'test' * 10000
+ end
+ end
+
+ it_behaves_like 'a successful package creation'
end
end
- it_behaves_like 'not a package tracking event'
+ context 'when the large field is not one of the ignored fields' do
+ let(:params) do
+ upload_params(package_name: package_name, package_version: '1.2.3').tap do |h|
+ h['versions']['1.2.3']['test'] = 'test' * 10000
+ end
+ end
- it 'returns an error' do
- expect { upload_package_with_token }
- .not_to change { project.packages.count }
+ it_behaves_like 'not a package tracking event'
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(response.body).to include('Validation failed: Package json structure is too large')
+ it 'returns an error' do
+ expect { upload_package_with_token }
+ .not_to change { project.packages.count }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(response.body).to include('Validation failed: Package json structure is too large')
+ end
end
end
end
diff --git a/spec/scripts/create_pipeline_failure_incident_spec.rb b/spec/scripts/create_pipeline_failure_incident_spec.rb
deleted file mode 100644
index efbd22ccb32..00000000000
--- a/spec/scripts/create_pipeline_failure_incident_spec.rb
+++ /dev/null
@@ -1,162 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-require_relative '../../scripts/create-pipeline-failure-incident'
-require_relative '../support/helpers/stub_env'
-
-RSpec.describe CreatePipelineFailureIncident, feature_category: :tooling do
- include StubENV
-
- describe '#execute' do
- let(:create_issue) { instance_double(CreateIssue) }
- let(:issue) { double('Issue', iid: 1) } # rubocop:disable RSpec/VerifiedDoubles
- let(:create_issue_discussion) { instance_double(CreateIssueDiscussion, execute: true) }
- let(:failed_jobs) { instance_double(PipelineFailedJobs, execute: []) }
-
- let(:options) do
- {
- project: 'gitlab-org/gitlab-test-project',
- api_token: 'asdf1234'
- }
- end
-
- let(:issue_params) do
- {
- issue_type: 'incident',
- title: title,
- description: description,
- labels: incident_labels
- }
- end
-
- subject { described_class.new(options).execute }
-
- before do
- stub_env(
- 'CI_COMMIT_SHA' => 'bfcd2b9b5cad0b889494ce830697392c8ca11257',
- 'CI_PROJECT_PATH' => 'gitlab.com/gitlab-org/gitlab',
- 'CI_PROJECT_NAME' => 'gitlab',
- 'GITLAB_USER_ID' => '1111',
- 'CI_PROJECT_ID' => '13083',
- 'CI_PIPELINE_ID' => '1234567',
- 'CI_PIPELINE_URL' => 'https://gitlab.com/gitlab-org/gitlab/-/pipelines/1234567',
- 'CI_PROJECT_URL' => 'https://gitlab.com/gitlab-org/gitlab',
- 'CI_PIPELINE_CREATED_AT' => '2023-01-24 00:00:00',
- 'CI_COMMIT_TITLE' => 'Commit title',
- 'CI_PIPELINE_SOURCE' => 'push',
- 'GITLAB_USER_NAME' => 'Foo User',
- 'PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE' => 'asdf1234',
- 'CI_SERVER_URL' => 'https://gitlab.com',
- 'GITLAB_USER_LOGIN' => 'foo'
- )
- end
-
- shared_examples 'creating an issue' do
- it 'successfully creates an issue' do
- allow(PipelineFailedJobs).to receive(:new)
- .with(API::DEFAULT_OPTIONS.merge(exclude_allowed_to_fail_jobs: true))
- .and_return(failed_jobs)
-
- expect(CreateIssue).to receive(:new)
- .with(project: options[:project], api_token: options[:api_token])
- .and_return(create_issue)
-
- expect(CreateIssueDiscussion).to receive(:new)
- .with(project: options[:project], api_token: options[:api_token])
- .and_return(create_issue_discussion).twice
-
- expect(create_issue).to receive(:execute)
- .with(issue_params).and_return(issue)
-
- expect(subject).to eq(issue)
- end
- end
-
- context 'when stable branch' do
- let(:incident_labels) { ['release-blocker'] }
- let(:title) { /broken `15-6-stable-ee`/ }
- let(:description) { /A broken stable branch prevents patch releases/ }
-
- let(:commit_merge_request) do
- {
- 'author' => {
- 'id' => '2'
- },
- 'title' => 'foo',
- 'web_url' => 'https://gitlab.com/test'
- }
- end
-
- let(:merge_request) { instance_double(CommitMergeRequests, execute: [commit_merge_request]) }
- let(:issue_params) { super().merge(assignee_ids: [1111, 2]) }
-
- before do
- stub_env(
- 'CI_COMMIT_REF_NAME' => '15-6-stable-ee'
- )
-
- allow(CommitMergeRequests).to receive(:new)
- .with(API::DEFAULT_OPTIONS.merge(sha: ENV['CI_COMMIT_SHA']))
- .and_return(merge_request)
- end
-
- it_behaves_like 'creating an issue'
- end
-
- context 'when other branch' do
- let(:title) { /broken `master`/ }
- let(:description) { /Follow the \[Broken `master` handbook guide\]/ }
-
- before do
- stub_env(
- 'CI_COMMIT_REF_NAME' => 'master'
- )
- end
-
- context 'when GitLab FOSS' do
- let(:incident_labels) { ['master:foss-broken', 'Engineering Productivity', 'master-broken::undetermined'] }
-
- before do
- stub_env(
- 'CI_PROJECT_NAME' => 'gitlab-foss'
- )
- end
-
- it_behaves_like 'creating an issue'
- end
-
- context 'when GitLab EE' do
- let(:incident_labels) { ['master:broken', 'Engineering Productivity', 'master-broken::undetermined'] }
-
- before do
- stub_env(
- 'CI_PROJECT_NAME' => 'gitlab'
- )
- end
-
- it_behaves_like 'creating an issue'
- end
- end
-
- context 'when review-apps' do
- let(:options) do
- {
- project: 'gitlab-org/quality/engineering-productivity/review-apps-broken-incidents',
- api_token: 'asdf1234'
- }
- end
-
- let(:incident_labels) { ["review-apps-broken", "Engineering Productivity", "ep::review-apps"] }
- let(:title) { /broken `my-branch`/ }
- let(:description) { /Please refer to \[the review-apps triaging process\]/ }
-
- before do
- stub_env(
- 'CI_COMMIT_REF_NAME' => 'my-branch'
- )
- end
-
- it_behaves_like 'creating an issue'
- end
- end
-end
diff --git a/spec/scripts/generate_failed_pipeline_slack_message_spec.rb b/spec/scripts/generate_failed_pipeline_slack_message_spec.rb
deleted file mode 100644
index 2418116e694..00000000000
--- a/spec/scripts/generate_failed_pipeline_slack_message_spec.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-require_relative '../../scripts/generate-failed-pipeline-slack-message'
-require_relative '../support/helpers/stub_env'
-
-RSpec.describe GenerateFailedPipelineSlackMessage, feature_category: :tooling do
- include StubENV
-
- describe '#execute' do
- let(:create_issue) { instance_double(CreateIssue) }
- let(:issue) { double('Issue', iid: 1) } # rubocop:disable RSpec/VerifiedDoubles
- let(:create_issue_discussion) { instance_double(CreateIssueDiscussion, execute: true) }
- let(:failed_jobs) { instance_double(PipelineFailedJobs, execute: []) }
-
- let(:project_path) { 'gitlab-org/gitlab-test-project' }
- let(:options) do
- {
- project: project_path,
- incident_json_file: 'incident_json_file_tests.json'
- }
- end
-
- subject { described_class.new(options).execute }
-
- before do
- stub_env(
- 'CI_COMMIT_REF_NAME' => 'my-branch',
- 'CI_COMMIT_SHA' => 'bfcd2b9b5cad0b889494ce830697392c8ca11257',
- 'CI_COMMIT_TITLE' => 'Commit title',
- 'CI_PIPELINE_CREATED_AT' => '2023-01-24 00:00:00',
- 'CI_PIPELINE_ID' => '1234567',
- 'CI_PIPELINE_SOURCE' => 'push',
- 'CI_PIPELINE_URL' => 'https://gitlab.com/gitlab-org/gitlab/-/pipelines/1234567',
- 'CI_PROJECT_PATH' => 'gitlab.com/gitlab-org/gitlab',
- 'CI_PROJECT_URL' => 'https://gitlab.com/gitlab-org/gitlab',
- 'CI_SERVER_URL' => 'https://gitlab.com',
- 'GITLAB_USER_ID' => '1111',
- 'GITLAB_USER_LOGIN' => 'foo',
- 'GITLAB_USER_NAME' => 'Foo User',
- 'PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE' => 'asdf1234',
- 'SLACK_CHANNEL' => '#a-slack-channel'
- )
-
- allow(PipelineFailedJobs).to receive(:new)
- .with(API::DEFAULT_OPTIONS.merge(exclude_allowed_to_fail_jobs: true))
- .and_return(failed_jobs)
- end
-
- it 'returns the correct keys' do
- expect(subject.keys).to match_array([:channel, :username, :icon_emoji, :text, :blocks])
- end
-
- it 'returns the correct channel' do
- expect(subject[:channel]).to eq('#a-slack-channel')
- end
-
- it 'returns the correct username' do
- expect(subject[:username]).to eq('Failed pipeline reporter')
- end
-
- it 'returns the correct icon_emoji' do
- expect(subject[:icon_emoji]).to eq(':boom:')
- end
-
- it 'returns the correct text' do
- expect(subject[:text]).to eq(
- '*<https://gitlab.com/gitlab-org/gitlab|gitlab.com/gitlab-org/gitlab> pipeline ' \
- '<https://gitlab.com/gitlab-org/gitlab/-/pipelines/1234567|#1234567> failed*'
- )
- end
-
- it 'returns the correct incident button link' do
- block_with_incident_link = subject[:blocks].detect { |block| block.key?(:accessory) }
-
- expect(block_with_incident_link[:accessory][:url]).to eq(
- "https://gitlab.com/#{project_path}/-/issues/new?issuable_template=incident&issue%5Bissue_type%5D=incident"
- )
- end
- end
-end
diff --git a/spec/support/helpers/snowplow_helpers.rb b/spec/support/helpers/snowplow_helpers.rb
index 265e1c38b09..a04e5d46df9 100644
--- a/spec/support/helpers/snowplow_helpers.rb
+++ b/spec/support/helpers/snowplow_helpers.rb
@@ -46,7 +46,7 @@ module SnowplowHelpers
# }
# ]
# )
- def expect_snowplow_event(category:, action:, context: nil, **kwargs)
+ def expect_snowplow_event(category:, action:, context: nil, tracking_method: :event, **kwargs)
if context
if context.is_a?(Array)
kwargs[:context] = []
@@ -60,7 +60,7 @@ module SnowplowHelpers
end
end
- expect(Gitlab::Tracking).to have_received(:event) # rubocop:disable RSpec/ExpectGitlabTracking
+ expect(Gitlab::Tracking).to have_received(tracking_method) # rubocop:disable RSpec/ExpectGitlabTracking
.with(category, action, **kwargs).at_least(:once)
end
@@ -79,11 +79,11 @@ module SnowplowHelpers
# expect_no_snowplow_event
# end
# end
- def expect_no_snowplow_event(category: nil, action: nil, **kwargs)
+ def expect_no_snowplow_event(category: nil, action: nil, tracking_method: :event, **kwargs)
if category && action
- expect(Gitlab::Tracking).not_to have_received(:event).with(category, action, **kwargs) # rubocop:disable RSpec/ExpectGitlabTracking
+ expect(Gitlab::Tracking).not_to have_received(tracking_method).with(category, action, **kwargs) # rubocop:disable RSpec/ExpectGitlabTracking
else
- expect(Gitlab::Tracking).not_to have_received(:event) # rubocop:disable RSpec/ExpectGitlabTracking
+ expect(Gitlab::Tracking).not_to have_received(tracking_method) # rubocop:disable RSpec/ExpectGitlabTracking
end
end
end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 5c7d31190fd..2058eeef442 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -5728,7 +5728,6 @@
- './spec/lib/gitlab/background_migration/migrate_personal_namespace_project_maintainer_to_owner_spec.rb'
- './spec/lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics_spec.rb'
- './spec/lib/gitlab/background_migration/migrate_shimo_confluence_integration_category_spec.rb'
-- './spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb'
- './spec/lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature_spec.rb'
- './spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb'
- './spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb'
diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
index b7f2805ed97..1f2450c864b 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -175,3 +175,15 @@ RSpec.shared_examples 'bumping the package last downloaded at field' do
.to change { package.reload.last_downloaded_at }.from(nil).to(instance_of(ActiveSupport::TimeWithZone))
end
end
+
+RSpec.shared_examples 'a successful package creation' do
+ it 'creates npm package with file' do
+ expect { subject }
+ .to change { project.packages.count }.by(1)
+ .and change { Packages::PackageFile.count }.by(1)
+ .and change { Packages::Tag.count }.by(1)
+ .and change { Packages::Npm::Metadatum.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+end
diff --git a/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/agent.rb b/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/agent.rb
index d4231d12f72..b7400a0081c 100644
--- a/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/agent.rb
+++ b/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/agent.rb
@@ -9,8 +9,7 @@ module CloudProfilerAgent
GoogleCloudProfiler = ::Google::Cloud::Profiler::V2
PROFILE_TYPES = {
- CPU: :cpu,
- WALL: :wall
+ CPU: :cpu
}.freeze
# This regexp will ensure the service name is valid.
# See https://cloud.google.com/ruby/docs/reference/google-cloud-profiler-v2/latest/Google-Cloud-Profiler-V2-Deployment#Google__Cloud__Profiler__V2__Deployment_target_instance_
diff --git a/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/looper.rb b/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/looper.rb
index 9f0a5ef2abd..de5fe3fe522 100644
--- a/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/looper.rb
+++ b/vendor/gems/cloud_profiler_agent/lib/cloud_profiler_agent/looper.rb
@@ -68,6 +68,14 @@ module CloudProfilerAgent
duration_s: elapsed,
**log_labels
)
+ rescue Exception => e # rubocop:disable Lint/RescueException
+ # We rescue exception here to make sure we log the error message, then we re-raise the exception.
+ logger.error(
+ gcp_ruby_status: "exception",
+ error: e.inspect,
+ **log_labels
+ )
+ raise e
else
iteration_time = @min_iteration_sec
end
diff --git a/vendor/gems/cloud_profiler_agent/spec/cloud_profiler_agent/looper_spec.rb b/vendor/gems/cloud_profiler_agent/spec/cloud_profiler_agent/looper_spec.rb
index 407d185c18f..6150fd8b1ea 100644
--- a/vendor/gems/cloud_profiler_agent/spec/cloud_profiler_agent/looper_spec.rb
+++ b/vendor/gems/cloud_profiler_agent/spec/cloud_profiler_agent/looper_spec.rb
@@ -79,6 +79,25 @@ RSpec.describe CloudProfilerAgent::Looper, feature_category: :application_perfor
end
end
+ context 'when the block raises an Exception' do
+ let(:exception_subject) do
+ subject.run do
+ raise Exception, 'bam'
+ end
+ end
+
+ it 'logs the error and re-raises the exception' do
+ expect_any_instance_of(Logger).to receive(:error).with(
+ hash_including(
+ gcp_ruby_status: "exception",
+ error: "#<Exception: bam>"
+ )
+ )
+
+ expect { exception_subject }.to raise_exception
+ end
+ end
+
context 'when Google asks for backoff' do
it 'slows down' do
subject.run(2) do