Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0537e77587
commit
f5eabcfa0e
35 changed files with 452 additions and 64 deletions
|
@ -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"
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 & {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 } }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
# This worker will start reading data from the replica database soon
|
||||
# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/333219
|
||||
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
|
||||
|
||||
# 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
1
db/schema_migrations/20210627204936
Normal file
1
db/schema_migrations/20210627204936
Normal file
|
@ -0,0 +1 @@
|
|||
b37bf7db9c00c8f54c0ccca2d418f1279e12ff7e5b71347966494dc5645eb648
|
|
@ -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
|
||||
|
|
|
@ -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) |
|
||||
|
|
|
@ -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` | |
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,7 +8,7 @@ module API
|
|||
|
||||
before { authenticated_as_admin! }
|
||||
|
||||
feature_category :continuous_integration
|
||||
feature_category :pipeline_authoring
|
||||
|
||||
namespace 'admin' do
|
||||
namespace 'ci' do
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue