From b81fd57f3d62db4455108c8de4b8d7b8d403de35 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 22 Apr 2021 09:09:45 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop_manual_todo.yml | 3 - GITALY_SERVER_VERSION | 2 +- app/controllers/groups_controller.rb | 2 +- app/controllers/invites_controller.rb | 13 +- app/controllers/projects/blame_controller.rb | 2 +- app/controllers/projects/blob_controller.rb | 2 +- app/controllers/projects/commit_controller.rb | 2 +- .../projects/compare_controller.rb | 2 +- app/controllers/projects/logs_controller.rb | 2 +- app/controllers/projects_controller.rb | 2 +- .../registrations/welcome_controller.rb | 21 ++- app/finders/environment_names_finder.rb | 57 -------- .../environments/environment_names_finder.rb | 59 ++++++++ .../environments_by_deployments_finder.rb | 69 +++++++++ .../environments/environments_finder.rb | 65 +++++++++ .../environments_by_deployments_finder.rb | 67 --------- app/finders/environments_finder.rb | 63 -------- .../resolvers/environments_resolver.rb | 4 +- app/helpers/invite_members_helper.rb | 11 ++ app/models/broadcast_message.rb | 8 + app/models/merge_request.rb | 4 +- app/models/service.rb | 1 - app/services/ci/stop_environments_service.rb | 2 +- app/services/lfs/push_service.rb | 5 + .../create_default_alerts_service.rb | 2 +- app/workers/build_finished_worker.rb | 1 - ...ult-invited-users-to-the-activity-page.yml | 5 + .../326209-remove-unnecessary-validation.yml | 5 + .../deprecate-alerts-service-metric.yml | 5 + .../improved-banner-path-matching.yml | 5 + .../jreporter-master-patch-75024.yml | 5 + .../unreleased/sh-log-lfs-push-failures.yml | 5 + .../new_graphql_keyset_pagination.yml | 8 + ...216175305_clusters_applications_runner.yml | 3 +- ...6_projects_with_alerts_service_enabled.yml | 2 +- ...216181949_clusters_applications_runner.yml | 13 +- ...04124902_gitlab_shared_runners_enabled.yml | 14 +- doc/development/usage_ping/dictionary.md | 10 +- doc/user/admin_area/credentials_inventory.md | 28 +--- .../security_dashboard/index.md | 27 +++- lib/api/environments.rb | 2 +- lib/gitlab/alert_management/payload/base.rb | 2 +- .../keyset/generic_keyset_pagination.rb | 13 ++ lib/gitlab/pagination/keyset/order.rb | 2 + .../pagination/keyset/simple_order_builder.rb | 137 ++++++++++++++++++ lib/gitlab/usage_data.rb | 2 +- locale/gitlab.pot | 38 ++++- scripts/rspec_helpers.sh | 5 +- spec/factories/usage_data.rb | 2 - spec/features/invites_spec.rb | 27 ++-- .../environment_names_finder_spec.rb | 2 +- ...environments_by_deployments_finder_spec.rb | 2 +- .../environments_finder_spec.rb | 2 +- .../pagination/keyset/connection_spec.rb | 5 +- .../keyset/simple_order_builder_spec.rb | 91 ++++++++++++ spec/lib/gitlab/usage_data_spec.rb | 2 +- spec/models/broadcast_message_spec.rb | 6 + spec/requests/api/graphql_spec.rb | 55 +++++-- spec/services/lfs/push_service_spec.rb | 2 + spec/services/post_receive_service_spec.rb | 2 +- spec/services/projects/create_service_spec.rb | 21 --- spec/workers/build_finished_worker_spec.rb | 1 - 62 files changed, 695 insertions(+), 332 deletions(-) delete mode 100644 app/finders/environment_names_finder.rb create mode 100644 app/finders/environments/environment_names_finder.rb create mode 100644 app/finders/environments/environments_by_deployments_finder.rb create mode 100644 app/finders/environments/environments_finder.rb delete mode 100644 app/finders/environments_by_deployments_finder.rb delete mode 100644 app/finders/environments_finder.rb create mode 100644 changelogs/unreleased/232885-experiment-default-invited-users-to-the-activity-page.yml create mode 100644 changelogs/unreleased/326209-remove-unnecessary-validation.yml create mode 100644 changelogs/unreleased/deprecate-alerts-service-metric.yml create mode 100644 changelogs/unreleased/improved-banner-path-matching.yml create mode 100644 changelogs/unreleased/jreporter-master-patch-75024.yml create mode 100644 changelogs/unreleased/sh-log-lfs-push-failures.yml create mode 100644 config/feature_flags/development/new_graphql_keyset_pagination.yml create mode 100644 lib/gitlab/pagination/keyset/simple_order_builder.rb rename spec/finders/{ => environments}/environment_names_finder_spec.rb (98%) rename spec/finders/{ => environments}/environments_by_deployments_finder_spec.rb (98%) rename spec/finders/{ => environments}/environments_finder_spec.rb (97%) create mode 100644 spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 8aa944ba30b..0cae247234f 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -1546,9 +1546,6 @@ Gitlab/NamespacedClass: - 'app/finders/context_commits_finder.rb' - 'app/finders/contributed_projects_finder.rb' - 'app/finders/deployments_finder.rb' - - 'app/finders/environment_names_finder.rb' - - 'app/finders/environments_finder.rb' - - 'app/finders/environments_by_deployments_finder.rb' - 'app/finders/events_finder.rb' - 'app/finders/feature_flags_finder.rb' - 'app/finders/feature_flags_user_lists_finder.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index eca95040562..781f69d811b 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -d0d2f4a763c2be059e4e2353f2e5affaff83305d +a7bc2f86b507daaaf9f18e0ea189b062d6149720 diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 63f138aa462..3fef5b9b8c5 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -197,7 +197,7 @@ class GroupsController < Groups::ApplicationController def unfoldered_environment_names respond_to do |format| format.json do - render json: EnvironmentNamesFinder.new(@group, current_user).execute + render json: Environments::EnvironmentNamesFinder.new(@group, current_user).execute end end end diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 0eb08d2d0ad..a347b72e6a7 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -26,8 +26,7 @@ class InvitesController < ApplicationController experiment('members/invite_email', actor: member).track(:accepted) if initial_invite_email? session.delete(:invite_type) - redirect_to invite_details[:path], notice: _("You have been granted %{member_human_access} access to %{title} %{name}.") % - { member_human_access: member.human_access, title: invite_details[:title], name: invite_details[:name] } + redirect_to invite_details[:path], notice: helpers.invite_accepted_notice(member) else redirect_back_or_default(options: { alert: _("The invitation could not be accepted.") }) end @@ -91,7 +90,7 @@ class InvitesController < ApplicationController def authenticate_user! return if current_user - store_location_for :user, request.fullpath + store_location_for(:user, invite_landing_url) if member if user_sign_up? redirect_to new_user_registration_path(invite_email: member.invite_email), notice: _("To accept this invitation, create an account or sign in.") @@ -116,6 +115,10 @@ class InvitesController < ApplicationController end end + def invite_landing_url + root_url + invite_details[:path] + end + def invite_details @invite_details ||= case member.source when Project @@ -123,14 +126,14 @@ class InvitesController < ApplicationController name: member.source.full_name, url: project_url(member.source), title: _("project"), - path: project_path(member.source) + path: activity_project_path(member.source) } when Group { name: member.source.name, url: group_url(member.source), title: _("group"), - path: group_path(member.source) + path: activity_group_path(member.source) } end end diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 2c7c49b4250..14fc2cd5a00 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -20,7 +20,7 @@ class Projects::BlameController < Projects::ApplicationController environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit } environment_params[:find_latest] = true - @environment = EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last + @environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last @blame = Gitlab::Blame.new(@blob, @commit) @blame = Gitlab::View::Presenter::Factory.new(@blame, project: @project, path: @path).fabricate! diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index a398fc56a35..b9b0c2b802c 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -214,7 +214,7 @@ class Projects::BlobController < Projects::ApplicationController def show_html environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit } environment_params[:find_latest] = true - @environment = EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last + @environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last @last_commit = @repository.last_commit_for_path(@commit.id, @blob.path, literal_pathspec: true) @code_navigation_path = Gitlab::CodeNavigationPath.new(@project, @blob.commit_id).full_json_path_for(@blob.path) diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 5a69898ce95..be1bd37e341 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -167,7 +167,7 @@ class Projects::CommitController < Projects::ApplicationController @diffs = commit.diffs(opts) @notes_count = commit.notes.count - @environment = EnvironmentsByDeploymentsFinder.new(@project, current_user, commit: @commit, find_latest: true).execute.last + @environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, commit: @commit, find_latest: true).execute.last end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index c716acd52bc..0db9940529a 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -136,7 +136,7 @@ class Projects::CompareController < Projects::ApplicationController if compare environment_params = source_project.repository.branch_exists?(head_ref) ? { ref: head_ref } : { commit: compare.commit } environment_params[:find_latest] = true - @environment = EnvironmentsByDeploymentsFinder.new(source_project, current_user, environment_params).execute.last + @environment = Environments::EnvironmentsByDeploymentsFinder.new(source_project, current_user, environment_params).execute.last end end diff --git a/app/controllers/projects/logs_controller.rb b/app/controllers/projects/logs_controller.rb index f9b8091a419..a4bdbc827e0 100644 --- a/app/controllers/projects/logs_controller.rb +++ b/app/controllers/projects/logs_controller.rb @@ -58,7 +58,7 @@ module Projects def environment strong_memoize(:environment) do if cluster_params.key?(:environment_name) - EnvironmentsFinder.new(project, current_user, name: cluster_params[:environment_name]).execute.first + ::Environments::EnvironmentsFinder.new(project, current_user, name: cluster_params[:environment_name]).execute.first else project.default_environment end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 7c9d6daad02..575b9642efe 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -311,7 +311,7 @@ class ProjectsController < Projects::ApplicationController def unfoldered_environment_names respond_to do |format| format.json do - render json: EnvironmentNamesFinder.new(@project, current_user).execute + render json: Environments::EnvironmentNamesFinder.new(@project, current_user).execute end end end diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb index 62ec03206c4..89fc1e4aa3c 100644 --- a/app/controllers/registrations/welcome_controller.rb +++ b/app/controllers/registrations/welcome_controller.rb @@ -18,7 +18,11 @@ module Registrations if result[:status] == :success return redirect_to new_users_sign_up_group_path if show_signup_onboarding? - redirect_to path_for_signed_in_user(current_user) + if current_user.members.count == 1 + redirect_to path_for_signed_in_user(current_user), notice: helpers.invite_accepted_notice(current_user.members.last) + else + redirect_to path_for_signed_in_user(current_user) + end else render :show end @@ -48,7 +52,20 @@ module Registrations def path_for_signed_in_user(user) return users_almost_there_path if requires_confirmation?(user) - stored_location_for(user) || dashboard_projects_path + stored_location_for(user) || members_activity_path(user) + end + + def members_activity_path(user) + return dashboard_projects_path unless user.members.count >= 1 + + case user.members.last.source + when Project + activity_project_path(user.members.last.source) + when Group + activity_group_path(user.members.last.source) + else + dashboard_projects_path + end end def show_signup_onboarding? diff --git a/app/finders/environment_names_finder.rb b/app/finders/environment_names_finder.rb deleted file mode 100644 index e9063ef4c90..00000000000 --- a/app/finders/environment_names_finder.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -# Finder for obtaining the unique environment names of a project or group. -# -# This finder exists so that the merge requests "environments" filter can be -# populated with a unique list of environment names. If we retrieve _just_ the -# environments, duplicates may be present (e.g. multiple projects in a group -# having a "staging" environment). -# -# In addition, this finder only produces unfoldered environments. We do this -# because when searching for environments we want to exclude review app -# environments. -class EnvironmentNamesFinder - attr_reader :project_or_group, :current_user - - def initialize(project_or_group, current_user = nil) - @project_or_group = project_or_group - @current_user = current_user - end - - def execute - all_environments.unfoldered.order_by_name.pluck_unique_names - end - - def all_environments - if project_or_group.is_a?(Namespace) - namespace_environments - else - project_environments - end - end - - def namespace_environments - # We assume reporter access is needed for the :read_environment permission - # here. This expection is also present in - # IssuableFinder::Params#min_access_level, which is used for filtering out - # merge requests that don't have the right permissions. - # - # We use this approach so we don't need to load every project into memory - # just to verify if we can see their environments. Doing so would not be - # efficient, and possibly mess up pagination if certain projects are not - # meant to be visible. - projects = project_or_group - .all_projects - .public_or_visible_to_user(current_user, Gitlab::Access::REPORTER) - - Environment.for_project(projects) - end - - def project_environments - if Ability.allowed?(current_user, :read_environment, project_or_group) - project_or_group.environments - else - Environment.none - end - end -end diff --git a/app/finders/environments/environment_names_finder.rb b/app/finders/environments/environment_names_finder.rb new file mode 100644 index 00000000000..d4928f0fc84 --- /dev/null +++ b/app/finders/environments/environment_names_finder.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Environments + # Finder for obtaining the unique environment names of a project or group. + # + # This finder exists so that the merge requests "environments" filter can be + # populated with a unique list of environment names. If we retrieve _just_ the + # environments, duplicates may be present (e.g. multiple projects in a group + # having a "staging" environment). + # + # In addition, this finder only produces unfoldered environments. We do this + # because when searching for environments we want to exclude review app + # environments. + class EnvironmentNamesFinder + attr_reader :project_or_group, :current_user + + def initialize(project_or_group, current_user = nil) + @project_or_group = project_or_group + @current_user = current_user + end + + def execute + all_environments.unfoldered.order_by_name.pluck_unique_names + end + + def all_environments + if project_or_group.is_a?(Namespace) + namespace_environments + else + project_environments + end + end + + def namespace_environments + # We assume reporter access is needed for the :read_environment permission + # here. This expection is also present in + # IssuableFinder::Params#min_access_level, which is used for filtering out + # merge requests that don't have the right permissions. + # + # We use this approach so we don't need to load every project into memory + # just to verify if we can see their environments. Doing so would not be + # efficient, and possibly mess up pagination if certain projects are not + # meant to be visible. + projects = project_or_group + .all_projects + .public_or_visible_to_user(current_user, Gitlab::Access::REPORTER) + + Environment.for_project(projects) + end + + def project_environments + if Ability.allowed?(current_user, :read_environment, project_or_group) + project_or_group.environments + else + Environment.none + end + end + end +end diff --git a/app/finders/environments/environments_by_deployments_finder.rb b/app/finders/environments/environments_by_deployments_finder.rb new file mode 100644 index 00000000000..e0ecc98b1c0 --- /dev/null +++ b/app/finders/environments/environments_by_deployments_finder.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Environments + class EnvironmentsByDeploymentsFinder + attr_reader :project, :current_user, :params + + def initialize(project, current_user, params = {}) + @project = project + @current_user = current_user + @params = params + end + + # rubocop: disable CodeReuse/ActiveRecord + def execute + deployments = project.deployments + deployments = + if ref + deployments_query = params[:with_tags] ? 'ref = :ref OR tag IS TRUE' : 'ref = :ref' + deployments.where(deployments_query, ref: ref.to_s) + elsif commit + deployments.where(sha: commit.sha) + else + deployments.none + end + + environment_ids = deployments + .group(:environment_id) + .select(:environment_id) + + environments = project.environments.available + .where(id: environment_ids) + + if params[:find_latest] + find_one(environments.order_by_last_deployed_at_desc) + else + find_all(environments.order_by_last_deployed_at.to_a) + end + end + # rubocop: enable CodeReuse/ActiveRecord + + private + + def find_one(environments) + [environments.find { |environment| valid_environment?(environment) }].compact + end + + def find_all(environments) + environments.select { |environment| valid_environment?(environment) } + end + + def valid_environment?(environment) + # Go in order of cost: SQL calls are cheaper than Gitaly calls + return false unless Ability.allowed?(current_user, :read_environment, environment) + + return false if ref && params[:recently_updated] && !environment.recently_updated_on_branch?(ref) + return false if ref && commit && !environment.includes_commit?(commit) + + true + end + + def ref + params[:ref].try(:to_s) + end + + def commit + params[:commit] + end + end +end diff --git a/app/finders/environments/environments_finder.rb b/app/finders/environments/environments_finder.rb new file mode 100644 index 00000000000..190cdb3dec3 --- /dev/null +++ b/app/finders/environments/environments_finder.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Environments + class EnvironmentsFinder + attr_reader :project, :current_user, :params + + InvalidStatesError = Class.new(StandardError) + + def initialize(project, current_user, params = {}) + @project = project + @current_user = current_user + @params = params + end + + def execute + environments = project.environments + environments = by_name(environments) + environments = by_search(environments) + + # Raises InvalidStatesError if params[:states] contains invalid states. + by_states(environments) + end + + private + + def by_name(environments) + if params[:name].present? + environments.for_name(params[:name]) + else + environments + end + end + + def by_search(environments) + if params[:search].present? + environments.for_name_like(params[:search], limit: nil) + else + environments + end + end + + def by_states(environments) + if params[:states].present? + environments_with_states(environments) + else + environments + end + end + + def environments_with_states(environments) + # Convert to array of strings + states = Array(params[:states]).map(&:to_s) + + raise InvalidStatesError, _('Requested states are invalid') unless valid_states?(states) + + environments.with_states(states) + end + + def valid_states?(states) + valid_states = Environment.valid_states.map(&:to_s) + + (states - valid_states).empty? + end + end +end diff --git a/app/finders/environments_by_deployments_finder.rb b/app/finders/environments_by_deployments_finder.rb deleted file mode 100644 index 76e1c050ea5..00000000000 --- a/app/finders/environments_by_deployments_finder.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -class EnvironmentsByDeploymentsFinder - attr_reader :project, :current_user, :params - - def initialize(project, current_user, params = {}) - @project = project - @current_user = current_user - @params = params - end - - # rubocop: disable CodeReuse/ActiveRecord - def execute - deployments = project.deployments - deployments = - if ref - deployments_query = params[:with_tags] ? 'ref = :ref OR tag IS TRUE' : 'ref = :ref' - deployments.where(deployments_query, ref: ref.to_s) - elsif commit - deployments.where(sha: commit.sha) - else - deployments.none - end - - environment_ids = deployments - .group(:environment_id) - .select(:environment_id) - - environments = project.environments.available - .where(id: environment_ids) - - if params[:find_latest] - find_one(environments.order_by_last_deployed_at_desc) - else - find_all(environments.order_by_last_deployed_at.to_a) - end - end - # rubocop: enable CodeReuse/ActiveRecord - - private - - def find_one(environments) - [environments.find { |environment| valid_environment?(environment) }].compact - end - - def find_all(environments) - environments.select { |environment| valid_environment?(environment) } - end - - def valid_environment?(environment) - # Go in order of cost: SQL calls are cheaper than Gitaly calls - return false unless Ability.allowed?(current_user, :read_environment, environment) - - return false if ref && params[:recently_updated] && !environment.recently_updated_on_branch?(ref) - return false if ref && commit && !environment.includes_commit?(commit) - - true - end - - def ref - params[:ref].try(:to_s) - end - - def commit - params[:commit] - end -end diff --git a/app/finders/environments_finder.rb b/app/finders/environments_finder.rb deleted file mode 100644 index c64e850f440..00000000000 --- a/app/finders/environments_finder.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -class EnvironmentsFinder - attr_reader :project, :current_user, :params - - InvalidStatesError = Class.new(StandardError) - - def initialize(project, current_user, params = {}) - @project = project - @current_user = current_user - @params = params - end - - def execute - environments = project.environments - environments = by_name(environments) - environments = by_search(environments) - - # Raises InvalidStatesError if params[:states] contains invalid states. - by_states(environments) - end - - private - - def by_name(environments) - if params[:name].present? - environments.for_name(params[:name]) - else - environments - end - end - - def by_search(environments) - if params[:search].present? - environments.for_name_like(params[:search], limit: nil) - else - environments - end - end - - def by_states(environments) - if params[:states].present? - environments_with_states(environments) - else - environments - end - end - - def environments_with_states(environments) - # Convert to array of strings - states = Array(params[:states]).map(&:to_s) - - raise InvalidStatesError, _('Requested states are invalid') unless valid_states?(states) - - environments.with_states(states) - end - - def valid_states?(states) - valid_states = Environment.valid_states.map(&:to_s) - - (states - valid_states).empty? - end -end diff --git a/app/graphql/resolvers/environments_resolver.rb b/app/graphql/resolvers/environments_resolver.rb index df04e70e250..ee604e7b307 100644 --- a/app/graphql/resolvers/environments_resolver.rb +++ b/app/graphql/resolvers/environments_resolver.rb @@ -21,8 +21,8 @@ module Resolvers def resolve(**args) return unless project.present? - EnvironmentsFinder.new(project, context[:current_user], args).execute - rescue EnvironmentsFinder::InvalidStatesError => exception + Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute + rescue Environments::EnvironmentsFinder::InvalidStatesError => exception raise Gitlab::Graphql::Errors::ArgumentError, exception.message end end diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb index 62d83ebe79e..5cf47f4a990 100644 --- a/app/helpers/invite_members_helper.rb +++ b/app/helpers/invite_members_helper.rb @@ -46,6 +46,17 @@ module InviteMembersHelper end end + def invite_accepted_notice(member) + case member.source + when Project + _("You have been granted %{member_human_access} access to project %{name}.") % + { member_human_access: member.human_access, name: member.source.name } + when Group + _("You have been granted %{member_human_access} access to group %{name}.") % + { member_human_access: member.human_access, name: member.source.name } + end + end + private def invite_members_url(form_model) diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index a8325e98095..e99362c502b 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -106,6 +106,14 @@ class BroadcastMessage < ApplicationRecord return false if current_path.blank? && target_path.present? return true if current_path.blank? || target_path.blank? + # Ensure paths are consistent across callers. + # This fixes a mismatch between requests in the GUI and CLI + # + # This has to be reassigned due to frozen strings being provided. + unless current_path.start_with?("/") + current_path = "/#{current_path}" + end + escaped = Regexp.escape(target_path).gsub('\\*', '.*') regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index e7f3762b9a3..5eb2bbe56fe 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1367,11 +1367,11 @@ class MergeRequest < ApplicationRecord def environments_for(current_user, latest: false) return [] unless diff_head_commit - envs = EnvironmentsByDeploymentsFinder.new(target_project, current_user, + envs = Environments::EnvironmentsByDeploymentsFinder.new(target_project, current_user, ref: target_branch, commit: diff_head_commit, with_tags: true, find_latest: latest).execute if source_project - envs.concat EnvironmentsByDeploymentsFinder.new(source_project, current_user, + envs.concat Environments::EnvironmentsByDeploymentsFinder.new(source_project, current_user, ref: source_branch, commit: diff_head_commit, find_latest: latest).execute end diff --git a/app/models/service.rb b/app/models/service.rb index aadc75ae710..9867081ce1b 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -241,7 +241,6 @@ class Service < ApplicationRecord service.project_id = project_id service.group_id = group_id service.inherit_from_id = integration.id if integration.instance? || integration.group - service.active = false if service.invalid? service end diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb index 81457130fa0..8c9ba849d2e 100644 --- a/app/services/ci/stop_environments_service.rb +++ b/app/services/ci/stop_environments_service.rb @@ -35,7 +35,7 @@ module Ci private def environments - @environments ||= EnvironmentsByDeploymentsFinder + @environments ||= Environments::EnvironmentsByDeploymentsFinder .new(project, current_user, ref: @ref, recently_updated: true) .execute end diff --git a/app/services/lfs/push_service.rb b/app/services/lfs/push_service.rb index 9b947fbed07..9e15e9ac64e 100644 --- a/app/services/lfs/push_service.rb +++ b/app/services/lfs/push_service.rb @@ -17,11 +17,16 @@ module Lfs success rescue => err + Gitlab::ErrorTracking.log_exception(err, extra_context) error(err.message) end private + def extra_context + { project_id: project.id, user_id: current_user&.id }.compact + end + # Currently we only set repository_type for design repository objects, so # push mirroring must send objects with a `nil` repository type - but if the # wiki repository uses LFS, its objects will also be sent. This will be diff --git a/app/services/prometheus/create_default_alerts_service.rb b/app/services/prometheus/create_default_alerts_service.rb index 4ae2743cc28..e59b0a8e8e3 100644 --- a/app/services/prometheus/create_default_alerts_service.rb +++ b/app/services/prometheus/create_default_alerts_service.rb @@ -84,7 +84,7 @@ module Prometheus def environment strong_memoize(:environment) do - EnvironmentsFinder.new(project, nil, name: 'production').execute.first || + Environments::EnvironmentsFinder.new(project, nil, name: 'production').execute.first || project.environments.first end end diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb index aeda8d113ac..b5071d93adc 100644 --- a/app/workers/build_finished_worker.rb +++ b/app/workers/build_finished_worker.rb @@ -34,7 +34,6 @@ class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker # We execute these async as these are independent operations. BuildHooksWorker.perform_async(build.id) - ExpirePipelineCacheWorker.perform_async(build.pipeline_id) ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat? if build.failed? diff --git a/changelogs/unreleased/232885-experiment-default-invited-users-to-the-activity-page.yml b/changelogs/unreleased/232885-experiment-default-invited-users-to-the-activity-page.yml new file mode 100644 index 00000000000..574d42a212e --- /dev/null +++ b/changelogs/unreleased/232885-experiment-default-invited-users-to-the-activity-page.yml @@ -0,0 +1,5 @@ +--- +title: Redirect to activity page when accepting invitation +merge_request: 56695 +author: +type: changed diff --git a/changelogs/unreleased/326209-remove-unnecessary-validation.yml b/changelogs/unreleased/326209-remove-unnecessary-validation.yml new file mode 100644 index 00000000000..74cf9f5403b --- /dev/null +++ b/changelogs/unreleased/326209-remove-unnecessary-validation.yml @@ -0,0 +1,5 @@ +--- +title: Remove unnecessary validation avoiding N+1 queries when building integrations +merge_request: 59635 +author: +type: performance diff --git a/changelogs/unreleased/deprecate-alerts-service-metric.yml b/changelogs/unreleased/deprecate-alerts-service-metric.yml new file mode 100644 index 00000000000..ae9a0993993 --- /dev/null +++ b/changelogs/unreleased/deprecate-alerts-service-metric.yml @@ -0,0 +1,5 @@ +--- +title: Deprecate Alerts service metric +merge_request: 59899 +author: +type: deprecated diff --git a/changelogs/unreleased/improved-banner-path-matching.yml b/changelogs/unreleased/improved-banner-path-matching.yml new file mode 100644 index 00000000000..201d7c1d6bf --- /dev/null +++ b/changelogs/unreleased/improved-banner-path-matching.yml @@ -0,0 +1,5 @@ +--- +title: Fix for shell announcement banners +merge_request: 59482 +author: +type: fixed diff --git a/changelogs/unreleased/jreporter-master-patch-75024.yml b/changelogs/unreleased/jreporter-master-patch-75024.yml new file mode 100644 index 00000000000..396108867c2 --- /dev/null +++ b/changelogs/unreleased/jreporter-master-patch-75024.yml @@ -0,0 +1,5 @@ +--- +title: Update Metrics Definitions for Runner +merge_request: 59824 +author: +type: other diff --git a/changelogs/unreleased/sh-log-lfs-push-failures.yml b/changelogs/unreleased/sh-log-lfs-push-failures.yml new file mode 100644 index 00000000000..27b6f2a477b --- /dev/null +++ b/changelogs/unreleased/sh-log-lfs-push-failures.yml @@ -0,0 +1,5 @@ +--- +title: Log exceptions in Lfs::PushService +merge_request: 59960 +author: +type: changed diff --git a/config/feature_flags/development/new_graphql_keyset_pagination.yml b/config/feature_flags/development/new_graphql_keyset_pagination.yml new file mode 100644 index 00000000000..f4afcba68cf --- /dev/null +++ b/config/feature_flags/development/new_graphql_keyset_pagination.yml @@ -0,0 +1,8 @@ +--- +name: new_graphql_keyset_pagination +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56751 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323730 +milestone: '13.10' +type: development +group: group::optimize +default_enabled: false diff --git a/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml b/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml index 3bab0578a71..a8ba5b65187 100644 --- a/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml +++ b/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml @@ -2,7 +2,7 @@ key_path: counts.clusters_applications_runner description: Total GitLab Managed clusters with GitLab Managed App:Runner installed product_section: ops -product_stage: +product_stage: configure product_group: group::configure product_category: kubernetes_management value_type: number @@ -16,4 +16,3 @@ tier: - free - premium - ultimate -skip_validation: true diff --git a/config/metrics/counts_all/20210216180456_projects_with_alerts_service_enabled.yml b/config/metrics/counts_all/20210216180456_projects_with_alerts_service_enabled.yml index 7f7c3b4d91a..d1ae17cdc47 100644 --- a/config/metrics/counts_all/20210216180456_projects_with_alerts_service_enabled.yml +++ b/config/metrics/counts_all/20210216180456_projects_with_alerts_service_enabled.yml @@ -6,7 +6,7 @@ product_stage: monitor product_group: group::health product_category: incident_management value_type: number -status: data_available +status: deprecated time_frame: all data_source: database distribution: diff --git a/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml b/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml index 3b36b410b3a..3e84a694e96 100644 --- a/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml +++ b/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml @@ -1,19 +1,18 @@ --- key_path: usage_activity_by_stage.verify.clusters_applications_runner -description: Total GitLab Managed clusters with Runner enabled +description: Count of users creating managed clusters with Runner enabled product_section: ops -product_stage: verify -product_group: group::runner -product_category: runner +product_stage: configure +product_group: group::configure +product_category: kubernetes_management value_type: number status: data_available time_frame: all -data_source: +data_source: database distribution: - ce - ee tier: - free - premium -- ultimate -skip_validation: true +- ultimate \ No newline at end of file diff --git a/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml b/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml index ed9491959ae..5ec4b9fdc09 100644 --- a/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml +++ b/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml @@ -1,16 +1,18 @@ --- key_path: gitlab_shared_runners_enabled description: Whether shared runners is enabled -product_section: growth -product_stage: growth -product_group: group::product intelligence -product_category: collection +product_section: ops +product_stage: verify +product_group: group::runner +product_category: runner value_type: boolean status: data_available time_frame: none -data_source: +data_source: database distribution: - ce +- ee tier: - free -skip_validation: true +- premium +- ultimate diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md index 5df9d859ee6..8f6b37105e3 100644 --- a/doc/development/usage_ping/dictionary.md +++ b/doc/development/usage_ping/dictionary.md @@ -4756,7 +4756,7 @@ Count of projects that have enabled the Alerts service Group: `group::health` -Status: `data_available` +Status: `deprecated` Tiers: `free`, `premium`, `ultimate` @@ -6506,11 +6506,11 @@ Whether shared runners is enabled [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml) -Group: `group::product intelligence` +Group: `group::runner` Status: `data_available` -Tiers: `free` +Tiers: `free`, `premium`, `ultimate` ### `gitpod_enabled` @@ -16224,11 +16224,11 @@ Tiers: `free` ### `usage_activity_by_stage.verify.clusters_applications_runner` -Total GitLab Managed clusters with Runner enabled +Count of users creating managed clusters with Runner enabled [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml) -Group: `group::runner` +Group: `group::configure` Status: `data_available` diff --git a/doc/user/admin_area/credentials_inventory.md b/doc/user/admin_area/credentials_inventory.md index 0ae6e41264c..dc64e9633f9 100644 --- a/doc/user/admin_area/credentials_inventory.md +++ b/doc/user/admin_area/credentials_inventory.md @@ -56,14 +56,7 @@ The instance then notifies the user. ## Review existing GPG keys > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/282429) in GitLab 13.10. -> - [Deployed behind a feature flag](../feature_flags.md), disabled by default. -> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/292961) in GitLab 13.11. -> - Enabled on GitLab.com. -> - Recommended for production use. -> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-the-gpg-keys-view). - -WARNING: -This feature might not be available to you. Check the **version history** note above for details. +> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/292961) in GitLab 13.12. You can view all existing GPG in your GitLab instance by navigating to the credentials inventory GPG Keys tab, as well as the following properties: @@ -73,22 +66,3 @@ credentials inventory GPG Keys tab, as well as the following properties: - Whether the GPG key is [verified or unverified](../project/repository/gpg_signed_commits/index.md) ![Credentials inventory page - GPG keys](img/credentials_inventory_gpg_keys_v13_10.png) - -### Enable or disable the GPG keys view - -Enabling or disabling the GPG keys view is under development but ready for production use. -It is deployed behind a feature flag that is **enabled by default**. -[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) -can opt to disable it. - -To enable it: - -```ruby -Feature.enable(:credential_inventory_gpg_keys) -``` - -To disable it: - -```ruby -Feature.disable(:credential_inventory_gpg_keys) -``` diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md index 326c88f9eef..6372bb3702c 100644 --- a/doc/user/application_security/security_dashboard/index.md +++ b/doc/user/application_security/security_dashboard/index.md @@ -73,18 +73,31 @@ CSV file containing details of the resources scanned. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235558) in GitLab 13.6. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285476) in GitLab 13.10, options to zoom in on a date range, and download the vulnerabilities chart. -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285477) in GitLab 13.11, date range slider to visualise data between given dates. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285477) in GitLab 13.11, date range slider to visualize data between given dates. -At the project level, the Security Dashboard displays a chart with the number of vulnerabilities over time. -Access it by navigating to **Security & Compliance > Security Dashboard**. We display historical -data up to 365 days. The chart's data is updated daily. +A project's Security Dashboard displays a chart with the total number of vulnerabilities +over time. It updates daily with up to 365 days of historical data. By default, +it shows statistics for all vulnerability severities. + +To access the dashboard, from your project's home page go to **Security & Compliance > Security Dashboard**. ![Project Security Dashboard](img/project_security_dashboard_chart_v13_11.png) -Filter the historical data by clicking on the corresponding legend name. The image above, for example, shows -only the graph for vulnerabilities with **high** severity. +### Filter the vulnerabilities chart -To zoom in, select the left-most icon, then select the desired rangeby dragging across the chart. Select **Remove Selection** (**{redo}**) to reset to the original date range. +To filter the chart by vulnerability severity, select the corresponding legend name. + +In the previous example, the chart shows statistics only for vulnerabilities of medium or unknown severity. + +### Customize vulnerabilities chart display + +To customize the view of the vulnerability chart, you can select: + +- A specific time frame by using the time range handles (**{scroll-handle}**). +- A specific area of the chart by using the left-most icon (**{marquee-selection}**) then drag + across the chart. To reset to the original range, select **Remove Selection** (**{redo}**). + +### Download a copy of the vulnerabilities chart To download an SVG image of the chart, select **Save chart to an image** (**{download}**). diff --git a/lib/api/environments.rb b/lib/api/environments.rb index b606b2e814d..57e548183b0 100644 --- a/lib/api/environments.rb +++ b/lib/api/environments.rb @@ -26,7 +26,7 @@ module API get ':id/environments' do authorize! :read_environment, user_project - environments = ::EnvironmentsFinder.new(user_project, current_user, params).execute + environments = ::Environments::EnvironmentsFinder.new(user_project, current_user, params).execute present paginate(environments), with: Entities::Environment, current_user: current_user end diff --git a/lib/gitlab/alert_management/payload/base.rb b/lib/gitlab/alert_management/payload/base.rb index 786c5bf675b..11868ab4e14 100644 --- a/lib/gitlab/alert_management/payload/base.rb +++ b/lib/gitlab/alert_management/payload/base.rb @@ -130,7 +130,7 @@ module Gitlab strong_memoize(:environment) do next unless environment_name - EnvironmentsFinder + ::Environments::EnvironmentsFinder .new(project, nil, { name: environment_name }) .execute .first diff --git a/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb b/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb index 318c6e1734f..f1b74999897 100644 --- a/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb +++ b/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb @@ -10,6 +10,8 @@ module Gitlab extend ActiveSupport::Concern def ordered_items + raise ArgumentError, 'Relation must have a primary key' unless items.primary_key.present? + return super unless Gitlab::Pagination::Keyset::Order.keyset_aware?(items) items @@ -40,6 +42,17 @@ module Gitlab sliced = slice_nodes(sliced, after, :after) if after.present? sliced end + + def items + original_items = super + return original_items if Gitlab::Pagination::Keyset::Order.keyset_aware?(original_items) || Feature.disabled?(:new_graphql_keyset_pagination) + + strong_memoize(:generic_keyset_pagination_items) do + rebuilt_items_with_keyset_order, success = Gitlab::Pagination::Keyset::SimpleOrderBuilder.build(original_items) + + success ? rebuilt_items_with_keyset_order : original_items + end + end end end end diff --git a/lib/gitlab/pagination/keyset/order.rb b/lib/gitlab/pagination/keyset/order.rb index e596e1bac9d..648b371ee9b 100644 --- a/lib/gitlab/pagination/keyset/order.rb +++ b/lib/gitlab/pagination/keyset/order.rb @@ -170,6 +170,8 @@ module Gitlab self.class.build(column_definitions.map(&:reverse)) end + alias_method :to_sql, :to_s + private # Adds extra columns to the SELECT clause diff --git a/lib/gitlab/pagination/keyset/simple_order_builder.rb b/lib/gitlab/pagination/keyset/simple_order_builder.rb new file mode 100644 index 00000000000..5ac5737c3be --- /dev/null +++ b/lib/gitlab/pagination/keyset/simple_order_builder.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +module Gitlab + module Pagination + module Keyset + # This class transforms the `order()` values from an Activerecord scope into a + # Gitlab::Pagination::Keyset::Order instance so the query later can be used in + # keyset pagination. + # + # Return values: + # [transformed_scope, true] # true indicates that the new scope was successfully built + # [orginal_scope, false] # false indicates that the order values are not supported in this class + class SimpleOrderBuilder + def self.build(scope) + new(scope: scope).build + end + + def initialize(scope:) + @scope = scope + @order_values = scope.order_values + @model_class = scope.model + @arel_table = @model_class.arel_table + @primary_key = @model_class.primary_key + end + + def build + order = if order_values.empty? + primary_key_descending_order + elsif ordered_by_primary_key? + primary_key_order + elsif ordered_by_other_column? + column_with_tie_breaker_order + elsif ordered_by_other_column_with_tie_breaker? + tie_breaker_attribute = order_values.second + + tie_breaker_column_order = Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: model_class.primary_key, + order_expression: tie_breaker_attribute + ) + + column_with_tie_breaker_order(tie_breaker_column_order) + end + + order ? [scope.reorder!(order), true] : [scope, false] # [scope, success] + end + + private + + attr_reader :scope, :order_values, :model_class, :arel_table, :primary_key + + def primary_key_descending_order + Gitlab::Pagination::Keyset::Order.build([ + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: model_class.primary_key, + order_expression: arel_table[primary_key].desc + ) + ]) + end + + def primary_key_order + Gitlab::Pagination::Keyset::Order.build([ + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: model_class.primary_key, + order_expression: order_values.first + ) + ]) + end + + def column_with_tie_breaker_order(tie_breaker_column_order = default_tie_breaker_column_order) + order_expression = order_values.first + attribute_name = order_expression.expr.name + + column_nullable = model_class.columns.find { |column| column.name == attribute_name }.null + + nullable = if column_nullable && order_expression.is_a?(Arel::Nodes::Ascending) + :nulls_last + elsif column_nullable && order_expression.is_a?(Arel::Nodes::Descending) + :nulls_first + else + :not_nullable + end + + Gitlab::Pagination::Keyset::Order.build([ + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: attribute_name, + order_expression: order_expression, + nullable: nullable, + distinct: false + ), + tie_breaker_column_order + ]) + end + + def ordered_by_primary_key? + return unless order_values.one? + + attribute = order_values.first.try(:expr) + + return unless attribute + + arel_table[primary_key].to_s == attribute.to_s + end + + def ordered_by_other_column? + return unless order_values.one? + + attribute = order_values.first.try(:expr) + + return unless attribute + return unless attribute.try(:name) + + model_class.column_names.include?(attribute.name.to_s) + end + + def ordered_by_other_column_with_tie_breaker? + return unless order_values.size == 2 + + attribute = order_values.first.try(:expr) + tie_breaker_attribute = order_values.second.try(:expr) + + return unless attribute + return unless tie_breaker_attribute + + model_class.column_names.include?(attribute.name.to_s) && + arel_table[primary_key].to_s == tie_breaker_attribute.to_s + end + + def default_tie_breaker_column_order + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: model_class.primary_key, + order_expression: arel_table[primary_key].desc + ) + end + end + end + end +end diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index d24d172520f..d476faa42c2 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -164,7 +164,7 @@ module Gitlab projects_with_repositories_enabled: count(ProjectFeature.where('repository_access_level > ?', ProjectFeature::DISABLED)), projects_with_tracing_enabled: count(ProjectTracingSetting), projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)), - projects_with_alerts_service_enabled: count(Service.active.where(type: 'AlertsService')), + projects_with_alerts_service_enabled: DEPRECATED_VALUE, projects_with_alerts_created: distinct_count(::AlertManagement::Alert, :project_id), projects_with_enabled_alert_integrations: distinct_count(::AlertManagement::HttpIntegration.active, :project_id), projects_with_prometheus_alerts: distinct_count(PrometheusAlert, :project_id), diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 34f315daa9c..a6e1c5ae782 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -691,6 +691,9 @@ msgstr "" msgid "%{milliseconds}ms" msgstr "" +msgid "%{model_name} not found" +msgstr "" + msgid "%{mrText}, this issue will be closed automatically." msgstr "" @@ -5786,6 +5789,9 @@ msgstr "" msgid "Cannot create the abuse report. This user has been blocked." msgstr "" +msgid "Cannot delete %{profile_name} referenced in security policy" +msgstr "" + msgid "Cannot enable shared runners because parent group does not allow it" msgstr "" @@ -5807,6 +5813,9 @@ msgstr "" msgid "Cannot merge" msgstr "" +msgid "Cannot modify %{profile_name} referenced in security policy" +msgstr "" + msgid "Cannot modify managed Kubernetes cluster" msgstr "" @@ -10115,6 +10124,9 @@ msgstr "" msgid "DastProfiles|Request headers" msgstr "" +msgid "DastProfiles|Rest API" +msgstr "" + msgid "DastProfiles|Run scan" msgstr "" @@ -10160,6 +10172,9 @@ msgstr "" msgid "DastProfiles|Site name" msgstr "" +msgid "DastProfiles|Site type" +msgstr "" + msgid "DastProfiles|Spider timeout" msgstr "" @@ -10208,6 +10223,9 @@ msgstr "" msgid "DastProfiles|Validation status" msgstr "" +msgid "DastProfiles|Website" +msgstr "" + msgid "DastSiteValidation|Copy HTTP header to clipboard" msgstr "" @@ -17185,6 +17203,9 @@ msgstr "" msgid "Instance overview" msgstr "" +msgid "Insufficient permissions" +msgstr "" + msgid "Integration" msgstr "" @@ -27661,6 +27682,12 @@ msgstr "" msgid "Scanner" msgstr "" +msgid "Scanner profile failed to delete" +msgstr "" + +msgid "Scanner profile not found for given parameters" +msgstr "" + msgid "Schedule a new pipeline" msgstr "" @@ -29310,6 +29337,12 @@ msgstr "" msgid "Single or combined queries" msgstr "" +msgid "Site profile failed to delete" +msgstr "" + +msgid "Site profile not found for given parameters" +msgstr "" + msgid "Size" msgstr "" @@ -36162,7 +36195,10 @@ msgstr "" msgid "You have been granted %{access_level} access to the %{source_name} %{source_type}." msgstr "" -msgid "You have been granted %{member_human_access} access to %{title} %{name}." +msgid "You have been granted %{member_human_access} access to group %{name}." +msgstr "" + +msgid "You have been granted %{member_human_access} access to project %{name}." msgstr "" msgid "You have been invited" diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh index 0b109ba0fb3..e53c55a54ea 100644 --- a/scripts/rspec_helpers.sh +++ b/scripts/rspec_helpers.sh @@ -3,11 +3,14 @@ function retrieve_tests_metadata() { mkdir -p knapsack/ rspec_flaky/ rspec_profiling/ + # ${CI_DEFAULT_BRANCH} might not be master in other forks but we want to + # always target the canonical project here, so the branch must be hardcoded local project_path="gitlab-org/gitlab" + local artifact_branch="master" local test_metadata_job_id # Ruby - test_metadata_job_id=$(scripts/api/get_job_id.rb --project "${project_path}" -q "status=success" -q "ref=${CI_DEFAULT_BRANCH}" -q "username=gitlab-bot" -Q "scope=success" --job-name "update-tests-metadata") + test_metadata_job_id=$(scripts/api/get_job_id.rb --project "${project_path}" -q "status=success" -q "ref=${artifact_branch}" -q "username=gitlab-bot" -Q "scope=success" --job-name "update-tests-metadata") if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then scripts/api/download_job_artifact.rb --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb index 714f8451f39..2aa926e4dd8 100644 --- a/spec/factories/usage_data.rb +++ b/spec/factories/usage_data.rb @@ -32,8 +32,6 @@ FactoryBot.define do create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true) create(:project_error_tracking_setting, project: projects[0]) create(:project_error_tracking_setting, project: projects[1], enabled: false) - create(:service, project: projects[0], type: 'AlertsService', active: true) - create(:service, project: projects[1], type: 'AlertsService', active: false) alert_bot_issues = create_list(:incident, 2, project: projects[0], author: User.alert_bot) create_list(:incident, 2, project: projects[1], author: User.alert_bot) issues = create_list(:issue, 4, project: projects[0]) diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index e9960802378..014684c481c 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -64,13 +64,12 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do expect(find_field('Email').value).to eq(group_invite.invite_email) end - it 'sign in, grants access and redirects to group page' do + it 'sign in, grants access and redirects to group activity page' do click_link 'Sign in' fill_in_sign_in_form(user) - expect(current_path).to eq(group_path(group)) - expect(page).to have_content('You have been granted Developer access to group Owned.') + expect(current_path).to eq(activity_group_path(group)) end end @@ -117,26 +116,28 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do context 'email confirmation disabled' do let(:send_email_confirmation) { false } - it 'signs up and redirects to the dashboard page with all the projects/groups invitations automatically accepted' do + it 'signs up and redirects to the most recent membership activity page with all the projects/groups invitations automatically accepted' do fill_in_sign_up_form(new_user) fill_in_welcome_form - expect(current_path).to eq(dashboard_projects_path) - expect(page).to have_content(project.full_name) + expect(current_path).to eq(activity_group_path(group)) + expect(page).to have_content('You have been granted Owner access to group Owned.') visit group_path(group) - expect(page).to have_content(group.full_name) + + visit project_path(project) + expect(page).to have_content(project.name) end context 'the user sign-up using a different email address' do let(:invite_email) { build_stubbed(:user).email } - it 'signs up and redirects to the invitation page' do + it 'signs up and redirects to the activity page' do fill_in_sign_up_form(new_user) fill_in_welcome_form - expect(current_path).to eq(invite_path(group_invite.raw_invite_token)) + expect(current_path).to eq(activity_group_path(group)) end end end @@ -207,7 +208,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_in_form(new_user) fill_in_welcome_form - expect(current_path).to eq(invite_path(group_invite.raw_invite_token)) + expect(current_path).to eq(activity_group_path(group)) end end @@ -221,7 +222,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_up_form(new_user) fill_in_welcome_form - expect(current_path).to eq(invite_path(group_invite.raw_invite_token)) + expect(current_path).to eq(activity_group_path(group)) end end end @@ -273,7 +274,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end end - context 'when accepting the invitation' do + context 'when accepting the invitation as an existing user' do let(:send_email_confirmation) { true } before do @@ -286,7 +287,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do page.click_link 'Accept invitation' - expect(current_path).to eq(group_path(group)) + expect(current_path).to eq(activity_group_path(group)) expect(page).to have_content('You have been granted Owner access to group Owned.') expect(group.users.include?(user)).to be true end diff --git a/spec/finders/environment_names_finder_spec.rb b/spec/finders/environments/environment_names_finder_spec.rb similarity index 98% rename from spec/finders/environment_names_finder_spec.rb rename to spec/finders/environments/environment_names_finder_spec.rb index fe00c800f0a..438f9e9ea7c 100644 --- a/spec/finders/environment_names_finder_spec.rb +++ b/spec/finders/environments/environment_names_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe EnvironmentNamesFinder do +RSpec.describe Environments::EnvironmentNamesFinder do describe '#execute' do let!(:group) { create(:group) } let!(:public_project) { create(:project, :public, namespace: group) } diff --git a/spec/finders/environments_by_deployments_finder_spec.rb b/spec/finders/environments/environments_by_deployments_finder_spec.rb similarity index 98% rename from spec/finders/environments_by_deployments_finder_spec.rb rename to spec/finders/environments/environments_by_deployments_finder_spec.rb index f5fcc4ef72a..1b86aced67d 100644 --- a/spec/finders/environments_by_deployments_finder_spec.rb +++ b/spec/finders/environments/environments_by_deployments_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe EnvironmentsByDeploymentsFinder do +RSpec.describe Environments::EnvironmentsByDeploymentsFinder do let(:project) { create(:project, :repository) } let(:user) { project.creator } let(:environment) { create(:environment, :available, project: project) } diff --git a/spec/finders/environments_finder_spec.rb b/spec/finders/environments/environments_finder_spec.rb similarity index 97% rename from spec/finders/environments_finder_spec.rb rename to spec/finders/environments/environments_finder_spec.rb index c2022331ad9..68c0c524478 100644 --- a/spec/finders/environments_finder_spec.rb +++ b/spec/finders/environments/environments_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe EnvironmentsFinder do +RSpec.describe Environments::EnvironmentsFinder do let(:project) { create(:project, :repository) } let(:user) { project.creator } let(:environment) { create(:environment, :available, project: project) } diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb index 839ad9110cc..03030728834 100644 --- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb +++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb @@ -357,9 +357,10 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do it 'is added to end' do sliced = subject.sliced_nodes - last_order_name = sliced.order_values.last.expr.name - expect(last_order_name).to eq sliced.primary_key + order_sql = sliced.order_values.last.to_sql + + expect(order_sql).to end_with(Project.arel_table[:id].desc.to_sql) end end diff --git a/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb b/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb new file mode 100644 index 00000000000..5af86cb2dc0 --- /dev/null +++ b/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Pagination::Keyset::SimpleOrderBuilder do + let(:ordered_scope) { described_class.build(scope).first } + let(:order_object) { Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(ordered_scope) } + + subject(:sql_with_order) { ordered_scope.to_sql } + + context 'when no order present' do + let(:scope) { Project.where(id: [1, 2, 3]) } + + it 'orders by primary key' do + expect(sql_with_order).to end_with('ORDER BY "projects"."id" DESC') + end + + it 'sets the column definition distinct and not nullable' do + column_definition = order_object.column_definitions.first + + expect(column_definition).to be_not_nullable + expect(column_definition).to be_distinct + end + end + + context 'when primary key order present' do + let(:scope) { Project.where(id: [1, 2, 3]).order(id: :asc) } + + it 'orders by primary key without altering the direction' do + expect(sql_with_order).to end_with('ORDER BY "projects"."id" ASC') + end + end + + context 'when ordered by other column' do + let(:scope) { Project.where(id: [1, 2, 3]).order(created_at: :asc) } + + it 'adds extra primary key order as tie-breaker' do + expect(sql_with_order).to end_with('ORDER BY "projects"."created_at" ASC, "projects"."id" DESC') + end + + it 'sets the column definition for created_at non-distinct and nullable' do + column_definition = order_object.column_definitions.first + + expect(column_definition.attribute_name).to eq('created_at') + expect(column_definition.nullable?).to eq(true) # be_nullable calls non_null? method for some reason + expect(column_definition).not_to be_distinct + end + end + + context 'when ordered by two columns where the last one is the tie breaker' do + let(:scope) { Project.where(id: [1, 2, 3]).order(created_at: :asc, id: :asc) } + + it 'preserves the order' do + expect(sql_with_order).to end_with('ORDER BY "projects"."created_at" ASC, "projects"."id" ASC') + end + end + + context 'when non-nullable column is given' do + let(:scope) { Project.where(id: [1, 2, 3]).order(namespace_id: :asc, id: :asc) } + + it 'sets the column definition for namespace_id non-distinct and non-nullable' do + column_definition = order_object.column_definitions.first + + expect(column_definition.attribute_name).to eq('namespace_id') + expect(column_definition).to be_not_nullable + expect(column_definition).not_to be_distinct + end + end + + context 'return :unable_to_order symbol when order cannot be built' do + subject(:success) { described_class.build(scope).last } + + context 'when raw SQL order is given' do + let(:scope) { Project.order('id DESC') } + + it { is_expected.to eq(false) } + end + + context 'when NULLS LAST order is given' do + let(:scope) { Project.order(::Gitlab::Database.nulls_last_order('created_at', 'ASC')) } + + it { is_expected.to eq(false) } + end + + context 'when more than 2 columns are given for the order' do + let(:scope) { Project.order(created_at: :asc, updated_at: :desc, id: :asc) } + + it { is_expected.to eq(false) } + end + end +end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index e3a224f5ce2..74d9d22b857 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -571,7 +571,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(count_data[:projects_with_repositories_enabled]).to eq(3) expect(count_data[:projects_with_error_tracking_enabled]).to eq(1) expect(count_data[:projects_with_tracing_enabled]).to eq(1) - expect(count_data[:projects_with_alerts_service_enabled]).to eq(1) + expect(count_data[:projects_with_alerts_service_enabled]).to eq(Gitlab::UsageData::DEPRECATED_VALUE) expect(count_data[:projects_with_enabled_alert_integrations]).to eq(1) expect(count_data[:projects_with_prometheus_alerts]).to eq(2) expect(count_data[:projects_with_terraform_reports]).to eq(2) diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index c4d17905637..d981189c6f1 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -120,6 +120,12 @@ RSpec.describe BroadcastMessage do expect(subject.call('/users/name/issues').length).to eq(1) end + it 'returns message if provided a path without a preceding slash' do + create(:broadcast_message, target_path: "/users/*/issues", broadcast_type: broadcast_type) + + expect(subject.call('users/name/issues').length).to eq(1) + end + it 'returns the message for empty target path' do create(:broadcast_message, target_path: "", broadcast_type: broadcast_type) diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb index 3a1bcfc69b8..6566bfc7992 100644 --- a/spec/requests/api/graphql_spec.rb +++ b/spec/requests/api/graphql_spec.rb @@ -283,25 +283,50 @@ RSpec.describe 'GraphQL' do ) end - it 'paginates datetimes correctly when they have millisecond data' do - # let's make sure we're actually querying a timestamp, just in case - expect(Gitlab::Graphql::Pagination::Keyset::QueryBuilder) - .to receive(:new).with(anything, anything, hash_including('created_at'), anything).and_call_original + context 'when new_graphql_keyset_pagination feature flag is off' do + before do + stub_feature_flags(new_graphql_keyset_pagination: false) + end - execute_query - first_page = graphql_data - edges = first_page.dig(*issues_edges) - cursor = first_page.dig(*end_cursor) + it 'paginates datetimes correctly when they have millisecond data' do + # let's make sure we're actually querying a timestamp, just in case + expect(Gitlab::Graphql::Pagination::Keyset::QueryBuilder) + .to receive(:new).with(anything, anything, hash_including('created_at'), anything).and_call_original - expect(edges.count).to eq(6) - expect(edges.last['node']['iid']).to eq(issues[4].iid.to_s) + execute_query + first_page = graphql_data + edges = first_page.dig(*issues_edges) + cursor = first_page.dig(*end_cursor) - execute_query(after: cursor) - second_page = graphql_data - edges = second_page.dig(*issues_edges) + expect(edges.count).to eq(6) + expect(edges.last['node']['iid']).to eq(issues[4].iid.to_s) - expect(edges.count).to eq(4) - expect(edges.last['node']['iid']).to eq(issues[0].iid.to_s) + execute_query(after: cursor) + second_page = graphql_data + edges = second_page.dig(*issues_edges) + + expect(edges.count).to eq(4) + expect(edges.last['node']['iid']).to eq(issues[0].iid.to_s) + end + end + + context 'when new_graphql_keyset_pagination feature flag is on' do + it 'paginates datetimes correctly when they have millisecond data' do + execute_query + first_page = graphql_data + edges = first_page.dig(*issues_edges) + cursor = first_page.dig(*end_cursor) + + expect(edges.count).to eq(6) + expect(edges.last['node']['iid']).to eq(issues[4].iid.to_s) + + execute_query(after: cursor) + second_page = graphql_data + edges = second_page.dig(*issues_edges) + + expect(edges.count).to eq(4) + expect(edges.last['node']['iid']).to eq(issues[0].iid.to_s) + end end end end diff --git a/spec/services/lfs/push_service_spec.rb b/spec/services/lfs/push_service_spec.rb index f67284ff48d..58fb2f3fb9b 100644 --- a/spec/services/lfs/push_service_spec.rb +++ b/spec/services/lfs/push_service_spec.rb @@ -63,6 +63,7 @@ RSpec.describe Lfs::PushService do it 'returns a failure when submitting a batch fails' do expect(lfs_client).to receive(:batch!) { raise 'failed' } + expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original expect(service.execute).to eq(status: :error, message: 'failed') end @@ -70,6 +71,7 @@ RSpec.describe Lfs::PushService do stub_lfs_batch(lfs_object) expect(lfs_client).to receive(:upload!) { raise 'failed' } + expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original expect(service.execute).to eq(status: :error, message: 'failed') end diff --git a/spec/services/post_receive_service_spec.rb b/spec/services/post_receive_service_spec.rb index 033194972c7..2a78dc454c7 100644 --- a/spec/services/post_receive_service_spec.rb +++ b/spec/services/post_receive_service_spec.rb @@ -264,7 +264,7 @@ RSpec.describe PostReceiveService do context "project path matches" do before do - allow(project).to receive(:full_path).and_return("/company/sekrit-project") + allow(project).to receive(:full_path).and_return("company/sekrit-project") end it "does output the latest scoped broadcast message" do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index e0d6b9afcff..36ae0571ad2 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -273,16 +273,6 @@ RSpec.describe Projects::CreateService, '#execute' do opts[:default_branch] = 'master' expect(create_project(user, opts)).to eq(nil) end - - it 'sets invalid service as inactive' do - create(:service, type: 'JiraService', project: nil, template: true, active: true) - - project = create_project(user, opts) - service = project.services.first - - expect(project).to be_persisted - expect(service.active).to be false - end end context 'wiki_enabled creates repository directory' do @@ -633,17 +623,6 @@ RSpec.describe Projects::CreateService, '#execute' do end end end - - context 'when there is an invalid integration' do - before do - create(:service, :template, type: 'DroneCiService', active: true) - end - - it 'creates an inactive service' do - expect(project).to be_persisted - expect(project.services.first.active).to be false - end - end end context 'when skip_disk_validation is used' do diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb index 5aca5d68677..3434980341b 100644 --- a/spec/workers/build_finished_worker_spec.rb +++ b/spec/workers/build_finished_worker_spec.rb @@ -22,7 +22,6 @@ RSpec.describe BuildFinishedWorker do end expect(BuildHooksWorker).to receive(:perform_async) - expect(ExpirePipelineCacheWorker).to receive(:perform_async) expect(ChatNotificationWorker).not_to receive(:perform_async) expect(ArchiveTraceWorker).to receive(:perform_in)