Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ec971a05e3
commit
dcacb5daf7
76 changed files with 1416 additions and 163 deletions
|
@ -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
|
||||
|
|
6
Gemfile
6
Gemfile
|
@ -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'
|
||||
|
||||
|
|
18
Gemfile.lock
18
Gemfile.lock
|
@ -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)
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
3
app/assets/javascripts/merge_request/eventhub.js
Normal file
3
app/assets/javascripts/merge_request/eventhub.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import createEventHub from '~/helpers/event_hub_factory';
|
||||
|
||||
export default createEventHub();
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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"
|
|
@ -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
|
38
app/models/ci/commit_with_pipeline.rb
Normal file
38
app/models/ci/commit_with_pipeline.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CommitWithPipeline < SimpleDelegator
|
||||
class Ci::CommitWithPipeline < SimpleDelegator
|
||||
include Presentable
|
||||
|
||||
def initialize(commit)
|
||||
|
|
36
app/models/concerns/enums/ci/commit_status.rb
Normal file
36
app/models/concerns/enums/ci/commit_status.rb
Normal 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')
|
|
@ -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')
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MattermostSlashCommandsService < SlashCommandsService
|
||||
include TriggersHelper
|
||||
include Ci::TriggersHelper
|
||||
|
||||
prop_accessor :token
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SlackSlashCommandsService < SlashCommandsService
|
||||
include TriggersHelper
|
||||
include Ci::TriggersHelper
|
||||
|
||||
def title
|
||||
'Slack slash commands'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
- return unless use_startup_css?
|
||||
|
||||
= javascript_tag do
|
||||
:plain
|
||||
document.querySelectorAll('link[media="print"]').forEach(linkTag => {
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 |
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add DAST.latest.gitlab-ci.yml
|
||||
merge_request: 50539
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Populate `finding_uuid` attribute for the existing `vulnerability_feedback`
|
||||
records
|
||||
merge_request: 49807
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Invalidate reviews counter cache when MR gets created
|
||||
merge_request: 51316
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Forbid snippet pushes when repo is read-only
|
||||
merge_request: 51318
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/ph-290850-mrStatusBoxPoll.yml
Normal file
5
changelogs/unreleased/ph-290850-mrStatusBoxPoll.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update merge request status box without reloading page
|
||||
merge_request: 50761
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/sh-disable-rebase-on-conflicts.yml
Normal file
5
changelogs/unreleased/sh-disable-rebase-on-conflicts.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Prevent rebase from being run in quick action when there are conflicts
|
||||
merge_request: 51243
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/update-puma-to-v-5.yml
Normal file
5
changelogs/unreleased/update-puma-to-v-5.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update puma & puma_worker_killer to upstream
|
||||
merge_request: 48897
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/yo-master-patch-74981.yml
Normal file
5
changelogs/unreleased/yo-master-patch-74981.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix import issues button style
|
||||
merge_request: 50969
|
||||
author: Yogi (@yo)
|
||||
type: fixed
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
1
db/schema_migrations/20201211090634
Normal file
1
db/schema_migrations/20201211090634
Normal file
|
@ -0,0 +1 @@
|
|||
5117b71950bec3c6c746eaf4851c111a335bf2280075ddc2c73b60a30e23d907
|
|
@ -58,6 +58,7 @@ exceptions:
|
|||
- GPL
|
||||
- GUI
|
||||
- HAML
|
||||
- HEAD
|
||||
- HIPAA
|
||||
- HTML
|
||||
- HTTP
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
45
lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
Normal file
45
lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
Normal 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
|
77
lib/gitlab/ci/variables/collection/sorted.rb
Normal file
77
lib/gitlab/ci/variables/collection/sorted.rb
Normal 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
|
|
@ -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!
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
43
qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb
Normal file
43
qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb
Normal 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
|
|
@ -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" }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
99
spec/frontend/merge_request/components/status_box_spec.js
Normal file
99
spec/frontend/merge_request/components/status_box_spec.js
Normal 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');
|
||||
});
|
||||
});
|
31
spec/helpers/ci/triggers_helper_spec.rb
Normal file
31
spec/helpers/ci/triggers_helper_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
251
spec/lib/gitlab/ci/variables/collection/sorted_spec.rb
Normal file
251
spec/lib/gitlab/ci/variables/collection/sorted_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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) }
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue