Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-01-11 18:10:43 +00:00
parent ec971a05e3
commit dcacb5daf7
76 changed files with 1416 additions and 163 deletions

View file

@ -112,7 +112,7 @@
.use-kaniko:
image:
name: gcr.io/kaniko-project/executor:debug-v0.20.0
name: gcr.io/kaniko-project/executor:debug-v1.3.0
entrypoint: [""]
before_script:
- source scripts/utils.sh

View file

@ -184,8 +184,8 @@ group :unicorn do
end
group :puma do
gem 'gitlab-puma', '~> 4.3.3.gitlab.2', require: false
gem 'gitlab-puma_worker_killer', '~> 0.1.1.gitlab.1', require: false
gem 'puma', '~> 5.1.1', require: false
gem 'puma_worker_killer', '~> 0.3.1', require: false
end
# State machine
@ -410,7 +410,7 @@ group :test do
gem 'rspec_profiling', '~> 0.0.6'
gem 'rspec-parameterized', require: false
gem 'capybara', '~> 3.33.0'
gem 'capybara', '~> 3.34.0'
gem 'capybara-screenshot', '~> 1.0.22'
gem 'selenium-webdriver', '~> 3.142'

View file

@ -152,7 +152,7 @@ GEM
bundler (>= 1.2.0, < 3)
thor (~> 0.18)
byebug (11.1.3)
capybara (3.33.0)
capybara (3.34.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
@ -447,11 +447,6 @@ GEM
gitlab-markup (1.7.1)
gitlab-net-dns (0.9.1)
gitlab-pg_query (1.3.1)
gitlab-puma (4.3.5.gitlab.3)
nio4r (~> 2.0)
gitlab-puma_worker_killer (0.1.1.gitlab.1)
get_process_mem (~> 0.2)
gitlab-puma (>= 2.7, < 5)
gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5)
gitlab-styles (5.4.0)
@ -876,6 +871,11 @@ GEM
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (4.0.6)
puma (5.1.1)
nio4r (~> 2.0)
puma_worker_killer (0.3.1)
get_process_mem (~> 0.2)
puma (>= 2.7)
pyu-ruby-sasl (0.0.3.3)
raabro (1.1.6)
racc (1.5.2)
@ -1301,7 +1301,7 @@ DEPENDENCIES
browser (~> 4.2)
bullet (~> 6.1.0)
bundler-audit (~> 0.6.1)
capybara (~> 3.33.0)
capybara (~> 3.34.0)
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
charlock_holmes (~> 0.7.7)
@ -1365,8 +1365,6 @@ DEPENDENCIES
gitlab-mail_room (~> 0.0.8)
gitlab-markup (~> 1.7.1)
gitlab-net-dns (~> 0.9.1)
gitlab-puma (~> 4.3.3.gitlab.2)
gitlab-puma_worker_killer (~> 0.1.1.gitlab.1)
gitlab-sidekiq-fetcher (= 0.5.2)
gitlab-styles (~> 5.4.0)
gitlab_chronic_duration (~> 0.10.6.2)
@ -1460,6 +1458,8 @@ DEPENDENCIES
prometheus-client-mmap (~> 0.12.0)
pry-byebug (~> 3.9.0)
pry-rails (~> 0.3.9)
puma (~> 5.1.1)
puma_worker_killer (~> 0.3.1)
rack (~> 2.2.3)
rack-attack (~> 6.3.0)
rack-cors (~> 1.0.6)

View file

