diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-01 12:07:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-01 12:07:33 +0300 |
commit | f5eabcfa0e39e8212eb333d9e824294d14f530d5 (patch) | |
tree | 1a365d06c584e61b50ac3850b4058fd7826173c8 | |
parent | 0537e77587de43e2b3c5cbd8610641b3003a9840 (diff) |
Add latest changes from gitlab-org/gitlab@master
35 files changed, 452 insertions, 64 deletions
diff --git a/app/assets/javascripts/batch_comments/components/review_bar.vue b/app/assets/javascripts/batch_comments/components/review_bar.vue index 9ffc5ee34cf..080a5543e53 100644 --- a/app/assets/javascripts/batch_comments/components/review_bar.vue +++ b/app/assets/javascripts/batch_comments/components/review_bar.vue @@ -26,7 +26,7 @@ export default { </script> <template> <div v-show="draftsCount > 0"> - <nav class="review-bar-component"> + <nav class="review-bar-component" data-testid="review_bar_component"> <div class="review-bar-content d-flex gl-justify-content-end" data-qa-selector="review_bar_content" diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 38a5bdd4a71..d00e6e59cf5 100644 --- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -75,6 +75,13 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => { icon: 'approval', tag: '@approved-by', }, + tokenAlternative: { + formattedKey: __('Approved-By'), + key: 'approved-by', + type: 'string', + param: 'usernames', + symbol: '@', + }, condition: [ { url: 'approved_by_usernames[]=None', @@ -105,7 +112,11 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => { const tokenPosition = 3; IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, ...[approvedBy.token]); - IssuableTokenKeys.tokenKeysWithAlternative.splice(tokenPosition, 0, ...[approvedBy.token]); + IssuableTokenKeys.tokenKeysWithAlternative.splice( + tokenPosition, + 0, + ...[approvedBy.token, approvedBy.tokenAlternative], + ); IssuableTokenKeys.conditions.push(...approvedBy.condition); const environmentToken = { diff --git a/app/assets/stylesheets/components/batch_comments/review_bar.scss b/app/assets/stylesheets/components/batch_comments/review_bar.scss index d769ea73101..bcd06974813 100644 --- a/app/assets/stylesheets/components/batch_comments/review_bar.scss +++ b/app/assets/stylesheets/components/batch_comments/review_bar.scss @@ -2,13 +2,15 @@ position: fixed; bottom: 0; left: 0; - width: 100%; - background: $white; z-index: $zindex-dropdown-menu; - padding: 7px 0 6px; // to keep aligned with "collapse sidebar" button on the left sidebar - border-top: 1px solid $border-color; + display: flex; + align-items: center; + width: 100%; + height: $toggle-sidebar-height; padding-left: $contextual-sidebar-width; padding-right: $gutter_collapsed_width; + background: $white; + border-top: 1px solid $border-color; transition: padding $sidebar-transition-duration; .page-with-icon-sidebar & { diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 9e42d218ceb..0f00fda4687 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -17,7 +17,7 @@ class Projects::ForksController < Projects::ApplicationController feature_category :source_code_management before_action do - push_frontend_feature_flag(:fork_project_form) + push_frontend_feature_flag(:fork_project_form, @project, default_enabled: :yaml) end def index diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 19fcd91a5b8..e23fa3f7f68 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -76,6 +76,7 @@ class MergeRequestsFinder < IssuableFinder def filter_negated_items(items) items = super(items) items = by_negated_reviewer(items) + items = by_negated_approved_by(items) by_negated_target_branch(items) end @@ -119,6 +120,12 @@ class MergeRequestsFinder < IssuableFinder end # rubocop: enable CodeReuse/ActiveRecord + def by_negated_approved_by(items) + return items unless not_params[:approved_by_usernames] + + items.not_approved_by_users_with_usernames(not_params[:approved_by_usernames]) + end + def source_project_id @source_project_id ||= params[:source_project_id].presence end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 29887fb238b..6c57a31f3db 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -64,7 +64,7 @@ module NavHelper end def admin_analytics_nav_links - %w(dev_ops_report) + %w(dev_ops_report usage_trends) end def group_issues_sub_menu_items diff --git a/app/models/concerns/approvable_base.rb b/app/models/concerns/approvable_base.rb index c2d94b50f8d..ef7ba7b1089 100644 --- a/app/models/concerns/approvable_base.rb +++ b/app/models/concerns/approvable_base.rb @@ -24,6 +24,19 @@ module ApprovableBase .group(:id) .having("COUNT(users.id) = ?", usernames.size) end + + scope :not_approved_by_users_with_usernames, -> (usernames) do + users = User.where(username: usernames).select(:id) + self_table = self.arel_table + app_table = Approval.arel_table + + where( + Approval.where(approvals: { user_id: users }) + .where(app_table[:merge_request_id].eq(self_table[:id])) + .select('true') + .arel.exists.not + ) + end end class_methods do diff --git a/app/models/plan_limits.rb b/app/models/plan_limits.rb index f17078c0cab..bf08da6a1e1 100644 --- a/app/models/plan_limits.rb +++ b/app/models/plan_limits.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class PlanLimits < ApplicationRecord + include IgnorableColumns + + ignore_column :ci_max_artifact_size_running_container_scanning, remove_with: '14.3', remove_after: '2021-08-22' + LimitUndefinedError = Class.new(StandardError) belongs_to :plan diff --git a/app/services/projects/protect_default_branch_service.rb b/app/services/projects/protect_default_branch_service.rb index 1d3fb523448..0111b9e377a 100644 --- a/app/services/projects/protect_default_branch_service.rb +++ b/app/services/projects/protect_default_branch_service.rb @@ -22,7 +22,7 @@ module Projects # Ensure HEAD points to the default branch in case it is not master project.change_head(default_branch) - create_protected_branch if protect_branch? + create_protected_branch if protect_branch? && !protected_branch_exists? end def create_protected_branch @@ -44,6 +44,10 @@ module Projects !ProtectedBranch.protected?(project, default_branch) end + def protected_branch_exists? + project.protected_branches.find_by_name(default_branch).present? + end + def default_branch project.default_branch end diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml index 624d0a21b81..b7c2b4d86b2 100644 --- a/app/views/groups/_home_panel.html.haml +++ b/app/views/groups/_home_panel.html.haml @@ -23,6 +23,10 @@ .home-panel-buttons.col-md-12.col-lg-6 - if current_user .gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } } + - if current_user.admin? + = link_to [:admin, @group], class: 'btn btn-default gl-button btn-icon gl-mt-3 gl-mr-2', title: s_('View group in admin area'), + data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = sprite_icon('admin') - if @notification_setting .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mx-2 gl-mt-3 gl-vertical-align-top' } } - if can_create_subgroups diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 26291c0358e..86172499118 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -47,6 +47,10 @@ = cache_if(cache_enabled, [@project, :buttons, current_user, @notification_setting], expires_in: 1.day) do .project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5 - if current_user + - if current_user.admin? + = link_to [:admin, @project], class: 'btn gl-button btn-icon gl-align-self-start gl-py-2! gl-mr-3', title: s_('View project in admin area'), + data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = sprite_icon('admin') .gl-display-flex.gl-align-items-start.gl-mr-3 - if @notification_setting .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id } } diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index 0716eda79a8..8848fbae9cb 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -1,6 +1,6 @@ - page_title s_("ForkProject|Fork project") -- if Feature.enabled?(:fork_project_form) +- if Feature.enabled?(:fork_project_form, @project, default_enabled: :yaml) #fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'), endpoint: new_project_fork_path(@project, format: :json), new_group_path: new_group_path, diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index eff33df665b..61edd4b91e3 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -37,7 +37,7 @@ :urgency: :low :resource_boundary: :unknown :weight: 1 - :idempotent: true + :idempotent: :tags: [] - :name: authorized_project_update:authorized_project_update_user_refresh_over_user_range :worker_name: AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker diff --git a/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb b/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb index 5ca9de63fd7..dbd54ad2adf 100644 --- a/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb +++ b/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb @@ -1,15 +1,55 @@ # frozen_string_literal: true module AuthorizedProjectUpdate - class UserRefreshFromReplicaWorker < ::AuthorizedProjectsWorker + class UserRefreshFromReplicaWorker # rubocop:disable Scalability/IdempotentWorker + include ApplicationWorker + + sidekiq_options retry: 3 feature_category :authentication_and_authorization urgency :low queue_namespace :authorized_project_update + + # This job will not be deduplicated since it is marked with + # `data_consistency :delayed` and not `idempotent!` + # See https://gitlab.com/gitlab-org/gitlab/-/issues/325291 deduplicate :until_executing, including_scheduled: true - idempotent! + data_consistency :delayed + + def perform(user_id) + user = User.find_by_id(user_id) + return unless user + + if Feature.enabled?(:user_refresh_from_replica_worker_uses_replica_db) + enqueue_project_authorizations_refresh(user) if project_authorizations_needs_refresh?(user) + else + use_primary_database + user.refresh_authorized_projects(source: self.class.name) + end + end + + private + + def use_primary_database + if ::Gitlab::Database::LoadBalancing.enable? + ::Gitlab::Database::LoadBalancing::Session.current.use_primary! + end + end + + def project_authorizations_needs_refresh?(user) + AuthorizedProjectUpdate::FindRecordsDueForRefreshService.new(user).needs_refresh? + end + + def enqueue_project_authorizations_refresh(user) + with_context(user: user, related_class: current_caller_id) do + AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker.perform_async(user.id) + end + end - # This worker will start reading data from the replica database soon - # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/333219 + # We use this so that we can obtain the details of the original caller + # in the enqueued `AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker` job. + def current_caller_id + Gitlab::ApplicationContext.current_context_attribute('meta.caller_id').presence + end end end diff --git a/config/feature_flags/development/fork_project_form.yml b/config/feature_flags/development/fork_project_form.yml index 93bccc4f41b..90532c78c8a 100644 --- a/config/feature_flags/development/fork_project_form.yml +++ b/config/feature_flags/development/fork_project_form.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321387 milestone: '13.10' type: development group: group::source code -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/user_refresh_from_replica_worker_uses_replica_db.yml b/config/feature_flags/development/user_refresh_from_replica_worker_uses_replica_db.yml new file mode 100644 index 00000000000..6e3d8d315cd --- /dev/null +++ b/config/feature_flags/development/user_refresh_from_replica_worker_uses_replica_db.yml @@ -0,0 +1,8 @@ +--- +name: user_refresh_from_replica_worker_uses_replica_db +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64276 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334766 +milestone: '14.1' +type: development +group: group::access +default_enabled: false diff --git a/db/migrate/20210627204936_add_plan_limits_max_size_cluster_image_scanning_column.rb b/db/migrate/20210627204936_add_plan_limits_max_size_cluster_image_scanning_column.rb new file mode 100644 index 00000000000..322e6991d0b --- /dev/null +++ b/db/migrate/20210627204936_add_plan_limits_max_size_cluster_image_scanning_column.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddPlanLimitsMaxSizeClusterImageScanningColumn < ActiveRecord::Migration[6.0] + def change + add_column :plan_limits, :ci_max_artifact_size_cluster_image_scanning, :integer, null: false, default: 0 + end +end diff --git a/db/schema_migrations/20210627204936 b/db/schema_migrations/20210627204936 new file mode 100644 index 00000000000..abc39339816 --- /dev/null +++ b/db/schema_migrations/20210627204936 @@ -0,0 +1 @@ +b37bf7db9c00c8f54c0ccca2d418f1279e12ff7e5b71347966494dc5645eb648
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index a9f532e63b5..7a274ebfdf8 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -16352,7 +16352,8 @@ CREATE TABLE plan_limits ( ci_registered_project_runners integer DEFAULT 1000 NOT NULL, web_hook_calls integer DEFAULT 0 NOT NULL, ci_daily_pipeline_schedule_triggers integer DEFAULT 0 NOT NULL, - ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL + ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL, + ci_max_artifact_size_cluster_image_scanning integer DEFAULT 0 NOT NULL ); CREATE SEQUENCE plan_limits_id_seq diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md index 300c107abc8..ea3348e6b75 100644 --- a/doc/administration/instance_limits.md +++ b/doc/administration/instance_limits.md @@ -426,6 +426,7 @@ setting is used: | `ci_max_artifact_size_archive` | 0 | | `ci_max_artifact_size_browser_performance` | 0 | | `ci_max_artifact_size_cluster_applications` | 0 | +| `ci_max_artifact_size_cluster_image_scanning` | 0 | | `ci_max_artifact_size_cobertura` | 0 | | `ci_max_artifact_size_codequality` | 0 | | `ci_max_artifact_size_container_scanning` | 0 | @@ -444,7 +445,6 @@ setting is used: | `ci_max_artifact_size_network_referee` | 0 | | `ci_max_artifact_size_performance` | 0 | | `ci_max_artifact_size_requirements` | 0 | -| `ci_max_artifact_size_running_container_scanning` | 0 | | `ci_max_artifact_size_sast` | 0 | | `ci_max_artifact_size_secret_detection` | 0 | | `ci_max_artifact_size_terraform` | 5 MB ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37018) in GitLab 13.3) | diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 56dc130bdd2..4a9c75967a3 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -12627,11 +12627,11 @@ Represents summary of a security report. | Name | Type | Description | | ---- | ---- | ----------- | | <a id="securityreportsummaryapifuzzing"></a>`apiFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `api_fuzzing` scan. | +| <a id="securityreportsummaryclusterimagescanning"></a>`clusterImageScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `cluster_image_scanning` scan. | | <a id="securityreportsummarycontainerscanning"></a>`containerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `container_scanning` scan. | | <a id="securityreportsummarycoveragefuzzing"></a>`coverageFuzzing` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `coverage_fuzzing` scan. | | <a id="securityreportsummarydast"></a>`dast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dast` scan. | | <a id="securityreportsummarydependencyscanning"></a>`dependencyScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `dependency_scanning` scan. | -| <a id="securityreportsummaryrunningcontainerscanning"></a>`runningContainerScanning` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `running_container_scanning` scan. | | <a id="securityreportsummarysast"></a>`sast` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `sast` scan. | | <a id="securityreportsummarysecretdetection"></a>`secretDetection` | [`SecurityReportSummarySection`](#securityreportsummarysection) | Aggregated counts for the `secret_detection` scan. | @@ -13486,7 +13486,7 @@ Represents a vulnerability. | <a id="vulnerabilitynotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) | | <a id="vulnerabilityprimaryidentifier"></a>`primaryIdentifier` | [`VulnerabilityIdentifier`](#vulnerabilityidentifier) | Primary identifier of the vulnerability. | | <a id="vulnerabilityproject"></a>`project` | [`Project`](#project) | The project on which the vulnerability was found. | -| <a id="vulnerabilityreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING, RUNNING_CONTAINER_SCANNING). `Scan Type` in the UI. | +| <a id="vulnerabilityreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING, CLUSTER_IMAGE_SCANNING). `Scan Type` in the UI. | | <a id="vulnerabilityresolvedat"></a>`resolvedAt` | [`Time`](#time) | Timestamp of when the vulnerability state was changed to resolved. | | <a id="vulnerabilityresolvedby"></a>`resolvedBy` | [`UserCore`](#usercore) | The user that resolved the vulnerability. | | <a id="vulnerabilityresolvedondefaultbranch"></a>`resolvedOnDefaultBranch` | [`Boolean!`](#boolean) | Indicates whether the vulnerability is fixed on the default branch or not. | @@ -15162,11 +15162,11 @@ The type of the security scan that found the vulnerability. | Value | Description | | ----- | ----------- | | <a id="vulnerabilityreporttypeapi_fuzzing"></a>`API_FUZZING` | | +| <a id="vulnerabilityreporttypecluster_image_scanning"></a>`CLUSTER_IMAGE_SCANNING` | | | <a id="vulnerabilityreporttypecontainer_scanning"></a>`CONTAINER_SCANNING` | | | <a id="vulnerabilityreporttypecoverage_fuzzing"></a>`COVERAGE_FUZZING` | | | <a id="vulnerabilityreporttypedast"></a>`DAST` | | | <a id="vulnerabilityreporttypedependency_scanning"></a>`DEPENDENCY_SCANNING` | | -| <a id="vulnerabilityreporttyperunning_container_scanning"></a>`RUNNING_CONTAINER_SCANNING` | | | <a id="vulnerabilityreporttypesast"></a>`SAST` | | | <a id="vulnerabilityreporttypesecret_detection"></a>`SECRET_DETECTION` | | diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index 7dd5dd74f0b..41b3b9ed428 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -205,6 +205,30 @@ def down end ``` +**Multiple changes on the same table:** + +The helper `with_lock_retries` wraps all operations into a single transaction. When you have the lock, +you should do as much as possible inside the transaction rather than trying to get another lock later. +Be careful about running long database statements within the block. The acquired locks are kept until the transaction (block) finishes and depending on the lock type, it might block other database operations. + +```ruby +include Gitlab::Database::MigrationHelpers + +def up + with_lock_retries do + add_column :users, :full_name, :string + add_column :users, :bio, :string + end +end + +def down + with_lock_retries do + remove_column :users, :full_name + remove_column :users, :bio + end +end +``` + **Removing a foreign key:** ```ruby diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md index cfb97f565b4..b7ec3892f1e 100644 --- a/doc/development/usage_ping/dictionary.md +++ b/doc/development/usage_ping/dictionary.md @@ -17318,6 +17318,18 @@ Status: `data_available` Tiers: +### `usage_activity_by_stage.secure.cluster_image_scanning_scans` + +Counts cluster image scanning jobs + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210618124854_cluster_image_scanning_scans.yml) + +Group: `group::container security` + +Status: `implemented` + +Tiers: `ultimate` + ### `usage_activity_by_stage.secure.container_scanning_scans` Counts container scanning jobs @@ -17366,18 +17378,6 @@ Status: `data_available` Tiers: `ultimate` -### `usage_activity_by_stage.secure.running_container_scanning_scans` - -Counts running container scanning jobs - -[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210618124854_running_container_scanning_scans.yml) - -Group: `group::container security` - -Status: `data_available` - -Tiers: `ultimate` - ### `usage_activity_by_stage.secure.sast_scans` Counts sast jobs @@ -19394,6 +19394,30 @@ Status: `data_available` Tiers: `free` +### `usage_activity_by_stage_monthly.secure.cluster_image_scanning_pipeline` + +Pipelines containing a Cluster Image Scanning job + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618125224_cluster_image_scanning_pipeline.yml) + +Group: `group::container security` + +Status: `implemented` + +Tiers: `ultimate` + +### `usage_activity_by_stage_monthly.secure.cluster_image_scanning_scans` + +Counts cluster image scanning jobs + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618101233_cluster_image_scanning_scans.yml) + +Group: `group::container security` + +Status: `implemented` + +Tiers: `ultimate` + ### `usage_activity_by_stage_monthly.secure.container_scanning_pipeline` Pipelines containing a Container Scanning job @@ -19490,30 +19514,6 @@ Status: `data_available` Tiers: `ultimate` -### `usage_activity_by_stage_monthly.secure.running_container_scanning_pipeline` - -Pipelines containing a Running Container Scanning job - -[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618125224_running_container_scanning_pipeline.yml) - -Group: `group::container security` - -Status: `data_available` - -Tiers: `ultimate` - -### `usage_activity_by_stage_monthly.secure.running_container_scanning_scans` - -Counts running container scanning jobs - -[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210618101233_running_container_scanning_scans.yml) - -Group: `group::container security` - -Status: `data_available` - -Tiers: `ultimate` - ### `usage_activity_by_stage_monthly.secure.sast_pipeline` Counts of Pipelines that have at least 1 SAST job diff --git a/lib/api/admin/ci/variables.rb b/lib/api/admin/ci/variables.rb index 654d3a48162..0462878c90c 100644 --- a/lib/api/admin/ci/variables.rb +++ b/lib/api/admin/ci/variables.rb @@ -8,7 +8,7 @@ module API before { authenticated_as_admin! } - feature_category :continuous_integration + feature_category :pipeline_authoring namespace 'admin' do namespace 'ci' do diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index b6efa0b04a1..ca165ab78af 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -7,7 +7,7 @@ module API content_type :txt, 'text/plain' - feature_category :continuous_integration + feature_category :runner resource :runners do desc 'Registers a new Runner' do diff --git a/lib/api/variables.rb b/lib/api/variables.rb index 8b0745c6b5b..75df0e050a6 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -7,7 +7,7 @@ module API before { authenticate! } before { authorize! :admin_build, user_project } - feature_category :continuous_integration + feature_category :pipeline_authoring helpers Helpers::VariablesHelpers diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a13fd8486b6..f643a0e73ba 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -35860,6 +35860,9 @@ msgstr "" msgid "View full log" msgstr "" +msgid "View group in admin area" +msgstr "" + msgid "View group labels" msgstr "" @@ -35911,6 +35914,9 @@ msgstr "" msgid "View project" msgstr "" +msgid "View project in admin area" +msgstr "" + msgid "View project labels" msgstr "" diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb index a5094be2bcf..1fcb40df491 100644 --- a/spec/features/merge_request/batch_comments_spec.rb +++ b/spec/features/merge_request/batch_comments_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Merge request > Batch comments', :js do end it 'has review bar' do - expect(page).to have_css('.review-bar-component', visible: false) + expect(page).to have_selector('[data-testid="review_bar_component"]', visible: false) end it 'adds draft note' do @@ -32,7 +32,7 @@ RSpec.describe 'Merge request > Batch comments', :js do expect(find('.draft-note-component')).to have_content('Line is wrong') - expect(page).to have_css('.review-bar-component') + expect(page).to have_selector('[data-testid="review_bar_component"]') expect(find('.review-bar-content .btn-confirm')).to have_content('1') end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 3b835d366db..c2ea918449c 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -520,6 +520,44 @@ RSpec.describe MergeRequestsFinder do end end + context 'filtering by approved by' do + let(:params) { { approved_by_usernames: user2.username } } + + before do + create(:approval, merge_request: merge_request3, user: user2) + end + + it 'returns merge requests approved by that user' do + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request3) + end + + context 'not filter' do + let(:params) { { not: { approved_by_usernames: user2.username } } } + + it 'returns merge requests not approved by that user' do + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request4, merge_request5) + end + end + + context 'when filtering by author and not approved by' do + let(:params) { { not: { approved_by_usernames: user2.username }, author_username: user.username } } + + before do + merge_request4.update!(author: user2) + end + + it 'returns merge requests authored by user and not approved by user2' do + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request5) + end + end + end + context 'filtering by created_at/updated_at' do let(:new_project) { create(:project, forked_from_project: project1) } diff --git a/spec/models/concerns/approvable_base_spec.rb b/spec/models/concerns/approvable_base_spec.rb index a9e944cf220..c7ea2631a24 100644 --- a/spec/models/concerns/approvable_base_spec.rb +++ b/spec/models/concerns/approvable_base_spec.rb @@ -59,4 +59,25 @@ RSpec.describe ApprovableBase do end end end + + describe '.not_approved_by_users_with_usernames' do + subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) } + + let!(:merge_request2) { create(:merge_request) } + let!(:merge_request3) { create(:merge_request) } + let!(:merge_request4) { create(:merge_request) } + let(:user2) { create(:user) } + let(:user3) { create(:user) } + + before do + create(:approval, merge_request: merge_request, user: user) + create(:approval, merge_request: merge_request2, user: user2) + create(:approval, merge_request: merge_request2, user: user3) + create(:approval, merge_request: merge_request4, user: user3) + end + + it 'has the merge request that is not approved at all and not approved by either user' do + expect(subject).to contain_exactly(merge_request3, merge_request4) + end + end end diff --git a/spec/models/plan_limits_spec.rb b/spec/models/plan_limits_spec.rb index 9078d72e044..72fda2280e5 100644 --- a/spec/models/plan_limits_spec.rb +++ b/spec/models/plan_limits_spec.rb @@ -185,7 +185,7 @@ RSpec.describe PlanLimits do ci_max_artifact_size_junit ci_max_artifact_size_sast ci_max_artifact_size_dast - ci_max_artifact_size_running_container_scanning + ci_max_artifact_size_cluster_image_scanning ci_max_artifact_size_codequality ci_max_artifact_size_license_management ci_max_artifact_size_performance diff --git a/spec/services/projects/protect_default_branch_service_spec.rb b/spec/services/projects/protect_default_branch_service_spec.rb index a485a64ca35..c8aa421cdd4 100644 --- a/spec/services/projects/protect_default_branch_service_spec.rb +++ b/spec/services/projects/protect_default_branch_service_spec.rb @@ -99,6 +99,53 @@ RSpec.describe Projects::ProtectDefaultBranchService do .not_to have_received(:create_protected_branch) end end + + context 'when protected branch does not exist' do + before do + allow(service) + .to receive(:protected_branch_exists?) + .and_return(false) + allow(service) + .to receive(:protect_branch?) + .and_return(true) + end + + it 'changes the HEAD of the project' do + service.protect_default_branch + + expect(project) + .to have_received(:change_head) + end + + it 'protects the default branch' do + service.protect_default_branch + + expect(service) + .to have_received(:create_protected_branch) + end + end + + context 'when protected branch already exists' do + before do + allow(service) + .to receive(:protected_branch_exists?) + .and_return(true) + end + + it 'changes the HEAD of the project' do + service.protect_default_branch + + expect(project) + .to have_received(:change_head) + end + + it 'does not protect the default branch' do + service.protect_default_branch + + expect(service) + .not_to have_received(:create_protected_branch) + end + end end describe '#create_protected_branch' do diff --git a/spec/views/groups/_home_panel.html.haml_spec.rb b/spec/views/groups/_home_panel.html.haml_spec.rb index b8168b20450..e76862cdaea 100644 --- a/spec/views/groups/_home_panel.html.haml_spec.rb +++ b/spec/views/groups/_home_panel.html.haml_spec.rb @@ -14,4 +14,30 @@ RSpec.describe 'groups/_home_panel' do expect(rendered).to have_content("Group ID: #{group.id}") end + + context 'admin area link' do + it 'renders admin area link for admin' do + allow(view).to receive(:current_user).and_return(create(:admin)) + + render + + expect(rendered).to have_link(href: admin_group_path(group)) + end + + it 'does not render admin area link for non-admin' do + allow(view).to receive(:current_user).and_return(create(:user)) + + render + + expect(rendered).not_to have_link(href: admin_group_path(group)) + end + + it 'does not render admin area link for anonymous' do + allow(view).to receive(:current_user).and_return(nil) + + render + + expect(rendered).not_to have_link(href: admin_group_path(group)) + end + end end diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index d329c57af00..78131937d3c 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -5,6 +5,38 @@ require 'spec_helper' RSpec.describe 'projects/_home_panel' do include ProjectForksHelper + context 'admin area link' do + let(:project) { create(:project) } + + before do + assign(:project, project) + end + + it 'renders admin area link for admin' do + allow(view).to receive(:current_user).and_return(create(:admin)) + + render + + expect(rendered).to have_link(href: admin_project_path(project)) + end + + it 'does not render admin area link for non-admin' do + allow(view).to receive(:current_user).and_return(create(:user)) + + render + + expect(rendered).not_to have_link(href: admin_project_path(project)) + end + + it 'does not render admin area link for anonymous' do + allow(view).to receive(:current_user).and_return(nil) + + render + + expect(rendered).not_to have_link(href: admin_project_path(project)) + end + end + context 'notifications' do let(:project) { create(:project) } diff --git a/spec/workers/authorized_project_update/user_refresh_from_replica_worker_spec.rb b/spec/workers/authorized_project_update/user_refresh_from_replica_worker_spec.rb index cdf2cb493b0..198a16bc22e 100644 --- a/spec/workers/authorized_project_update/user_refresh_from_replica_worker_spec.rb +++ b/spec/workers/authorized_project_update/user_refresh_from_replica_worker_spec.rb @@ -3,9 +3,97 @@ require 'spec_helper' RSpec.describe AuthorizedProjectUpdate::UserRefreshFromReplicaWorker do + let_it_be(:project) { create(:project) } + let_it_be(:user) { project.namespace.owner } + + let(:execute_worker) { subject.perform(user.id) } + it 'is labeled as low urgency' do expect(described_class.get_urgency).to eq(:low) end - it_behaves_like "refreshes user's project authorizations" + it_behaves_like 'worker with data consistency', + described_class, + data_consistency: :delayed + + describe '#perform' do + it 'checks if a project_authorization refresh is needed for the user' do + expect(AuthorizedProjectUpdate::FindRecordsDueForRefreshService).to( + receive(:new).with(user).and_call_original) + + execute_worker + end + + context 'when there are project authorization records due for either removal or addition for a specific user' do + before do + user.project_authorizations.delete_all + end + + it 'enqueues a new project authorization update job for the user' do + expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id) + + execute_worker + end + + context 'setting `meta.caller_id` as `meta.related_class` in the context of the newly enqueued `UserRefreshWithLowUrgencyWorker` job' do + context 'when the `UserRefreshFromReplicaWorker` job has a `caller_id` set' do + it 'sets the same `caller_id` as `related_class`' do + expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id) do + expect(Gitlab::ApplicationContext.current).to include('meta.related_class' => 'Foo') + end + + Gitlab::ApplicationContext.with_context(caller_id: 'Foo') do + execute_worker + end + end + end + + context 'when the `UserRefreshFromReplicaWorker` job does not have a `caller_id` set' do + it 'does not set the value of `related_class`' do + expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).to receive(:perform_async).with(user.id) do + expect(Gitlab::ApplicationContext.current).not_to include('meta.related_class') + end + + execute_worker + end + end + end + end + + context 'when there are no additions or removals to be made to project authorizations for a specific user' do + it 'does not enqueue a new project authorization update job for the user' do + expect(AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker).not_to receive(:perform_async) + + execute_worker + end + end + + context 'when the feature flag `user_refresh_from_replica_worker_uses_replica_db` is disabled' do + before do + stub_feature_flags(user_refresh_from_replica_worker_uses_replica_db: false) + end + + context 'when load balancing is enabled' do + before do + allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true) + end + + it 'reads from the primary database' do + expect(Gitlab::Database::LoadBalancing::Session.current) + .to receive(:use_primary!) + + execute_worker + end + end + + it 'calls Users::RefreshAuthorizedProjectsService' do + source = 'AuthorizedProjectUpdate::UserRefreshFromReplicaWorker' + expect_next_instance_of(Users::RefreshAuthorizedProjectsService, user, { source: source }) do |service| + expect(service).to receive(:execute) + end + + execute_worker + end + end + end end |