@ -18,7 +18,7 @@ import {
GlTooltipDirective,
GlSafeHtmlDirective as SafeHtml,
} from '@gitlab/ui';
import deployBoardSvg from 'empty_states/icons/_deploy_board.svg';
import deployBoardSvg from '@gitlab/svgs/dist/illustrations/deploy-boards.svg';
import instanceComponent from '~/vue_shared/components/deployment_instance.vue';
import { n__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';

View file

@ -22,7 +22,7 @@ const handleStartupEvents = () => {
/* For `waitForCSSLoaded` methods, see docs.gitlab.com/ee/development/fe_guide/performance.html#important-considerations */
export const waitForCSSLoaded = (action = () => {}) => {
if (!gon?.features?.startupCss || allLinksLoaded()) {
if (allLinksLoaded()) {
return new Promise((resolve) => {
action();
resolve();

View file

@ -0,0 +1,94 @@
<script>
import { GlIcon, GlSprintf, GlLink } from '@gitlab/ui';
import { __ } from '~/locale';
import mrEventHub from '../eventhub';
const CLASSES = {
opened: 'status-box-open',
closed: 'status-box-mr-closed',
merged: 'status-box-mr-merged',
};
const STATUS = {
opened: [__('Open'), 'issue-open-m'],
closed: [__('Closed'), 'close'],
merged: [__('Merged'), 'git-merge'],
};
export default {
components: {
GlIcon,
GlSprintf,
GlLink,
},
props: {
initialState: {
type: String,
required: true,
},
initialIsReverted: {
type: Boolean,
required: true,
},
initialRevertedPath: {
type: String,
required: false,
default: null,
},
},
data() {
return {
state: this.initialState,
isReverted: this.initialIsReverted,
revertedPath: this.initialRevertedPath,
};
},
computed: {
statusBoxClass() {
return CLASSES[this.state];
},
statusHumanName() {
return STATUS[this.state][0];
},
statusIconName() {
return STATUS[this.state][1];
},
},
created() {
mrEventHub.$on('mr.state.updated', this.updateState);
},
beforeDestroy() {
mrEventHub.$off('mr.state.updated', this.updateState);
},
methods: {
updateState({ state, reverted, revertedPath }) {
this.state = state;
this.reverted = reverted;
this.revertedPath = revertedPath;
},
},
};
</script>
<template>
<div :class="statusBoxClass" class="issuable-status-box status-box">
<gl-icon
:name="statusIconName"
class="gl-display-block gl-display-sm-none!"
data-testid="status-icon"
/>
<span class="gl-display-none gl-display-sm-block">
<gl-sprintf v-if="isReverted" :message="__('Merged (%{linkStart}reverted%{linkEnd})')">
<template #link="{ content }">
<gl-link
:href="revertedPath"
class="gl-reset-color! gl-text-decoration-underline"
data-testid="reverted-link"
>{{ content }}</gl-link
>
</template>
</gl-sprintf>
<template v-else>{{ statusHumanName }}</template>
</span>
</div>
</template>

View file

@ -0,0 +1,3 @@
import createEventHub from '~/helpers/event_hub_factory';
export default createEventHub();

View file

@ -1,12 +1,14 @@
import Vue from 'vue';
import ZenMode from '~/zen_mode';
import initIssuableSidebar from '~/init_issuable_sidebar';
import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import { handleLocationHash } from '~/lib/utils/common_utils';
import { handleLocationHash, parseBoolean } from '~/lib/utils/common_utils';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
import initSourcegraph from '~/sourcegraph';
import loadAwardsHandler from '~/awards_handler';
import initInviteMemberTrigger from '~/invite_member/init_invite_member_trigger';
import initInviteMemberModal from '~/invite_member/init_invite_member_modal';
import StatusBox from '~/merge_request/components/status_box.vue';
export default function () {
new ZenMode(); // eslint-disable-line no-new
@ -18,4 +20,19 @@ export default function () {
loadAwardsHandler();
initInviteMemberModal();
initInviteMemberTrigger();
const el = document.querySelector('.js-mr-status-box');
// eslint-disable-next-line no-new
new Vue({
el,
render(h) {
return h(StatusBox, {
props: {
initialState: el.dataset.state,
initialIsReverted: parseBoolean(el.dataset.isReverted),
initialRevertedPath: el.dataset.revertedPath,
},
});
},
});
}

View file

@ -1,5 +1,6 @@
import { format } from 'timeago.js';
import getStateKey from 'ee_else_ce/vue_merge_request_widget/stores/get_state_key';
import mrEventHub from '~/merge_request/eventhub';
import { stateKey } from './state_maps';
import { formatDate } from '../../lib/utils/datetime_utility';
import { MTWPS_MERGE_STRATEGY, MT_MERGE_STRATEGY, MWPS_MERGE_STRATEGY } from '../constants';
@ -154,6 +155,12 @@ export default class MergeRequestStore {
this.canRevertInCurrentMR = currentUser.can_revert_on_current_merge_request || false;
this.setState();
mrEventHub.$emit('mr.state.updated', {
state: this.mergeRequestState,
reverted: data.reverted,
reverted_path: data.revertedPath,
});
}
setGraphqlData(project) {

View file

@ -8,6 +8,8 @@ class Import::BulkImportsController < ApplicationController
feature_category :importers
POLLING_INTERVAL = 3_000
rescue_from BulkImports::Clients::Http::ConnectionError, with: :bulk_import_connection_error
def configure
@ -34,6 +36,12 @@ class Import::BulkImportsController < ApplicationController
render json: :ok
end
def realtime_changes
Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL)
render json: current_user_bulk_imports.to_json(only: [:id], methods: [:status_name])
end
private
def serialized_importable_data
@ -152,4 +160,8 @@ class Import::BulkImportsController < ApplicationController
def sanitized_filter_param
@filter ||= sanitize(params[:filter])&.downcase
end
def current_user_bulk_imports
current_user.bulk_imports.gitlab
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Projects::MattermostsController < Projects::ApplicationController
include TriggersHelper
include Ci::TriggersHelper
include ActionView::Helpers::AssetUrlHelper
layout 'project_settings'

View file

@ -4,8 +4,6 @@ require 'digest/md5'
require 'uri'
module ApplicationHelper
include StartupCssHelper
# See https://docs.gitlab.com/ee/development/ee_features.html#code-in-appviews
# rubocop: disable CodeReuse/ActiveRecord
# We allow partial to be nil so that collection views can be passed in
@ -250,11 +248,7 @@ module ApplicationHelper
end
def stylesheet_link_tag_defer(path)
if use_startup_css?
stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil)
else
stylesheet_link_tag(path, media: "all")
end
stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil)
end
def outdated_browser?

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
module TriggersHelper
module Ci::TriggersHelper
def builds_trigger_url(project_id, ref: nil)
if ref.nil?
"#{Settings.gitlab.url}/api/v4/projects/#{project_id}/trigger/pipeline"

View file

@ -1,7 +0,0 @@
# frozen_string_literal: true
module StartupCssHelper
def use_startup_css?
(Feature.enabled?(:startup_css) || params[:startup_css] == 'true' || cookies['startup_css'] == 'true') && !Rails.env.test?
end
end

View file

@ -0,0 +1,38 @@
# frozen_string_literal: true
class Ci::CommitWithPipeline < SimpleDelegator
include Presentable
def initialize(commit)
@latest_pipelines = {}
super(commit)
end
def pipelines
project.ci_pipelines.where(sha: sha)
end
def last_pipeline
strong_memoize(:last_pipeline) do
pipelines.last
end
end
def latest_pipeline(ref = nil)
@latest_pipelines.fetch(ref) do |ref|
@latest_pipelines[ref] = latest_pipeline_for_project(ref, project)
end
end
def latest_pipeline_for_project(ref, pipeline_project)
pipeline_project.ci_pipelines.latest_pipeline_per_commit(id, ref)[id]
end
def set_latest_pipeline_for_ref(ref, pipeline)
@latest_pipelines[ref] = pipeline
end
def status(ref = nil)
latest_pipeline(ref)&.status
end
end

View file

@ -148,7 +148,7 @@ class Commit
to: :with_pipeline
def with_pipeline
@with_pipeline ||= CommitWithPipeline.new(self)
@with_pipeline ||= Ci::CommitWithPipeline.new(self)
end
def id

View file

@ -80,9 +80,9 @@ class CommitStatus < ApplicationRecord
merge(or_conditions)
end
# We use `Enums::CommitStatus.failure_reasons` here so that EE can more easily
# We use `Enums::Ci::CommitStatus.failure_reasons` here so that EE can more easily
# extend this `Hash` with new values.
enum_with_nil failure_reason: Enums::CommitStatus.failure_reasons
enum_with_nil failure_reason: Enums::Ci::CommitStatus.failure_reasons
##
# We still create some CommitStatuses outside of CreatePipelineService.

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
class CommitWithPipeline < SimpleDelegator
class Ci::CommitWithPipeline < SimpleDelegator
include Presentable
def initialize(commit)

View file

@ -0,0 +1,36 @@
# frozen_string_literal: true
module Enums
module Ci
module CommitStatus
# Returns the Hash to use for creating the `failure_reason` enum for
# `CommitStatus`.
def self.failure_reasons
{
unknown_failure: nil,
script_failure: 1,
api_failure: 2,
stuck_or_timeout_failure: 3,
runner_system_failure: 4,
missing_dependency_failure: 5,
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8,
archived_failure: 9,
unmet_prerequisites: 10,
scheduler_failure: 11,
data_integrity_failure: 12,
forward_deployment_failure: 13,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
bridge_pipeline_is_child_pipeline: 1_006, # not used anymore, but cannot be deleted because of old data
downstream_pipeline_creation_failed: 1_007,
secrets_provider_not_found: 1_008,
reached_max_descendant_pipelines_depth: 1_009
}
end
end
end
end
Enums::Ci::CommitStatus.prepend_if_ee('EE::Enums::Ci::CommitStatus')

View file

@ -1,35 +0,0 @@
# frozen_string_literal: true
module Enums
module CommitStatus
# Returns the Hash to use for creating the `failure_reason` enum for
# `CommitStatus`.
def self.failure_reasons
{
unknown_failure: nil,
script_failure: 1,
api_failure: 2,
stuck_or_timeout_failure: 3,
runner_system_failure: 4,
missing_dependency_failure: 5,
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8,
archived_failure: 9,
unmet_prerequisites: 10,
scheduler_failure: 11,
data_integrity_failure: 12,
forward_deployment_failure: 13,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
bridge_pipeline_is_child_pipeline: 1_006, # not used anymore, but cannot be deleted because of old data
downstream_pipeline_creation_failed: 1_007,
secrets_provider_not_found: 1_008,
reached_max_descendant_pipelines_depth: 1_009
}
end
end
end
Enums::CommitStatus.prepend_if_ee('EE::Enums::CommitStatus')

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class MattermostSlashCommandsService < SlashCommandsService
include TriggersHelper
include Ci::TriggersHelper
prop_accessor :token

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class SlackSlashCommandsService < SlashCommandsService
include TriggersHelper
include Ci::TriggersHelper
def title
'Slack slash commands'

View file

@ -114,6 +114,14 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity
end
end
expose :reverted do |merge_request|
merge_request.reverted_by_merge_request?(current_user)
end
expose :reverted_path, if: -> (mr) { mr.reverted_by_merge_request?(current_user) } do |merge_request|
merge_request_path(merge_request.reverting_merge_request(current_user))
end
private
delegate :current_user, to: :request

View file

@ -158,7 +158,9 @@ class IssuableBaseService < BaseService
after_create(issuable)
execute_hooks(issuable)
invalidate_cache_counts(issuable, users: issuable.assignees)
users_to_invalidate = issuable.allows_reviewers? ? issuable.assignees | issuable.reviewers : issuable.assignees
invalidate_cache_counts(issuable, users: users_to_invalidate)
issuable.update_project_counter_caches
end

View file

@ -43,8 +43,6 @@
= stylesheet_link_tag_defer "application"
= yield :page_specific_styles
= stylesheet_link_tag_defer "application_utilities"
- unless use_startup_css?
= stylesheet_link_tag_defer "themes/#{user_application_theme_css_filename}" if user_application_theme_css_filename
= stylesheet_link_tag "disable_animations", media: "all" if Rails.env.test? || Gitlab.config.gitlab['disable_animations']
= stylesheet_link_tag_defer "highlight/themes/#{user_color_scheme}"

View file

@ -1,5 +1,3 @@
- return unless use_startup_css?
- startup_filename = current_path?("sessions#new") ? 'signin' : user_application_theme == 'gl-dark' ? 'dark' : 'general'
%style{ type: "text/css" }

View file

@ -1,5 +1,3 @@
- return unless use_startup_css?
= javascript_tag do
:plain
document.querySelectorAll('link[media="print"]').forEach(linkTag => {

View file

@ -2,7 +2,7 @@
- can_edit = can?(current_user, :admin_project, @project)
.dropdown.btn-group
%button.btn.rounded-right.text-center{ class: ('has-tooltip' if type == :icon), title: (_('Import issues') if type == :icon),
%button.btn.gl-button.rounded-right.text-center{ class: ('has-tooltip' if type == :icon), title: (_('Import issues') if type == :icon),
data: { toggle: 'dropdown', qa_selector: 'import_issues_button' }, 'aria-label' => _('Import issues'), 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
- if type == :icon
= sprite_icon('import')

View file

@ -3,6 +3,8 @@
- can_reopen_merge_request = can?(current_user, :reopen_merge_request, @merge_request)
- state_human_name, state_icon_name = state_name_with_icon(@merge_request)
- are_close_and_open_buttons_hidden = merge_request_button_hidden?(@merge_request, true) && merge_request_button_hidden?(@merge_request, false)
- is_reverted = @merge_request.reverted_by_merge_request?(current_user)
- reverted_mr_path = is_reverted ? merge_request_path(@merge_request.reverting_merge_request(current_user)) : nil
- if @merge_request.closed_or_merged_without_fork?
.gl-alert.gl-alert-danger.gl-mb-5
@ -12,11 +14,11 @@
.detail-page-header.border-bottom-0.pt-0.pb-0
.detail-page-header-body
.issuable-status-box.status-box{ class: status_box_class(@merge_request) }
= sprite_icon(state_icon_name, css_class: 'd-block d-sm-none')
%span.d-none.d-sm-block
.issuable-status-box.status-box.js-mr-status-box{ class: status_box_class(@merge_request), data: { state: @merge_request.state, is_reverted: is_reverted.to_s, reverted_path: reverted_mr_path } }
= sprite_icon(state_icon_name, css_class: 'gl-display-block gl-display-sm-none!')
%span.gl-display-none.gl-display-sm-block
- if @merge_request.reverted_by_merge_request?(current_user)
= _('Merged (%{reverted})').html_safe % { reverted: link_to(s_('MergeRequest|reverted'), merge_request_path(@merge_request.reverting_merge_request(current_user)), class: 'gl-reset-color! gl-text-decoration-underline') }
= _('Merged (%{reverted})').html_safe % { reverted: link_to(s_('MergeRequest|reverted'), reverted_mr_path, class: 'gl-reset-color! gl-text-decoration-underline') }
- else
= state_human_name

View file

@ -1 +0,0 @@
<svg width="55" height="44" viewBox="0 0 55 44" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g transform="translate(1.488 .803)"><rect stroke="#E5E5E5" stroke-width="1.6" fill="#FFF" width="42" height="42" rx="4"/><rect stroke="#E7E7E7" stroke-width="1.6" fill="#FFF" x="6" y="12" width="7.171" height="24" rx="2"/><rect stroke="#B5A7DD" stroke-width="1.6" fill="#FFF" x="6" y="5" width="32.17" height="3.5" rx="1.75"/><rect stroke="#FDE5D8" stroke-width="1.6" fill="#FFF" x="17" y="12" width="21.17" height="24" rx="2"/><rect fill="#E52C5A" x="19.74" y="14.197" width="7.171" height="2.408" rx="1.204"/><rect fill="#E5E5E5" x="28.35" y="18.272" width="7.171" height="2.408" rx="1.204"/><rect fill="#E5E5E5" x="19.74" y="26.697" width="15.943" height="2.408" rx="1.204"/><rect fill="#E5E5E5" x="19.74" y="30.697" width="15.943" height="2.408" rx="1.204"/><rect fill="#E52C5A" x="21.911" y="21.272" width="4.78" height="2.408" rx="1.204"/><rect fill="#FC8A51" x="28.472" y="22.429" width="7.171" height="2.408" rx="1.204"/><rect fill="#FDE5D8" x="26.691" y="8.429" width="2.39" height="2.408" rx="1.195"/><rect fill="#FF8340" x="8.512" y="14.85" width="2.39" height="2.408" rx="1.195"/><rect fill="#E52C5A" x="8.512" y="19.197" width="2.39" height="2.408" rx="1.195"/><rect fill="#FF8340" x="8.512" y="31.197" width="2.39" height="2.408" rx="1.195"/><rect fill="#E7E7E7" x="8.512" y="27.197" width="2.39" height="2.408" rx="1.195"/><rect fill="#B5A7DD" x="8.512" y="23.197" width="2.39" height="2.408" rx="1.195"/></g><g transform="rotate(-45 33.371 -12.99)"><ellipse stroke="#6B4FBB" stroke-width="3.2" fill-opacity=".1" fill="#FFF" cx="11.951" cy="12.041" rx="11.951" ry="12.041"/><path d="M5.536 22.29c5.716 3.3 13.046 1.307 16.37-4.452 3.326-5.759 1.387-13.103-4.329-16.403" stroke="#6B4FBB" stroke-width="3.2" fill-opacity=".3" fill="#FFF"/><rect fill="#6B4FBB" x="9.561" y="23.279" width="4.78" height="13.646" rx="2.39"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,5 @@
---
title: Add DAST.latest.gitlab-ci.yml
merge_request: 50539
author:
type: added

View file

@ -0,0 +1,6 @@
---
title: Populate `finding_uuid` attribute for the existing `vulnerability_feedback`
records
merge_request: 49807
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Invalidate reviews counter cache when MR gets created
merge_request: 51316
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Forbid snippet pushes when repo is read-only
merge_request: 51318
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Update merge request status box without reloading page
merge_request: 50761
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Prevent rebase from being run in quick action when there are conflicts
merge_request: 51243
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Update puma & puma_worker_killer to upstream
merge_request: 48897
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Fix import issues button style
merge_request: 50969
author: Yogi (@yo)
type: fixed

View file

@ -1,8 +0,0 @@
---
name: startup_css
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39713
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/238718
milestone: '13.3'
type: development
group: group::editor
default_enabled: false

View file

@ -0,0 +1,8 @@
---
name: variable_inside_variable
introduced_by_url:
rollout_issue_url:
milestone: '13.7'
type: development
group: group::runner
default_enabled: false

View file

@ -79,6 +79,11 @@ tag 'gitlab-puma-worker'
#
worker_timeout 60
# https://github.com/puma/puma/blob/master/5.0-Upgrade.md#lower-latency-better-throughput
if defined?(wait_for_less_busy_worker)
wait_for_less_busy_worker ENV.fetch('PUMA_WAIT_FOR_LESS_BUSY_WORKER', 0.001).to_f
end
# Use json formatter
require_relative "/home/git/gitlab/lib/gitlab/puma_logging/json_formatter"

View file

@ -69,10 +69,15 @@ tag 'gitlab-puma-worker'
#
worker_timeout 60
# https://github.com/puma/puma/blob/master/5.0-Upgrade.md#lower-latency-better-throughput
if defined?(wait_for_less_busy_worker)
wait_for_less_busy_worker ENV.fetch('PUMA_WAIT_FOR_LESS_BUSY_WORKER', 0.001).to_f
end
# Use json formatter
require_relative "/home/git/gitlab/lib/gitlab/puma_logging/json_formatter"
json_formatter = Gitlab::PumaLogging::JSONFormatter.new
log_formatter do |str|
json_formatter.call(str)
end
end

View file

@ -79,6 +79,11 @@ tag 'gitlab-actioncable-puma-worker'
#
worker_timeout 60
# https://github.com/puma/puma/blob/master/5.0-Upgrade.md#lower-latency-better-throughput
if defined?(wait_for_less_busy_worker)
wait_for_less_busy_worker ENV.fetch('PUMA_WAIT_FOR_LESS_BUSY_WORKER', 0.001).to_f
end
# Use json formatter
require_relative "/home/git/gitlab/lib/gitlab/puma_logging/json_formatter"

View file

@ -63,6 +63,7 @@ namespace :import do
resource :bulk_imports, only: [:create] do
post :configure
get :status
get :realtime_changes
end
resource :manifest, only: [:create, :new], controller: :manifest do

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
class SchedulePopulateFindingUuidForVulnerabilityFeedback < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION_CLASS = 'PopulateFindingUuidForVulnerabilityFeedback'
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 1000
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(
Gitlab::BackgroundMigration::PopulateFindingUuidForVulnerabilityFeedback::VulnerabilityFeedback,
MIGRATION_CLASS,
DELAY_INTERVAL,
batch_size: BATCH_SIZE
)
end
def down
# no-op
end
end

View file

@ -0,0 +1 @@
5117b71950bec3c6c746eaf4851c111a335bf2280075ddc2c73b60a30e23d907

View file

@ -58,6 +58,7 @@ exceptions:
- GPL
- GUI
- HAML
- HEAD
- HIPAA
- HTML
- HTTP

View file

@ -867,6 +867,26 @@ understand the implications.
WARNING:
This is a destructive operation.
When you run `registry-garbage-collect` with the -m flag, garbage collection unlinks manifests that
are part of a multi-arch manifest, unless they're tagged in the same repository.
See [this issue](https://gitlab.com/gitlab-org/container-registry/-/issues/149) for details.
To work around this issue, instead of:
```plaintext
myrepo/multiarchmanifest:latest
myrepo/manifest/amd-64:latest
myrepo/manifest/arm:latest
```
Use:
```plaintext
myrepo/multiarchmanifest:latest
myrepo/manifest:amd-64-latest
myrepo/manifest:arm-latest
```
The GitLab Container Registry follows the same default workflow as Docker Distribution:
retain untagged manifests and all layers, even ones that are not referenced directly. All content
can be accessed by using context addressable identifiers.

View file

@ -14,10 +14,8 @@ Some of the predefined environment variables are available only if a minimum
version of [GitLab Runner](https://docs.gitlab.com/runner/) is used. Consult the table below to find the
version of GitLab Runner that's required.
NOTE:
Starting with GitLab 9.0, we have deprecated some variables. Read the
[9.0 Renaming](deprecated_variables.md#gitlab-90-renamed-variables) section to find out their replacements.
**To avoid problems with deprecated and removed variables in future releases, you are strongly advised to use the new variables.**
In GitLab 9.0, some [variable were deprecated](deprecated_variables.md#gitlab-90-renamed-variables).
You should ensure you are using the latest variables.
You can add a command to your `.gitlab-ci.yml` file to
[output the values of all variables available for a job](README.md#list-all-environment-variables).
@ -37,28 +35,28 @@ Kubernetes-specific environment variables are detailed in the
| `CI_COMMIT_MESSAGE` | 10.8 | all | The full commit message. |
| `CI_COMMIT_REF_NAME` | 9.0 | all | The branch or tag name for which project is built |
| `CI_COMMIT_REF_PROTECTED` | 11.11 | all | `true` if the job is running on a protected reference, `false` if not |
| `CI_COMMIT_REF_SLUG` | 9.0 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. No leading / trailing `-`. Use in URLs, host names and domain names. |
| `CI_COMMIT_REF_SLUG` | 9.0 | all | `$CI_COMMIT_REF_NAME` in lowercase, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. No leading / trailing `-`. Use in URLs, host names and domain names. |
| `CI_COMMIT_SHA` | 9.0 | all | The commit revision for which project is built |
| `CI_COMMIT_SHORT_SHA` | 11.7 | all | The first eight characters of `CI_COMMIT_SHA` |
| `CI_COMMIT_BRANCH` | 12.6 | 0.5 | The commit branch name. Present in branch pipelines, including pipelines for the default branch. Not present in merge request pipelines or tag pipelines. |
| `CI_COMMIT_TAG` | 9.0 | 0.5 | The commit tag name. Present only when building tags. |
| `CI_COMMIT_TITLE` | 10.8 | all | The title of the commit - the full first line of the message |
| `CI_COMMIT_TIMESTAMP` | 13.4 | all | The timestamp of the commit in the ISO 8601 format. |
| `CI_CONCURRENT_ID` | all | 11.10 | Unique ID of build execution within a single executor. |
| `CI_CONCURRENT_PROJECT_ID` | all | 11.10 | Unique ID of build execution within a single executor and project. |
| `CI_CONCURRENT_ID` | all | 11.10 | Unique ID of build execution in a single executor. |
| `CI_CONCURRENT_PROJECT_ID` | all | 11.10 | Unique ID of build execution in a single executor and project. |
| `CI_CONFIG_PATH` | 9.4 | 0.5 | The path to CI configuration file. Defaults to `.gitlab-ci.yml` |
| `CI_DEBUG_TRACE` | all | 1.7 | Whether [debug logging (tracing)](README.md#debug-logging) is enabled |
| `CI_DEFAULT_BRANCH` | 12.4 | all | The name of the default branch for the project. |
| `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` | 13.7 | all | The image prefix for pulling images through the Dependency Proxy. |
| `CI_DEPENDENCY_PROXY_SERVER` | 13.7 | all | The server for logging in to the Dependency Proxy. This is equivelant to `$CI_SERVER_HOST:$CI_SERVER_PORT`. |
| `CI_DEPENDENCY_PROXY_PASSWORD` | 13.7 | all | The password to use to pull images through the Dependency Proxy. |
| `CI_DEPENDENCY_PROXY_USER` | 13.7 | all | The username to use to pull images through the Dependency Proxy. |
| `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` | 13.7 | all | The image prefix for pulling images through the Dependency Proxy. |
| `CI_DEPENDENCY_PROXY_SERVER` | 13.7 | all | The server for logging in to the Dependency Proxy. This is equivalent to `$CI_SERVER_HOST:$CI_SERVER_PORT`. |
| `CI_DEPENDENCY_PROXY_PASSWORD` | 13.7 | all | The password to use to pull images through the Dependency Proxy. |
| `CI_DEPENDENCY_PROXY_USER` | 13.7 | all | The username to use to pull images through the Dependency Proxy. |
| `CI_DEPLOY_FREEZE` | 13.2 | all | Included with the value `true` if the pipeline runs during a [deploy freeze window](../../user/project/releases/index.md#prevent-unintentional-releases-by-setting-a-deploy-freeze). |
| `CI_DEPLOY_PASSWORD` | 10.8 | all | Authentication password of the [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token), only present if the Project has one related. |
| `CI_DEPLOY_USER` | 10.8 | all | Authentication username of the [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token), only present if the Project has one related. |
| `CI_DISPOSABLE_ENVIRONMENT` | all | 10.1 | Marks that the job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. |
| `CI_ENVIRONMENT_NAME` | 8.15 | all | The name of the environment for this job. Only present if [`environment:name`](../yaml/README.md#environmentname) is set. |
| `CI_ENVIRONMENT_SLUG` | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. Only present if [`environment:name`](../yaml/README.md#environmentname) is set. |
| `CI_ENVIRONMENT_SLUG` | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, and so on. Only present if [`environment:name`](../yaml/README.md#environmentname) is set. |
| `CI_ENVIRONMENT_URL` | 9.3 | all | The URL of the environment for this job. Only present if [`environment:url`](../yaml/README.md#environmenturl) is set. |
| `CI_EXTERNAL_PULL_REQUEST_IID` | 12.3 | all | Pull Request ID from GitHub if the [pipelines are for external pull requests](../ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests). Available only if `only: [external_pull_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the pull request is open. |
| `CI_EXTERNAL_PULL_REQUEST_SOURCE_REPOSITORY` | 13.3 | all | The source repository name of the pull request if [the pipelines are for external pull requests](../ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests). Available only if `only: [external_pull_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the pull request is open. |
@ -85,9 +83,9 @@ Kubernetes-specific environment variables are detailed in the
| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `namespace/awesome-project`). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `http://192.168.10.15:3000/namespace/awesome-project`). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). (e.g. `refs/merge-requests/1/head`). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (for example `namespace/awesome-project`). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (for example `http://192.168.10.15:3000/namespace/awesome-project`). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). (for example `refs/merge-requests/1/head`). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the source branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used, the merge request is created, and the pipeline is a [merged result pipeline](../merge_request_pipelines/pipelines_for_merged_results/index.md). **(PREMIUM)** |
| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
@ -111,12 +109,12 @@ Kubernetes-specific environment variables are detailed in the
| `CI_PROJECT_CONFIG_PATH` | 13.8 | all | The CI configuration path for the project
| `CI_PROJECT_DIR` | all | all | The full path where the repository is cloned and where the job is run. If the GitLab Runner `builds_dir` parameter is set, this variable is set relative to the value of `builds_dir`. For more information, see [Advanced configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) for GitLab Runner. |
| `CI_PROJECT_ID` | all | all | The unique ID of the current project that GitLab CI/CD uses internally |
| `CI_PROJECT_NAME` | 8.10 | 0.5 | The name of the directory for the project that is currently being built. For example, if the project URL is `gitlab.example.com/group-name/project-1`, the `CI_PROJECT_NAME` would be `project-1`. |
| `CI_PROJECT_NAMESPACE` | 8.10 | 0.5 | The project namespace (username or group name) that is currently being built |
| `CI_PROJECT_ROOT_NAMESPACE` | 13.2 | 0.5 | The **root** project namespace (username or group name) that is currently being built. For example, if `CI_PROJECT_NAMESPACE` is `root-group/child-group/grandchild-group`, `CI_PROJECT_ROOT_NAMESPACE` would be `root-group`. |
| `CI_PROJECT_NAME` | 8.10 | 0.5 | The name of the directory for the project that is being built. For example, if the project URL is `gitlab.example.com/group-name/project-1`, the `CI_PROJECT_NAME` would be `project-1`. |
| `CI_PROJECT_NAMESPACE` | 8.10 | 0.5 | The project namespace (username or group name) that is being built |
| `CI_PROJECT_ROOT_NAMESPACE` | 13.2 | 0.5 | The **root** project namespace (username or group name) that is being built. For example, if `CI_PROJECT_NAMESPACE` is `root-group/child-group/grandchild-group`, `CI_PROJECT_ROOT_NAMESPACE` would be `root-group`. |
| `CI_PROJECT_PATH` | 8.10 | 0.5 | The namespace with project name |
| `CI_PROJECT_PATH_SLUG` | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
| `CI_PROJECT_REPOSITORY_LANGUAGES` | 12.3 | all | Comma-separated, lowercased list of the languages used in the repository (e.g. `ruby,javascript,html,css`) |
| `CI_PROJECT_PATH_SLUG` | 9.3 | all | `$CI_PROJECT_PATH` in lowercase and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
| `CI_PROJECT_REPOSITORY_LANGUAGES` | 12.3 | all | Comma-separated, lowercase list of the languages used in the repository (for example `ruby,javascript,html,css`) |
| `CI_PROJECT_TITLE` | 12.4 | all | The human-readable project name as displayed in the GitLab web interface. |
| `CI_PROJECT_URL` | 8.10 | 0.5 | The HTTP(S) address to access project |
| `CI_PROJECT_VISIBILITY` | 10.3 | all | The project visibility (internal, private, public) |

View file

@ -2275,6 +2275,58 @@ job3:
- deploy_to_staging
```
#### `allow_failure:exit_codes`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/273157) in GitLab 13.8.
> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-allow_failureexit_codes). **(CORE ONLY)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
Use `allow_failure:exit_codes` to dynamically control if a job should be allowed
to fail. You can list which exit codes are not considered failures. The job fails
for any other exit code:
```yaml
test_job_1:
script:
- echo "Run a script that results in exit code 1. This job fails."
- exit 1
allow_failure:
exit_codes: 137
test_job_2:
script:
- echo "Run a script that results in exit code 137. This job is allowed to fail."
- exit 137
allow_failure:
exit_codes:
- 137
- 255
```
##### Enable or disable `allow_failure:exit_codes` **(CORE ONLY)**
`allow_failure:exit_codes` is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:ci_allow_failure_with_exit_codes)
```
To disable it:
```ruby
Feature.disable(:ci_allow_failure_with_exit_codes)
```
### `when`
`when` is used to implement jobs that are run in case of failure or despite the

View file

@ -86,6 +86,20 @@ variables:
DAST_WEBSITE: https://example.com
```
### Latest template
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254325) in GitLab 13.8
To use the latest version of the DAST template, include
`DAST.latest.gitlab-ci.yml` instead of `DAST.gitlab-ci.yml`.
See the CI [docs](../../../development/cicd/templates.md#latest-version)
on template versioning for more information.
Please note that the latest version may include breaking changes. Check the
[DAST troubleshooting guide](#troubleshooting) if you experience problems.
### Template options
There are two ways to define the URL to be scanned by DAST:
1. Set the `DAST_WEBSITE` [variable](../../../ci/yaml/README.md#variables).
@ -1040,6 +1054,25 @@ If your DAST job exceeds the job timeout and you need to reduce the scan duratio
For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload).
### Getting error `dast job: chosen stage does not exist` when including DAST CI template
Newer versions of the DAST CI template do not define stages in order to avoid
overwriting stages from other CI files. If you've recently started using
`DAST.latest.gitlab-ci.yml` or upgraded to a new major release of GitLab and
began receiving this error, you will need to define a `dast` stage with your
other stages. Please note that you must have a running application for DAST to
scan. If your application is set up in your pipeline, it must be deployed
in a stage _before_ the `dast` stage:
```yaml
stages:
- deploy # DAST needs a running application to scan
- dast
include:
- template: DAST.latest.gitlab-ci.yml
```
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues

View file

@ -16,6 +16,12 @@ module ExpandVariables
end
end
def possible_var_reference?(value)
return unless value
%w[$ %].any? { |symbol| value.include?(symbol) }
end
private
def replace_with(value, variables)

View file

@ -0,0 +1,128 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# This class populates the `finding_uuid` attribute for
# the existing `vulnerability_feedback` records.
class PopulateFindingUuidForVulnerabilityFeedback
REPORT_TYPES = {
sast: 0,
dependency_scanning: 1,
container_scanning: 2,
dast: 3,
secret_detection: 4,
coverage_fuzzing: 5,
api_fuzzing: 6
}.freeze
class VulnerabilityFeedback < ActiveRecord::Base # rubocop:disable Style/Documentation
include EachBatch
self.table_name = 'vulnerability_feedback'
enum category: REPORT_TYPES
scope :in_range, -> (start, stop) { where(id: start..stop) }
scope :without_uuid, -> { where(finding_uuid: nil) }
def self.load_vulnerability_findings
all.to_a.tap { |collection| collection.each(&:vulnerability_finding) }
end
def set_finding_uuid
return unless vulnerability_finding.present? && vulnerability_finding.primary_identifier.present?
update_column(:finding_uuid, calculated_uuid)
rescue StandardError => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
end
def vulnerability_finding
BatchLoader.for(finding_key).batch(replace_methods: false) do |finding_keys, loader|
project_ids = finding_keys.map { |key| key[:project_id] }
categories = finding_keys.map { |key| key[:category] }
fingerprints = finding_keys.map { |key| key[:project_fingerprint] }
findings = Finding.with_primary_identifier.where(
project_id: project_ids.uniq,
report_type: categories.uniq,
project_fingerprint: fingerprints.uniq
).to_a
finding_keys.each do |finding_key|
loader.call(
finding_key,
findings.find { |f| finding_key == f.finding_key }
)
end
end
end
private
def calculated_uuid
Gitlab::UUID.v5(uuid_components)
end
def uuid_components
[
category,
vulnerability_finding.primary_identifier.fingerprint,
vulnerability_finding.location_fingerprint,
project_id
].join('-')
end
def finding_key
{
project_id: project_id,
category: category,
project_fingerprint: project_fingerprint
}
end
end
class Finding < ActiveRecord::Base # rubocop:disable Style/Documentation
include ShaAttribute
self.table_name = 'vulnerability_occurrences'
sha_attribute :project_fingerprint
sha_attribute :location_fingerprint
belongs_to :primary_identifier, class_name: 'Gitlab::BackgroundMigration::PopulateFindingUuidForVulnerabilityFeedback::Identifier'
enum report_type: REPORT_TYPES
scope :with_primary_identifier, -> { includes(:primary_identifier) }
def finding_key
{
project_id: project_id,
category: report_type,
project_fingerprint: project_fingerprint
}
end
end
class Identifier < ActiveRecord::Base # rubocop:disable Style/Documentation
self.table_name = 'vulnerability_identifiers'
end
def perform(*range)
feedback = VulnerabilityFeedback.without_uuid.in_range(*range).load_vulnerability_findings
feedback.each(&:set_finding_uuid)
log_info(feedback.count)
end
def log_info(feedback_count)
::Gitlab::BackgroundMigration::Logger.info(
migrator: self.class.name,
message: '`finding_uuid` attributes has been set',
count: feedback_count
)
end
end
end
end

View file

@ -0,0 +1,45 @@
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dast/
# Configure the scanning tool through the environment variables.
# List of the variables: https://docs.gitlab.com/ee/user/application_security/dast/#available-variables
# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
variables:
DAST_VERSION: 1
# Setting this variable will affect all Security templates
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
dast:
stage: dast
image:
name: "$SECURE_ANALYZERS_PREFIX/dast:$DAST_VERSION"
variables:
GIT_STRATEGY: none
allow_failure: true
script:
- export DAST_WEBSITE=${DAST_WEBSITE:-$(cat environment_url.txt)}
- if [ -z "$DAST_WEBSITE$DAST_API_SPECIFICATION" ]; then echo "Either DAST_WEBSITE or DAST_API_SPECIFICATION must be set. See https://docs.gitlab.com/ee/user/application_security/dast/#configuration for more details." && exit 1; fi
- /analyze
artifacts:
reports:
dast: gl-dast-report.json
rules:
- if: $DAST_DISABLED
when: never
- if: $DAST_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- if: $CI_DEFAULT_BRANCH != $CI_COMMIT_REF_NAME &&
$REVIEW_DISABLED && $DAST_WEBSITE == null &&
$DAST_API_SPECIFICATION == null
when: never
- if: $CI_COMMIT_BRANCH &&
$CI_KUBERNETES_ACTIVE &&
$GITLAB_FEATURES =~ /\bdast\b/
- if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdast\b/ &&
$DAST_WEBSITE
- if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdast\b/ &&
$DAST_API_SPECIFICATION

View file

@ -0,0 +1,77 @@
# frozen_string_literal: true
module Gitlab
module Ci
module Variables
class Collection
class Sorted
include TSort
include Gitlab::Utils::StrongMemoize
def initialize(variables)
@variables = variables
end
def valid?
errors.nil?
end
# errors sorts an array of variables, ignoring unknown variable references,
# and returning an error string if a circular variable reference is found
def errors
return if Feature.disabled?(:variable_inside_variable)
strong_memoize(:errors) do
# Check for cyclic dependencies and build error message in that case
errors = each_strongly_connected_component.filter_map do |component|
component.map { |v| v[:key] }.inspect if component.size > 1
end
"circular variable reference detected: #{errors.join(', ')}" if errors.any?
end
end
# sort sorts an array of variables, ignoring unknown variable references.
# If a circular variable reference is found, the original array is returned
def sort
return @variables if Feature.disabled?(:variable_inside_variable)
return @variables if errors
tsort
end
private
def tsort_each_node(&block)
@variables.each(&block)
end
def tsort_each_child(variable, &block)
each_variable_reference(variable[:value], &block)
end
def input_vars
strong_memoize(:input_vars) do
@variables.index_by { |env| env.fetch(:key) }
end
end
def walk_references(value)
return unless ExpandVariables.possible_var_reference?(value)
value.scan(ExpandVariables::VARIABLES_REGEXP) do |var_ref|
yield(input_vars, var_ref.first)
end
end
def each_variable_reference(value)
walk_references(value) do |vars_hash, ref_var_name|
variable = vars_hash.dig(ref_var_name)
yield variable if variable
end
end
end
end
end
end
end

View file

@ -59,7 +59,7 @@ module Gitlab
# TODO: Investigate if expanding actor/authentication types are needed.
# https://gitlab.com/gitlab-org/gitlab/issues/202190
if actor && !allowed_actor?
raise ForbiddenError, ERROR_MESSAGES[:authentication_mechanism]
raise ForbiddenError, error_message(:authentication_mechanism)
end
super
@ -71,14 +71,18 @@ module Gitlab
override :check_push_access!
def check_push_access!
raise ForbiddenError, ERROR_MESSAGES[:update_snippet] unless user
raise ForbiddenError, error_message(:update_snippet) unless user
if snippet&.repository_read_only?
raise ForbiddenError, error_message(:read_only)
end
check_change_access!
end
def check_snippet_accessibility!
if snippet.blank?
raise NotFoundError, ERROR_MESSAGES[:snippet_not_found]
raise NotFoundError, error_message(:snippet_not_found)
end
end
@ -94,14 +98,14 @@ module Gitlab
passed = guest_can_download_code? || user_can_download_code?
unless passed
raise ForbiddenError, ERROR_MESSAGES[:read_snippet]
raise ForbiddenError, error_message(:read_snippet)
end
end
override :check_change_access!
def check_change_access!
unless user_can_push?
raise ForbiddenError, ERROR_MESSAGES[:update_snippet]
raise ForbiddenError, error_message(:update_snippet)
end
check_size_before_push!

View file

@ -4,7 +4,6 @@
module Gitlab
module GonHelper
include StartupCssHelper
include WebpackHelper
def add_gon_variables
@ -49,9 +48,6 @@ module Gitlab
push_frontend_feature_flag(:usage_data_api, default_enabled: true)
push_frontend_feature_flag(:security_auto_fix, default_enabled: false)
push_frontend_feature_flag(:gl_tooltips, default_enabled: :yaml)
# Startup CSS feature is a special one as it can be enabled by means of cookies and params
gon.push({ features: { 'startupCss' => use_startup_css? } }, true)
end
# Exposes the state of a feature flag to the frontend code.

View file

@ -57,6 +57,11 @@ module Gitlab
access_check.can_push_to_branch?(merge_request.source_branch)
end
command :rebase do
if quick_action_target.cannot_be_merged?
@execution_message[:rebase] = _('This merge request cannot be rebased while there are conflicts.')
next
end
if quick_action_target.rebase_in_progress?
@execution_message[:rebase] = _('A rebase is already in progress.')
next

View file

@ -17670,6 +17670,9 @@ msgstr ""
msgid "Merged"
msgstr ""
msgid "Merged (%{linkStart}reverted%{linkEnd})"
msgstr ""
msgid "Merged (%{reverted})"
msgstr ""
@ -28835,6 +28838,9 @@ msgstr ""
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr ""
msgid "This merge request cannot be rebased while there are conflicts."
msgstr ""
msgid "This merge request does not have accessibility reports"
msgstr ""

View file

@ -45,7 +45,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.178.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "25.7.2",
"@gitlab/ui": "25.7.3",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Create' do
context 'Gitaly', :orchestrated, :mtls do
describe 'Using mTLS' do
let(:intial_commit_message) { 'Initial commit' }
let(:first_added_commit_message) { 'commit over git' }
let(:second_added_commit_message) { 'commit over api' }
it 'pushes to gitaly', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1118' do
project = Resource::Project.fabricate! do |project|
project.name = "mTLS"
project.initialize_with_readme = true
end
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.new_branch = false
push.commit_message = first_added_commit_message
push.file_content = 'First commit'
end
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = second_added_commit_message
commit.add_files([
{
file_path: "file-#{SecureRandom.hex(8)}",
content: 'Second commit'
}
])
end
expect(project.commits.map { |commit| commit[:message].chomp })
.to include(intial_commit_message)
.and include(first_added_commit_message)
.and include(second_added_commit_message)
end
end
end
end
end

View file

@ -149,6 +149,22 @@ RSpec.describe Import::BulkImportsController do
end
end
describe 'GET realtime_changes' do
let_it_be(:bulk_import) { create(:bulk_import, :created, user: user) }
it 'returns bulk imports created by current user' do
get :realtime_changes
expect(json_response).to eq([{ 'id' => bulk_import.id, 'status_name' => bulk_import.status_name.to_s }])
end
it 'sets a Poll-Interval header' do
get :realtime_changes
expect(response.headers['Poll-Interval']).to eq(Import::BulkImportsController::POLLING_INTERVAL.to_s)
end
end
describe 'POST create' do
let(:instance_url) { "http://fake-intance" }
let(:pat) { "fake-pat" }

View file

@ -259,7 +259,7 @@ RSpec.describe 'Dashboard Projects' do
# 4. ProjectsHelper#load_pipeline_status
# 5. RendersMemberAccess#preload_max_member_access_for_collection
# 6. User#max_member_access_for_project_ids
# 7. CommitWithPipeline#last_pipeline
# 7. Ci::CommitWithPipeline#last_pipeline
expect { visit dashboard_projects_path }.not_to exceed_query_limit(control_count + 7)
end

View file

@ -39,24 +39,7 @@ describe('waitForCSSLoaded', () => {
});
});
describe('with startup css disabled', () => {
gon.features = {
startupCss: false,
};
it('should invoke the action right away', async () => {
const events = waitForCSSLoaded(mockedCallback);
await events;
expect(mockedCallback).toHaveBeenCalledTimes(1);
});
});
describe('with startup css enabled', () => {
gon.features = {
startupCss: true,
};
it('should dispatch CSSLoaded when the assets are cached or already loaded', async () => {
setFixtures(`
<link href="one.css" data-startupcss="loaded">

View file

@ -0,0 +1,99 @@
import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
import { GlSprintf } from '@gitlab/ui';
import StatusBox from '~/merge_request/components/status_box.vue';
import mrEventHub from '~/merge_request/eventhub';
let wrapper;
function factory(propsData) {
wrapper = shallowMount(StatusBox, { propsData, stubs: { GlSprintf } });
}
const testCases = [
{
name: 'Open',
state: 'opened',
class: 'status-box-open',
icon: 'issue-open-m',
},
{
name: 'Closed',
state: 'closed',
class: 'status-box-mr-closed',
icon: 'close',
},
{
name: 'Merged',
state: 'merged',
class: 'status-box-mr-merged',
icon: 'git-merge',
},
];
describe('Merge request status box component', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
testCases.forEach((testCase) => {
describe(`when merge request is ${testCase.name}`, () => {
it('renders human readable test', () => {
factory({
initialState: testCase.state,
initialIsReverted: false,
});
expect(wrapper.text()).toContain(testCase.name);
});
it('sets css class', () => {
factory({
initialState: testCase.state,
initialIsReverted: false,
});
expect(wrapper.classes()).toContain(testCase.class);
});
it('renders icon', () => {
factory({
initialState: testCase.state,
initialIsReverted: false,
});
expect(wrapper.find('[data-testid="status-icon"]').props('name')).toBe(testCase.icon);
});
});
});
describe('when merge request is reverted', () => {
it('renders a link to the reverted merge request', () => {
factory({
initialState: 'merged',
initialIsReverted: true,
initialRevertedPath: 'http://test.com',
});
expect(wrapper.find('[data-testid="reverted-link"]').attributes('href')).toBe(
'http://test.com',
);
});
});
it('updates with eventhub event', async () => {
factory({
initialState: 'opened',
initialIsReverted: false,
});
expect(wrapper.text()).toContain('Open');
mrEventHub.$emit('mr.state.updated', { state: 'closed', reverted: false });
await nextTick();
expect(wrapper.text()).toContain('Closed');
});
});

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::TriggersHelper do
let(:project_id) { 1 }
describe '.builds_trigger_url' do
subject { helper.builds_trigger_url(project_id, ref: ref) }
context 'with no ref' do
let(:ref) { nil }
specify { expect(subject).to eq "#{Settings.gitlab.url}/api/v4/projects/1/trigger/pipeline" }
end
context 'with ref' do
let(:ref) { 'master' }
specify { expect(subject).to eq "#{Settings.gitlab.url}/api/v4/projects/1/ref/master/trigger/pipeline" }
end
end
describe '.service_trigger_url' do
subject { helper.service_trigger_url(service) }
let(:service) { double(project_id: 1, to_param: 'param') }
specify { expect(subject).to eq "#{Settings.gitlab.url}/api/v4/projects/1/services/param/trigger" }
end
end

View file

@ -224,4 +224,41 @@ RSpec.describe ExpandVariables do
end
end
end
describe '#possible_var_reference?' do
context 'table tests' do
using RSpec::Parameterized::TableSyntax
where do
{
"empty value": {
value: '',
result: false
},
"normal value": {
value: 'some value',
result: false
},
"simple expansions": {
value: 'key$variable',
result: true
},
"complex expansions": {
value: 'key${variable}${variable2}',
result: true
},
"complex expansions for Windows": {
value: 'key%variable%%variable2%',
result: true
}
}
end
with_them do
subject { ExpandVariables.possible_var_reference?(value) }
it { is_expected.to eq(result) }
end
end
end
end

View file

@ -0,0 +1,113 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::PopulateFindingUuidForVulnerabilityFeedback, schema: 20201211090634 do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:users) { table(:users) }
let(:scanners) { table(:vulnerability_scanners) }
let(:identifiers) { table(:vulnerability_identifiers) }
let(:findings) { table(:vulnerability_occurrences) }
let(:vulnerability_feedback) { table(:vulnerability_feedback) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
let(:user) { users.create!(username: 'john.doe', projects_limit: 5) }
let(:scanner) { scanners.create!(project_id: project.id, external_id: 'foo', name: 'bar') }
let(:identifier) { identifiers.create!(project_id: project.id, fingerprint: 'foo', external_type: 'bar', external_id: 'zoo', name: 'baz') }
let(:sast_report) { 0 }
let(:dependency_scanning_report) { 1 }
let(:dast_report) { 3 }
let(:secret_detection_report) { 4 }
let(:project_fingerprint) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
let(:location_fingerprint_1) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
let(:location_fingerprint_2) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
let(:location_fingerprint_3) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
let(:finding_1) { finding_creator.call(sast_report, location_fingerprint_1) }
let(:finding_2) { finding_creator.call(dast_report, location_fingerprint_2) }
let(:finding_3) { finding_creator.call(secret_detection_report, location_fingerprint_3) }
let(:uuid_1_components) { ['sast', identifier.fingerprint, location_fingerprint_1, project.id].join('-') }
let(:uuid_2_components) { ['dast', identifier.fingerprint, location_fingerprint_2, project.id].join('-') }
let(:uuid_3_components) { ['secret_detection', identifier.fingerprint, location_fingerprint_3, project.id].join('-') }
let(:expected_uuid_1) { Gitlab::UUID.v5(uuid_1_components) }
let(:expected_uuid_2) { Gitlab::UUID.v5(uuid_2_components) }
let(:expected_uuid_3) { Gitlab::UUID.v5(uuid_3_components) }
let(:finding_creator) do
-> (report_type, location_fingerprint) do
findings.create!(
project_id: project.id,
primary_identifier_id: identifier.id,
scanner_id: scanner.id,
report_type: report_type,
uuid: SecureRandom.uuid,
name: 'Foo',
location_fingerprint: Gitlab::Database::ShaAttribute.serialize(location_fingerprint),
project_fingerprint: Gitlab::Database::ShaAttribute.serialize(project_fingerprint),
metadata_version: '1',
severity: 0,
confidence: 5,
raw_metadata: '{}'
)
end
end
let(:feedback_creator) do
-> (category, project_fingerprint) do
vulnerability_feedback.create!(
project_id: project.id,
author_id: user.id,
feedback_type: 0,
category: category,
project_fingerprint: project_fingerprint
)
end
end
let!(:feedback_1) { feedback_creator.call(finding_1.report_type, project_fingerprint) }
let!(:feedback_2) { feedback_creator.call(finding_2.report_type, project_fingerprint) }
let!(:feedback_3) { feedback_creator.call(finding_3.report_type, project_fingerprint) }
let!(:feedback_4) { feedback_creator.call(finding_1.report_type, 'foo') }
let!(:feedback_5) { feedback_creator.call(dependency_scanning_report, project_fingerprint) }
subject(:populate_finding_uuids) { described_class.new.perform(feedback_1.id, feedback_5.id) }
before do
allow(Gitlab::BackgroundMigration::Logger).to receive(:info)
end
describe '#perform' do
it 'updates the `finding_uuid` attributes of the feedback records' do
expect { populate_finding_uuids }.to change { feedback_1.reload.finding_uuid }.from(nil).to(expected_uuid_1)
.and change { feedback_2.reload.finding_uuid }.from(nil).to(expected_uuid_2)
.and change { feedback_3.reload.finding_uuid }.from(nil).to(expected_uuid_3)
.and not_change { feedback_4.reload.finding_uuid }
.and not_change { feedback_5.reload.finding_uuid }
expect(Gitlab::BackgroundMigration::Logger).to have_received(:info).once
end
it 'preloads the finding and identifier records to prevent N+1 queries' do
# Load feedback records(1), load findings(2), load identifiers(3) and finally update feedback records one by one(6)
expect { populate_finding_uuids }.not_to exceed_query_limit(6)
end
context 'when setting the `finding_uuid` attribute of a feedback record fails' do
let(:expected_error) { RuntimeError.new }
before do
allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
allow_next_found_instance_of(described_class::VulnerabilityFeedback) do |feedback|
allow(feedback).to receive(:update_column).and_raise(expected_error)
end
end
it 'captures the errors and does not crash entirely' do
expect { populate_finding_uuids }.not_to raise_error
expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(expected_error).exactly(3).times
end
end
end
end

View file

@ -0,0 +1,251 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Variables::Collection::Sorted do
describe '#errors' do
context 'when FF :variable_inside_variable is disabled' do
before do
stub_feature_flags(variable_inside_variable: false)
end
context 'table tests' do
using RSpec::Parameterized::TableSyntax
where do
{
"empty array": {
variables: []
},
"simple expansions": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'result' },
{ key: 'variable3', value: 'key$variable$variable2' }
]
},
"complex expansion": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'key${variable}' }
]
},
"complex expansions with missing variable for Windows": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable3', value: 'key%variable%%variable2%' }
]
},
"out-of-order variable reference": {
variables: [
{ key: 'variable2', value: 'key${variable}' },
{ key: 'variable', value: 'value' }
]
},
"array with cyclic dependency": {
variables: [
{ key: 'variable', value: '$variable2' },
{ key: 'variable2', value: '$variable3' },
{ key: 'variable3', value: 'key$variable$variable2' }
]
}
}
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
it 'does not report error' do
expect(subject.errors).to eq(nil)
end
it 'valid? reports true' do
expect(subject.valid?).to eq(true)
end
end
end
end
context 'when FF :variable_inside_variable is enabled' do
before do
stub_feature_flags(variable_inside_variable: true)
end
context 'table tests' do
using RSpec::Parameterized::TableSyntax
where do
{
"empty array": {
variables: [],
validation_result: nil
},
"simple expansions": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'result' },
{ key: 'variable3', value: 'key$variable$variable2' }
],
validation_result: nil
},
"cyclic dependency": {
variables: [
{ key: 'variable', value: '$variable2' },
{ key: 'variable2', value: '$variable3' },
{ key: 'variable3', value: 'key$variable$variable2' }
],
validation_result: 'circular variable reference detected: ["variable", "variable2", "variable3"]'
}
}
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
it 'errors matches expected validation result' do
expect(subject.errors).to eq(validation_result)
end
it 'valid? matches expected validation result' do
expect(subject.valid?).to eq(validation_result.nil?)
end
end
end
end
end
describe '#sort' do
context 'when FF :variable_inside_variable is disabled' do
before do
stub_feature_flags(variable_inside_variable: false)
end
context 'table tests' do
using RSpec::Parameterized::TableSyntax
where do
{
"empty array": {
variables: []
},
"simple expansions": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'result' },
{ key: 'variable3', value: 'key$variable$variable2' }
]
},
"complex expansion": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'key${variable}' }
]
},
"complex expansions with missing variable for Windows": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable3', value: 'key%variable%%variable2%' }
]
},
"out-of-order variable reference": {
variables: [
{ key: 'variable2', value: 'key${variable}' },
{ key: 'variable', value: 'value' }
]
},
"array with cyclic dependency": {
variables: [
{ key: 'variable', value: '$variable2' },
{ key: 'variable2', value: '$variable3' },
{ key: 'variable3', value: 'key$variable$variable2' }
]
}
}
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
it 'does not expand variables' do
expect(subject.sort).to eq(variables)
end
end
end
end
context 'when FF :variable_inside_variable is enabled' do
before do
stub_licensed_features(group_saml_group_sync: true)
stub_feature_flags(saml_group_links: true)
stub_feature_flags(variable_inside_variable: true)
end
context 'table tests' do
using RSpec::Parameterized::TableSyntax
where do
{
"empty array": {
variables: [],
result: []
},
"simple expansions, no reordering needed": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable2', value: 'result' },
{ key: 'variable3', value: 'key$variable$variable2' }
],
result: %w[variable variable2 variable3]
},
"complex expansion, reordering needed": {
variables: [
{ key: 'variable2', value: 'key${variable}' },
{ key: 'variable', value: 'value' }
],
result: %w[variable variable2]
},
"unused variables": {
variables: [
{ key: 'variable', value: 'value' },
{ key: 'variable4', value: 'key$variable$variable3' },
{ key: 'variable2', value: 'result2' },
{ key: 'variable3', value: 'result3' }
],
result: %w[variable variable3 variable4 variable2]
},
"missing variable": {
variables: [
{ key: 'variable2', value: 'key$variable' }
],
result: %w[variable2]
},
"complex expansions with missing variable": {
variables: [
{ key: 'variable4', value: 'key${variable}${variable2}${variable3}' },
{ key: 'variable', value: 'value' },
{ key: 'variable3', value: 'value3' }
],
result: %w[variable variable3 variable4]
},
"cyclic dependency causes original array to be returned": {
variables: [
{ key: 'variable2', value: '$variable3' },
{ key: 'variable3', value: 'key$variable$variable2' },
{ key: 'variable', value: '$variable2' }
],
result: %w[variable2 variable3 variable]
}
}
end
with_them do
subject { Gitlab::Ci::Variables::Collection::Sorted.new(variables) }
it 'sort returns correctly sorted variables' do
expect(subject.sort.map { |var| var[:key] }).to eq(result)
end
end
end
end
end
end

View file

@ -29,8 +29,17 @@ RSpec.describe Gitlab::GitAccessSnippet do
let(:actor) { build(:deploy_key) }
it 'does not allow push and pull access' do
expect { push_access_check }.to raise_forbidden(described_class::ERROR_MESSAGES[:authentication_mechanism])
expect { pull_access_check }.to raise_forbidden(described_class::ERROR_MESSAGES[:authentication_mechanism])
expect { push_access_check }.to raise_forbidden(:authentication_mechanism)
expect { pull_access_check }.to raise_forbidden(:authentication_mechanism)
end
end
describe 'when snippet repository is read-only' do
it 'does not allow push and allows pull access' do
allow(snippet).to receive(:repository_read_only?).and_return(true)
expect { push_access_check }.to raise_forbidden(:read_only)
expect { pull_access_check }.not_to raise_error
end
end
@ -58,7 +67,7 @@ RSpec.describe Gitlab::GitAccessSnippet do
let(:snippet) { nil }
it 'blocks access with "not found"' do
expect { pull_access_check }.to raise_snippet_not_found
expect { pull_access_check }.to raise_not_found(:snippet_not_found)
end
end
@ -66,7 +75,7 @@ RSpec.describe Gitlab::GitAccessSnippet do
let(:snippet) { build_stubbed(:personal_snippet) }
it 'blocks access with "not found"' do
expect { pull_access_check }.to raise_snippet_not_found
expect { pull_access_check }.to raise_not_found(:no_repo)
end
end
end
@ -81,8 +90,8 @@ RSpec.describe Gitlab::GitAccessSnippet do
it 'blocks access when the user did not accept terms' do
message = /must accept the Terms of Service in order to perform this action/
expect { push_access_check }.to raise_forbidden(message)
expect { pull_access_check }.to raise_forbidden(message)
expect { push_access_check }.to raise_forbidden_with_message(message)
expect { pull_access_check }.to raise_forbidden_with_message(message)
end
it 'allows access when the user accepted the terms' do
@ -149,8 +158,8 @@ RSpec.describe Gitlab::GitAccessSnippet do
let(:membership) { membership }
it 'respects accessibility' do
expect { push_access_check }.to raise_snippet_not_found
expect { pull_access_check }.to raise_snippet_not_found
expect { push_access_check }.to raise_not_found(:project_not_found)
expect { pull_access_check }.to raise_not_found(:project_not_found)
end
end
end
@ -273,7 +282,7 @@ RSpec.describe Gitlab::GitAccessSnippet do
allow(check).to receive(:validate!).and_raise(Gitlab::GitAccess::ForbiddenError, 'foo')
end
expect { push_access_check }.to raise_forbidden('foo')
expect { push_access_check }.to raise_forbidden_with_message('foo')
end
it 'sets the file count limit from Snippet class' do
@ -424,15 +433,15 @@ RSpec.describe Gitlab::GitAccessSnippet do
private
def raise_snippet_not_found
raise_error(Gitlab::GitAccess::NotFoundError, Gitlab::GitAccess::ERROR_MESSAGES[:snippet_not_found])
def raise_not_found(message_key)
raise_error(described_class::NotFoundError, described_class.error_message(message_key))
end
def raise_project_not_found
raise_error(Gitlab::GitAccess::NotFoundError, Gitlab::GitAccess::ERROR_MESSAGES[:project_not_found])
def raise_forbidden(message_key)
raise_error(Gitlab::GitAccess::ForbiddenError, described_class.error_message(message_key))
end
def raise_forbidden(message)
def raise_forbidden_with_message(message)
raise_error(Gitlab::GitAccess::ForbiddenError, message)
end
end

View file

@ -0,0 +1,37 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe SchedulePopulateFindingUuidForVulnerabilityFeedback do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:users) { table(:users) }
let(:vulnerability_feedback) { table(:vulnerability_feedback) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
let(:user) { users.create!(username: 'john.doe', projects_limit: 1) }
let(:common_feedback_params) { { feedback_type: 0, category: 0, project_id: project.id, author_id: user.id } }
let!(:feedback_1) { vulnerability_feedback.create!(**common_feedback_params, project_fingerprint: 'foo') }
let!(:feedback_2) { vulnerability_feedback.create!(**common_feedback_params, project_fingerprint: 'bar') }
let!(:feedback_3) { vulnerability_feedback.create!(**common_feedback_params, project_fingerprint: 'zoo', finding_uuid: SecureRandom.uuid) }
around do |example|
freeze_time { Sidekiq::Testing.fake! { example.run } }
end
before do
stub_const("#{described_class.name}::BATCH_SIZE", 1)
end
it 'schedules the background jobs', :aggregate_failures do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to be(3)
expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(2.minutes, feedback_1.id, feedback_1.id)
expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(4.minutes, feedback_2.id, feedback_2.id)
expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(6.minutes, feedback_3.id, feedback_3.id)
end
end

View file

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe CommitWithPipeline do
RSpec.describe Ci::CommitWithPipeline do
let(:project) { create(:project, :public, :repository) }
let(:commit) { described_class.new(project.commit) }

View file

@ -155,6 +155,12 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
expect(Todo.where(attributes).count).to eq 1
end
it 'invalidates counter cache for reviewers', :use_clean_rails_memory_store_caching do
expect { merge_request }
.to change { user2.review_requested_open_merge_requests_count }
.by(1)
end
end
context 'when head pipelines already exist for merge request source branch', :sidekiq_inline do

View file

@ -63,6 +63,16 @@ RSpec.shared_examples 'rebase quick action' do
expect(page).not_to have_content 'Scheduled a rebase'
end
end
context 'when there are conflicts in the merge request' do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, source_branch: 'conflict-missing-side', target_branch: 'conflict-start', merge_status: :cannot_be_merged) }
it 'does not rebase the MR' do
add_note("/rebase")
expect(page).to have_content 'This merge request cannot be rebased while there are conflicts.'
end
end
end
context 'when the current user cannot rebase the MR' do

View file

@ -59,7 +59,7 @@ RSpec.describe 'layouts/_head' do
render
expect(rendered).to match('<link rel="stylesheet" media="all" href="/stylesheets/highlight/themes/solarised-light.css" />')
expect(rendered).to match('<link rel="stylesheet" media="print" href="/stylesheets/highlight/themes/solarised-light.css" />')
end
context 'when an asset_host is set and snowplow url is set' do

View file

@ -876,10 +876,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@25.7.2":
version "25.7.2"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-25.7.2.tgz#50271f99818645f9bc7ef675c08f9bf1bae54293"
integrity sha512-hgM9JHesIqnDujjJ16BHFQWYwzWd9php8xElhkGUdKWGV+PVw9TVdYcKfkIAb3TXtb9551uXN0Jv6wH5DRJyXw==
"@gitlab/ui@25.7.3":
version "25.7.3"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-25.7.3.tgz#abc5caa585c307f38e01a7f061d9da0bc238acd0"
integrity sha512-Ia+Pqmy4tQ5oztxXSjViM6YMt13MGyrQ6dqOn3zsiKG9RLh9kSBMtCif9jA0dDT17P+HMIdBYxYc6rTRjbfC0w==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"