Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-05 00:09:24 +00:00
parent 6609e5ea75
commit 7ff2de7c12
186 changed files with 965 additions and 555 deletions

View File

@ -2,6 +2,18 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 13.9.2 (2021-03-04)
### Security (6 changes)
- Bump thrift gem to 0.14.0.
- Allow only owners to manage group variables.
- Do not store marshalled sessions ids in Redis.
- Fix XSS in wiki author email and name.
- Workhorse: prevent escaped router path traversal.
- Fix XSS vulnerability for swagger file viewer.
## 13.9.1 (2021-02-23)
### Fixed (6 changes, 1 of them is from the community)
@ -588,6 +600,18 @@ entry.
- Apply new GitLab UI for buttons in pipeline schedules.
## 13.8.5 (2021-03-04)
### Security (6 changes)
- Fix XSS in wiki author email and name.
- Bump thrift gem to 0.14.0.
- Allow only owners to manage group variables.
- Do not store marshalled sessions ids in Redis.
- Workhorse: prevent escaped router path traversal.
- Fix XSS vulnerability for swagger file viewer.
## 13.8.4 (2021-02-11)
### Security (9 changes)
@ -988,6 +1012,17 @@ entry.
- Add verbiage + link sast to show it's in core. !51935
## 13.7.8 (2021-03-04)
### Security (5 changes)
- Bump thrift gem to 0.14.0.
- Allow only owners to manage group variables.
- Do not store marshalled sessions ids in Redis.
- Workhorse: prevent escaped router path traversal.
- Fix XSS vulnerability for swagger file viewer.
## 13.7.7 (2021-02-11)
### Security (9 changes)

View File

@ -1 +1 @@
cdb02af5b322de1f4091b39c349579b2e335b914
14b985f2d0add8bd3b6fbf01d06e3bbafcb12f6c

View File

@ -1 +1 @@
8.64.0
8.64.2

View File

@ -314,6 +314,9 @@ gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '~> 0.16.0'
# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
gem 'thrift', '>= 0.14.0'
# I18n
gem 'ruby_parser', '~> 3.15', require: false

View File

@ -1578,6 +1578,7 @@ DEPENDENCIES
terser (= 1.0.2)
test-prof (~> 0.12.0)
thin (~> 1.8.0)
thrift (>= 0.14.0)
timecop (~> 0.9.1)
toml-rb (~> 1.0.0)
truncato (~> 0.7.11)

View File

@ -225,7 +225,7 @@ export default {
getIssueMeta({ issue: { iid, state } }) {
return {
state: state === 'closed' ? `(${this.$options.i18n.closed})` : '',
link: joinPaths('/', this.projectPath, '-', 'issues', iid),
link: joinPaths('/', this.projectPath, '-', 'issues/incident', iid),
};
},
tbodyTrClass(item) {

View File

@ -85,7 +85,7 @@ export default {
</div>
<gl-form-checkbox
data-testid="show-whitespace"
class="gl-mt-3 gl-pl-3"
class="gl-mt-3 gl-ml-3"
:checked="showWhitespace"
@input="toggleWhitespace"
>
@ -93,7 +93,7 @@ export default {
</gl-form-checkbox>
<gl-form-checkbox
data-testid="file-by-file"
class="gl-pl-3 gl-mb-0"
class="gl-ml-3 gl-mb-0"
:checked="viewDiffsFileByFile"
@input="toggleFileByFile"
>

View File

@ -213,7 +213,7 @@ export default {
<gl-button
v-if="newUserListPath"
:href="newUserListPath"
variant="success"
variant="confirm"
category="secondary"
class="gl-mb-3"
data-testid="ff-new-list-button"
@ -224,7 +224,7 @@ export default {
<gl-button
v-if="hasNewPath"
:href="featureFlagsLimitExceeded ? '' : newFeatureFlagPath"
variant="success"
variant="confirm"
data-testid="ff-new-button"
@click="onNewFeatureFlagCLick"
>
@ -301,7 +301,7 @@ export default {
<gl-button
v-if="newUserListPath"
:href="newUserListPath"
variant="success"
variant="confirm"
category="secondary"
class="gl-mb-0 gl-mr-4"
data-testid="ff-new-list-button"
@ -312,7 +312,7 @@ export default {
<gl-button
v-if="hasNewPath"
:href="featureFlagsLimitExceeded ? '' : newFeatureFlagPath"
variant="success"
variant="confirm"
data-testid="ff-new-button"
@click="onNewFeatureFlagCLick"
>

View File

@ -40,7 +40,7 @@ export const I18N_ALERT_SETTINGS_FORM = {
label: __('Incident template (optional)'),
},
sendEmail: {
label: __('Send a separate email notification to Developers.'),
label: __('Send a single email notification to Owners and Maintainers for new alerts.'),
},
autoCloseIncidents: {
label: __('Automatically close incidents when the associated Prometheus alert resolves.'),

View File

@ -1,3 +1,4 @@
import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
import { parseBoolean } from '../lib/utils/common_utils';
@ -7,7 +8,6 @@ import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue';
import DirectoryDownloadLinks from './components/directory_download_links.vue';
import LastCommit from './components/last_commit.vue';
import TreeActionLink from './components/tree_action_link.vue';
import apolloProvider from './graphql';
import createRouter from './router';
import { updateFormAction } from './utils/dom';
@ -101,14 +101,17 @@ export default function setupVueRepositoryList() {
el: treeHistoryLinkEl,
router,
render(h) {
return h(TreeActionLink, {
props: {
path: `${historyLink}/${
this.$route.params.path ? escapeFileUrl(this.$route.params.path) : ''
}`,
text: __('History'),
return h(
GlButton,
{
attrs: {
href: `${historyLink}/${
this.$route.params.path ? escapeFileUrl(this.$route.params.path) : ''
}`,
},
},
});
[__('History')],
);
},
});

View File

@ -222,7 +222,9 @@ export default {
});
},
incidentPath(issueId) {
return joinPaths(this.projectIssuesPath, issueId);
return this.isThreatMonitoringPage
? joinPaths(this.projectIssuesPath, issueId)
: joinPaths(this.projectIssuesPath, 'incident', issueId);
},
trackPageViews() {
const { category, action } = this.trackAlertsDetailsViewsOptions;

View File

@ -34,7 +34,14 @@
overflow-y: auto;
&.dropdown-extended-height {
max-height: 400px;
$extended-max-height: 400px;
max-height: $extended-max-height;
// See comment below for explanation
.gl-new-dropdown-inner {
max-height: $extended-max-height - 2px;
}
}
@include media-breakpoint-down(xs) {
@ -46,6 +53,15 @@
overflow-y: initial;
max-height: initial;
}
// `GlDropdown` specifies the `max-height` of `.gl-new-dropdown-inner`
// as `$dropdown-max-height`, but the `max-height` rule above forces
// the parent `.dropdown-menu` to be _slightly_ too small because of
// the 1px borders. The workaround below overrides the @gitlab/ui style
// to avoid a double scroll bar.
.gl-new-dropdown-inner {
max-height: $dropdown-max-height - 2px;
}
}
.dropdown-toggle,

View File

@ -136,33 +136,9 @@
.project-repo-buttons {
.btn {
&:last-child {
margin-left: 0;
}
svg {
fill: $layout-link-gray;
fill: $gray-500;
}
.notifications-icon {
top: 1px;
margin-right: 0;
}
}
.icon {
top: 0;
}
.count-badge,
.btn-xs {
height: 24px;
}
.home-panel-action-button,
.project-action-button {
margin: $gl-padding $gl-padding-8 0 0;
vertical-align: top;
}
.notification-dropdown .dropdown-menu {
@ -175,34 +151,6 @@
}
}
.count-buttons {
display: inline-block;
vertical-align: top;
margin-top: $gl-padding;
.count-badge-count,
.count-badge-button {
border: 1px solid $border-color;
line-height: 1;
}
.count,
.count-badge-button {
color: $gl-text-color;
}
.count-badge-count {
padding: 0 12px;
background: $gray-light;
border-radius: 0 $border-radius-base $border-radius-base 0;
}
.count-badge-button {
border-right: 0;
border-radius: $border-radius-base 0 0 $border-radius-base;
}
}
.project-clone-holder {
display: inline-block;
margin: $gl-padding 0 0;
@ -887,10 +835,6 @@ pre.light-well {
}
.git-clone-holder {
.btn-clipboard {
border: 1px solid $border-color;
}
.clone-options {
display: table-cell;

View File

@ -2,7 +2,7 @@
module Groups
class VariablesController < Groups::ApplicationController
before_action :authorize_admin_build!
before_action :authorize_admin_group!
skip_cross_project_access_check :show, :update

View File

@ -8,9 +8,8 @@ class Profiles::ActiveSessionsController < Profiles::ApplicationController
end
def destroy
# params[:id] can be either an Rack::Session::SessionId#private_id
# or an encrypted Rack::Session::SessionId#public_id
ActiveSession.destroy_with_deprecated_encryption(current_user, params[:id])
# params[:id] can be an Rack::Session::SessionId#private_id
ActiveSession.destroy_session(current_user, params[:id])
current_user.forget_me!
respond_to do |format|

View File

@ -24,11 +24,6 @@ module ActiveSessionsHelper
end
def revoke_session_path(active_session)
if active_session.session_private_id
profile_active_session_path(active_session.session_private_id)
else
# TODO: remove in 13.7
profile_active_session_path(active_session.public_id)
end
profile_active_session_path(active_session.session_private_id)
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
module WikiPageVersionHelper
def wiki_page_version_author_url(wiki_page_version)
user = wiki_page_version.author
user.nil? ? "mailto:#{wiki_page_version.author_email}" : Gitlab::UrlBuilder.build(user)
end
def wiki_page_version_author_avatar(wiki_page_version)
image_tag(avatar_icon_for_email(wiki_page_version.author_email, 24), class: "avatar s24 float-none gl-mr-0!")
end
def wiki_page_version_author_header(wiki_page_version)
avatar = wiki_page_version_author_avatar(wiki_page_version)
name = "<strong>".html_safe + wiki_page_version.author_name + "</strong>".html_safe
link_start = "<a href='".html_safe + wiki_page_version_author_url(wiki_page_version) + "'>".html_safe
html_escape(_("Last edited by %{link_start}%{avatar} %{name}%{link_end}")) % { avatar: avatar, name: name, link_start: link_start, link_end: '</a>'.html_safe }
end
end

View File

@ -42,13 +42,6 @@ class ActiveSession
device_type&.titleize
end
# This is not the same as Rack::Session::SessionId#public_id, but we
# need to preserve this for backwards compatibility.
# TODO: remove in 13.7
def public_id
Gitlab::CryptoHelper.aes256_gcm_encrypt(session_id)
end
def self.set(user, request)
Gitlab::Redis::SharedState.with do |redis|
session_private_id = request.session.id.private_id
@ -63,8 +56,6 @@ class ActiveSession
device_type: client.device_type,
created_at: user.current_sign_in_at || timestamp,
updated_at: timestamp,
# TODO: remove in 13.7
session_id: request.session.id.public_id,
session_private_id: session_private_id,
is_impersonated: request.session[:impersonator_id].present?
)
@ -80,20 +71,10 @@ class ActiveSession
lookup_key_name(user.id),
session_private_id
)
# We remove the ActiveSession stored by using public_id to avoid
# duplicate entries
remove_deprecated_active_sessions_with_public_id(redis, user.id, request.session.id.public_id)
end
end
end
# TODO: remove in 13.7
private_class_method def self.remove_deprecated_active_sessions_with_public_id(redis, user_id, rack_session_public_id)
redis.srem(lookup_key_name(user_id), rack_session_public_id)
redis.del(key_name(user_id, rack_session_public_id))
end
def self.list(user)
Gitlab::Redis::SharedState.with do |redis|
cleaned_up_lookup_entries(redis, user).map do |raw_session|
@ -109,18 +90,6 @@ class ActiveSession
end
end
# TODO: remove in 13.7
# After upgrade there might be a duplicate ActiveSessions:
# - one with the public_id stored in #session_id
# - another with private_id stored in #session_private_id
def self.destroy_with_rack_session_id(user, rack_session_id)
return unless rack_session_id
Gitlab::Redis::SharedState.with do |redis|
destroy_sessions(redis, user, [rack_session_id.public_id, rack_session_id.private_id])
end
end
def self.destroy_sessions(redis, user, session_ids)
key_names = session_ids.map { |session_id| key_name(user.id, session_id) }
@ -132,19 +101,11 @@ class ActiveSession
end
end
# TODO: remove in 13.7
# After upgrade, .destroy might be called with the session id encrypted
# by .public_id.
def self.destroy_with_deprecated_encryption(user, session_id)
def self.destroy_session(user, session_id)
return unless session_id
decrypted_session_id = decrypt_public_id(session_id)
rack_session_private_id = if decrypted_session_id
Rack::Session::SessionId.new(decrypted_session_id).private_id
end
Gitlab::Redis::SharedState.with do |redis|
destroy_sessions(redis, user, [session_id, decrypted_session_id, rack_session_private_id].compact)
destroy_sessions(redis, user, [session_id].compact)
end
end
@ -275,11 +236,4 @@ class ActiveSession
entries.compact
end
# TODO: remove in 13.7
private_class_method def self.decrypt_public_id(public_id)
Gitlab::CryptoHelper.aes256_gcm_decrypt(public_id)
rescue
nil
end
end

View File

@ -693,14 +693,6 @@ module Ci
.exists?
end
# TODO: this logic is duplicate with Pipeline::Chain::Config::Content
# we should persist this is `ci_pipelines.config_path`
def config_path
return unless repository_source? || unknown_source?
project.ci_config_path_or_default
end
def has_yaml_errors?
yaml_errors.present?
end
@ -786,8 +778,6 @@ module Ci
variables.append(key: 'CI_PIPELINE_IID', value: iid.to_s)
variables.append(key: 'CI_PIPELINE_SOURCE', value: source.to_s)
variables.append(key: 'CI_CONFIG_PATH', value: config_path)
variables.concat(predefined_commit_variables)
if merge_request?

View File

@ -39,6 +39,7 @@ class Environment < ApplicationRecord
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
before_save :set_environment_type
before_save :ensure_environment_tier
after_save :clear_reactive_cache!
validates :name,
@ -87,6 +88,7 @@ class Environment < ApplicationRecord
end
scope :for_project, -> (project) { where(project_id: project) }
scope :for_tier, -> (tier) { where(tier: tier).where('tier IS NOT NULL') }
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
scope :unfoldered, -> { where(environment_type: nil) }
scope :with_rank, -> do
@ -94,6 +96,14 @@ class Environment < ApplicationRecord
end
scope :for_id, -> (id) { where(id: id) }
enum tier: {
production: 0,
staging: 1,
testing: 2,
development: 3,
other: 4
}
state_machine :state, initial: :available do
event :start do
transition stopped: :available
@ -429,6 +439,24 @@ class Environment < ApplicationRecord
def generate_slug
self.slug = Gitlab::Slug::Environment.new(name).generate
end
def ensure_environment_tier
return unless ::Feature.enabled?(:environment_tier, project, default_enabled: :yaml)
self.tier ||= guess_tier
end
# Guessing the tier of the environment if it's not explicitly specified by users.
# See https://en.wikipedia.org/wiki/Deployment_environment for industry standard deployment environments
def guess_tier
case name
when %r{dev|review|trunk}i then self.class.tiers[:development]
when %r{test|qc}i then self.class.tiers[:testing]
when %r{st(a|)g|mod(e|)l|pre|demo}i then self.class.tiers[:staging]
when %r{pr(o|)d|live}i then self.class.tiers[:production]
else self.class.tiers[:other]
end
end
end
Environment.prepend_if_ee('EE::Environment')

View File

@ -1987,6 +1987,7 @@ class Project < ApplicationRecord
.append(key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: repository_languages.map(&:name).join(',').downcase)
.append(key: 'CI_DEFAULT_BRANCH', value: default_branch)
.append(key: 'CI_PROJECT_CONFIG_PATH', value: ci_config_path_or_default)
.append(key: 'CI_CONFIG_PATH', value: ci_config_path_or_default)
end
def predefined_ci_server_variables

View File

@ -4,8 +4,8 @@
- emails_disabled = @project.emails_disabled?
.project-home-panel.js-show-on-project-root.gl-my-5{ class: [("empty-project" if empty_repo)] }
.row.gl-mb-3
.home-panel-title-row.col-md-12.col-lg-6.d-flex
.gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-sm-flex-direction-column.gl-mb-3
.home-panel-title-row.gl-display-flex
.avatar-container.rect-avatar.s64.home-panel-avatar.gl-flex-shrink-0.gl-w-11.gl-h-11.gl-mr-3.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'image')
.d-flex.flex-column.flex-wrap.align-items-baseline
@ -41,13 +41,13 @@
= _("+ %{count} more") % { count: @project.count_of_extra_topics_not_shown }
.project-repo-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
.project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
- if current_user
.d-inline-flex
.gl-display-flex.gl-align-items-start.gl-mr-3
- if @notification_setting
.js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, container_class: 'gl-mr-3 gl-mt-5 gl-vertical-align-top' } }
.js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id } }
.count-buttons.d-inline-flex
.count-buttons.gl-display-flex.gl-align-items-flex-start
= render 'projects/buttons/star'
= render 'projects/buttons/fork'

View File

@ -2,7 +2,7 @@
- dropdown_class = local_assigns.fetch(:dropdown_class, '')
.git-clone-holder.js-git-clone-holder
%a#clone-dropdown.gl-button.btn.btn-info.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%a#clone-dropdown.gl-button.btn.btn-confirm.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.gl-mr-2.js-clone-dropdown-label
= _('Clone')
= sprite_icon("chevron-down", css_class: "icon")
@ -11,19 +11,19 @@
%li{ class: 'gl-px-4!' }
%label.label-bold
= _('Clone with SSH')
.input-group
.input-group.btn-group
= text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control qa-ssh-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), class: "input-group-text gl-button btn btn-icon btn-default")
= render_if_exists 'projects/buttons/geo'
- if http_enabled?
%li.pt-2{ class: 'gl-px-4!' }
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
.input-group
.input-group.btn-group
= text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control qa-http-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
= clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
= clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text gl-button btn btn-icon btn-default")
= render_if_exists 'projects/buttons/geo'
%li.divider.mt-2
%li.pt-2.gl-new-dropdown-item

View File

@ -1,17 +1,16 @@
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project)
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3
.count-badge.btn-group
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn' do
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'gl-button btn btn-default btn-sm has-tooltip fork-btn' do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
- else
- can_create_fork = current_user.can?(:create_fork)
- disabled_fork_tooltip = s_('ProjectOverview|You have reached your project limit')
%span.has-tooltip{ title: (disabled_fork_tooltip unless can_create_fork) }
= link_to new_project_fork_path(@project), class: "btn btn-default btn-xs count-badge-button d-flex align-items-center fork-btn #{' disabled' unless can_create_fork }", 'aria-label' => (disabled_fork_tooltip unless can_create_fork) do
%span.btn-group.has-tooltip{ title: (disabled_fork_tooltip unless can_create_fork) }
= link_to new_project_fork_path(@project), class: "gl-button btn btn-default btn-sm fork-btn #{' disabled' unless can_create_fork }", 'aria-label' => (disabled_fork_tooltip unless can_create_fork) do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
%span.fork-count.count-badge-count.d-flex.align-items-center
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
= @project.forks_count
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Forks'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'gl-button btn btn-default btn-sm count has-tooltip' do
= @project.forks_count

View File

@ -1,21 +1,19 @@
- if current_user
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3
%button.count-badge-button.btn.btn-default.btn-xs.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3.btn-group
%button.gl-button.btn.btn-default.btn-sm.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
- if current_user.starred?(@project)
= sprite_icon('star', css_class: 'icon')
%span.starred= s_('ProjectOverview|Unstar')
- else
= sprite_icon('star-o', css_class: 'icon')
%span= s_('ProjectOverview|Star')
%span.star-count.count-badge-count.d-flex.align-items-center
= link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'count' do
= @project.star_count
= link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default btn-sm star-count count' do
= @project.star_count
- else
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3
= link_to new_user_session_path, class: 'btn btn-default btn-xs has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
.count-badge.d-inline-flex.align-item-stretch.gl-mr-3.btn-group
= link_to new_user_session_path, class: 'gl-button btn btn-default btn-sm has-tooltip star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= sprite_icon('star-o', css_class: 'icon')
%span= s_('ProjectOverview|Star')
%span.star-count.count-badge-count.d-flex.align-items-center
= link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'count' do
= @project.star_count
= link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default btn-sm star-count count' do
= @project.star_count

View File

@ -7,7 +7,7 @@
.tree-controls
.d-block.d-sm-flex.flex-wrap.align-items-start.gl-children-ml-sm-3<
= render_if_exists 'projects/tree/lock_link'
#js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } }
#js-tree-history-link{ data: { history_link: project_commits_path(@project, @ref) } }
= render 'projects/find_file_link'
= render 'shared/web_ide_button', blob: nil

View File

@ -3,8 +3,8 @@
- http_copy_label = _('Copy %{http_label} clone URL') % { http_label: gitlab_config.protocol.upcase }
.btn-group.mobile-git-clone.js-mobile-git-clone.btn-block
= clipboard_button(button_text: default_clone_label, text: default_url_to_repo(project), hide_button_icon: true, class: "btn-primary flex-fill bold justify-content-center input-group-text clone-dropdown-btn js-clone-dropdown-label")
%button.btn.btn-primary.dropdown-toggle.js-dropdown-toggle.flex-grow-0.d-flex-center.w-auto.ml-0{ type: "button", data: { toggle: "dropdown" } }
= clipboard_button(button_text: default_clone_label, text: default_url_to_repo(project), hide_button_icon: true, class: "gl-button btn btn-confirm flex-fill input-group-text clone-dropdown-btn js-clone-dropdown-label")
%button.gl-button.btn.btn-confirm.btn-icon.dropdown-toggle.js-dropdown-toggle.flex-grow-0.d-flex-center.w-auto.ml-0{ type: "button", data: { toggle: "dropdown" } }
= sprite_icon("chevron-down", css_class: "dropdown-btn-icon icon")
%ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } }
- if ssh_enabled?

View File

@ -7,7 +7,7 @@
.nav-text.flex-fill
%span.wiki-last-edit-by
- if @page.last_version
= html_escape(_("Last edited by %{link_start}%{avatar} %{name}%{link_end}")) % { avatar: image_tag(avatar_icon_for_email(@page.last_version.author_email, 24), class: "avatar s24 float-none gl-mr-0!"), name: "<strong>#{@page.last_version.author_name}</strong>".html_safe, link_start: "<a href='#{@page.last_version.author_url}'>".html_safe, link_end: '</a>'.html_safe }
= wiki_page_version_author_header(@page.last_version)
= time_ago_with_tooltip(@page.last_version.authored_date)
.nav-controls.pb-md-3.pb-lg-0

View File

@ -0,0 +1,5 @@
---
title: Update project page buttons to conform to design system
merge_request: 53260
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Change the button to Primary Blue
merge_request: 55204
author: Yogi (@yo)
type: changed

View File

@ -0,0 +1,5 @@
---
title: Update text for email config under Incidents in Settings > Operations
merge_request: 55753
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Allow alert to link to incidents
merge_request: 55426
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add tier column to the environments table
merge_request: 55471
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Move CI_CONFIG_PATH as project variable and deprecate CI_PROJECT_CONFIG_PATH
merge_request: 54498
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Fix double scrollbars in some dropdowns
merge_request: 54837
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: 'Workhorse: prevent escaped router path traversal'
merge_request:
author:
type: security

View File

@ -0,0 +1,5 @@
---
title: 'Workhorse: Stop logging when path is excluded'
merge_request:
author:
type: security

View File

@ -0,0 +1,5 @@
---
title: Fix horizontal alignment of MR Changes cog menu dropdown checkboxes
merge_request: 55591
author:
type: fixed

View File

@ -0,0 +1,8 @@
---
name: environment_tier
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55471
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323166
milestone: '13.10'
type: development
group: group::release
default_enabled: false

View File

@ -42,8 +42,7 @@ Rails.application.configure do |config|
activity = Gitlab::Auth::Activity.new(opts)
tracker = Gitlab::Auth::BlockedUserTracker.new(user, auth)
# TODO: switch to `auth.request.session.id.private_id` in 13.7
ActiveSession.destroy_with_rack_session_id(user, auth.request.session.id)
ActiveSession.destroy_session(user, auth.request.session.id.private_id) if auth.request.session.id
activity.user_session_destroyed!
##

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddTierToEnvironments < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :environments, :tier, :smallint
end
end
def down
with_lock_retries do
remove_column :environments, :tier
end
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddIndexToEnvironmentsTier < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
INDEX_NAME = 'index_environments_on_project_id_and_tier'
DOWNTIME = false
def up
add_concurrent_index :environments, [:project_id, :tier], where: 'tier IS NOT NULL', name: INDEX_NAME
end
def down
remove_concurrent_index :environments, :state, name: INDEX_NAME
end
end

View File

@ -0,0 +1 @@
6c52ab55814241b37014949976c4f3a0c63bea0a4f9a301735cc9f4c509f433d

View File

@ -0,0 +1 @@
e1641d84828e3d87aea626dbce6b3b2d231d08fcf1475991fe8d11714cdb0af0

View File

@ -12163,7 +12163,8 @@ CREATE TABLE environments (
state character varying DEFAULT 'available'::character varying NOT NULL,
slug character varying NOT NULL,
auto_stop_at timestamp with time zone,
auto_delete_at timestamp with time zone
auto_delete_at timestamp with time zone,
tier smallint
);
CREATE SEQUENCE environments_id_seq
@ -22207,6 +22208,8 @@ CREATE UNIQUE INDEX index_environments_on_project_id_and_name ON environments US
CREATE UNIQUE INDEX index_environments_on_project_id_and_slug ON environments USING btree (project_id, slug);
CREATE INDEX index_environments_on_project_id_and_tier ON environments USING btree (project_id, tier) WHERE (tier IS NOT NULL);
CREATE INDEX index_environments_on_project_id_state_environment_type ON environments USING btree (project_id, state, environment_type);
CREATE INDEX index_environments_on_state_and_auto_stop_at ON environments USING btree (state, auto_stop_at) WHERE ((auto_stop_at IS NOT NULL) AND ((state)::text = 'available'::text));

View File

@ -12,7 +12,7 @@ This is the administration documentation. There is a separate [user documentatio
on issue closing pattern.
When a commit or merge request resolves one or more issues, it is possible to
automatically have these issues closed when the commit or merge request lands
automatically close these issues when the commit or merge request lands
in the project's default branch.
## Change the issue closing pattern
@ -35,7 +35,7 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
expression of your liking:
```ruby
gitlab_rails['gitlab_issue_closing_pattern'] = "\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)"
gitlab_rails['gitlab_issue_closing_pattern'] = /\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)/.source
```
1. [Reconfigure](restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect.

View File

@ -72,7 +72,7 @@ There are also [Kubernetes-specific deployment variables](../../user/project/clu
| `CI_PIPELINE_SOURCE` | 10.0 | all | How the pipeline was triggered. Can be `push`, `web`, `schedule`, `api`, `external`, `chat`, `webide`, `merge_request_event`, `external_pull_request_event`, `parent_pipeline`, [`trigger`, or `pipeline`](../triggers/README.md#authentication-tokens). |
| `CI_PIPELINE_TRIGGERED` | all | all | `true` if the job was [triggered](../triggers/README.md). |
| `CI_PIPELINE_URL` | 11.1 | 0.5 | The URL for the pipeline details. |
| `CI_PROJECT_CONFIG_PATH` | 13.8 | all | The CI/CD configuration path for the project. |
| `CI_PROJECT_CONFIG_PATH` | 13.8 | all | (Deprecated) The CI configuration path for the project. [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/321334) in GitLab 13.10. [Removal planned](https://gitlab.com/gitlab-org/gitlab/-/issues/322807) for GitLab 14.0. |
| `CI_PROJECT_DIR` | all | all | The full path the repository is cloned to, and where the job runs from. If the GitLab Runner `builds_dir` parameter is set, this variable is set relative to the value of `builds_dir`. For more information, see the [Advanced GitLab Runner configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section). |
| `CI_PROJECT_ID` | all | all | The ID of the current project. This ID is unique across all projects on the GitLab instance. |
| `CI_PROJECT_NAME` | 8.10 | 0.5 | The name of the directory for the project. For example if the project URL is `gitlab.example.com/group-name/project-1`, `CI_PROJECT_NAME` is `project-1`. |

View File

@ -109,7 +109,7 @@ Running a DAST scan requires a URL. There are two ways to define the URL to be s
If both values are set, the `DAST_WEBSITE` value takes precedence.
The included template creates a `dast` job in your CI/CD pipeline and scans
your project's source code for possible vulnerabilities.
your project's running application for possible vulnerabilities.
The results are saved as a
[DAST report artifact](../../../ci/pipelines/job_artifacts.md#artifactsreportsdast)
@ -127,6 +127,73 @@ image. Using the `DAST_VERSION` variable, you can choose how DAST updates:
Find the latest DAST versions on the [Releases](https://gitlab.com/gitlab-org/security-products/dast/-/releases) page.
## Deployment options
Depending on the complexity of the target application, there are a few options as to how to deploy and configure
the DAST template. A set of example applications with their configurations have been made available in our
[DAST demonstrations](https://gitlab.com/gitlab-org/security-products/demos/dast/) project.
### Review Apps
Review Apps are the most involved method of deploying your DAST target application. To assist in the process,
we created a Review App deployment using Google Kubernetes Engine (GKE). This example can be found in our
[Review Apps - GKE](https://gitlab.com/gitlab-org/security-products/demos/dast/review-app-gke) project along with detailed
instructions in the [README.md](https://gitlab.com/gitlab-org/security-products/demos/dast/review-app-gke/-/blob/master/README.md)
on how to configure Review Apps for DAST.
### Docker Services
If your application utilizes Docker containers you have another option for deploying and scanning with DAST.
After your Docker build job completes and your image is added to your container registry, you can utilize the image as a
[service](../../../ci/docker/using_docker_images.md#what-is-a-service).
By using service definitions in your `gitlab-ci.yml`, you can scan services with the DAST analyzer.
```yaml
stages:
- build
- dast
include:
- template: DAST.gitlab-ci.yml
# Deploys the container to the GitLab container registry
deploy:
services:
- name: docker:dind
alias: dind
image: docker:19.03.5
stage: build
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker pull $CI_REGISTRY_IMAGE:latest || true
- docker build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
services: # use services to link your app container to the dast job
- name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
alias: yourapp
variables:
DAST_FULL_SCAN_ENABLED: "true" # do a full scan
DAST_ZAP_USE_AJAX_SPIDER: "true" # use the ajax spider
```
Most applications depend on multiple services such as databases or caching services. By default, services defined in the services fields cannot communicate
with each another. To allow communication between services, enable the `FF_NETWORK_PER_BUILD` [feature flag](https://docs.gitlab.com/runner/configuration/feature-flags.html#available-feature-flags).
```yaml
variables:
FF_NETWORK_PER_BUILD: "true" # enable network per build so all services can communicate on the same network
services: # use services to link the container to the dast job
- name: mongo:latest
alias: mongo
- name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
alias: yourapp
```
### DAST application analysis
DAST can analyze applications in two ways:

View File

@ -5,8 +5,7 @@ module API
include PaginationParams
before { authenticate! }
before { authorize! :admin_build, user_group }
before { authorize! :admin_group, user_group }
feature_category :continuous_integration
params do

View File

@ -3,22 +3,6 @@
module Gitlab
module Diff
class InlineDiff
# Regex to find a run of deleted lines followed by the same number of added lines
LINE_PAIRS_PATTERN = %r{
# Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
(?:\A|\s)
# This matches a number of `-`s followed by the same number of `+`s through recursion
(?<del_ins>
-
\g<del_ins>?
\+
)
# Runs end at the end of the string (the last line) or before a space (for an unchanged line)
(?=\s|\z)
}x.freeze
attr_accessor :old_line, :new_line, :offset
def initialize(old_line, new_line, offset: 0)
@ -40,11 +24,11 @@ module Gitlab
class << self
def for_lines(lines, project: nil)
changed_line_pairs = find_changed_line_pairs(lines)
pair_selector = Gitlab::Diff::PairSelector.new(lines)
inline_diffs = []
changed_line_pairs.each do |old_index, new_index|
pair_selector.each do |old_index, new_index|
old_line = lines[old_index]
new_line = lines[new_index]
@ -56,34 +40,6 @@ module Gitlab
inline_diffs
end
private
# Finds pairs of old/new line pairs that represent the same line that changed
# rubocop: disable CodeReuse/ActiveRecord
def find_changed_line_pairs(lines)
# Prefixes of all diff lines, indicating their types
# For example: `" - + -+ ---+++ --+ -++"`
line_prefixes = lines.each_with_object(+"") { |line, s| s << (line[0] || ' ') }.gsub(/[^ +-]/, ' ')
changed_line_pairs = []
line_prefixes.scan(LINE_PAIRS_PATTERN) do
# For `"---+++"`, `begin_index == 0`, `end_index == 6`
begin_index, end_index = Regexp.last_match.offset(:del_ins)
# For `"---+++"`, `changed_line_count == 3`
changed_line_count = (end_index - begin_index) / 2
halfway_index = begin_index + changed_line_count
(begin_index...halfway_index).each do |i|
# For `"---+++"`, index 1 maps to 1 + 3 = 4
changed_line_pairs << [i, i + changed_line_count]
end
end
changed_line_pairs
end
# rubocop: enable CodeReuse/ActiveRecord
end
private

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
module Gitlab
module Diff
class PairSelector
include Enumerable
# Regex to find a run of deleted lines followed by the same number of added lines
# rubocop: disable Lint/MixedRegexpCaptureTypes
LINE_PAIRS_PATTERN = %r{
# Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
(?:\A|\s)
# This matches a number of `-`s followed by the same number of `+`s through recursion
(?<del_ins>
-
\g<del_ins>?
\+
)
# Runs end at the end of the string (the last line) or before a space (for an unchanged line)
(?=\s|\z)
}x.freeze
# rubocop: enable Lint/MixedRegexpCaptureTypes
def initialize(lines)
@lines = lines
end
# Finds pairs of old/new line pairs that represent the same line that changed
# rubocop: disable CodeReuse/ActiveRecord
def each
# Prefixes of all diff lines, indicating their types
# For example: `" - + -+ ---+++ --+ -++"`
line_prefixes = lines.each_with_object(+"") { |line, s| s << (line[0] || ' ') }.gsub(/[^ +-]/, ' ')
line_prefixes.scan(LINE_PAIRS_PATTERN) do
# For `"---+++"`, `begin_index == 0`, `end_index == 6`
begin_index, end_index = Regexp.last_match.offset(:del_ins)
# For `"---+++"`, `changed_line_count == 3`
changed_line_count = (end_index - begin_index) / 2
halfway_index = begin_index + changed_line_count
(begin_index...halfway_index).each do |i|
# For `"---+++"`, index 1 maps to 1 + 3 = 4
yield [i, i + changed_line_count]
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
private
attr_reader :lines
end
end
end

View File

@ -3,6 +3,8 @@
module Gitlab
module Git
class WikiPageVersion
include Gitlab::Utils::StrongMemoize
attr_reader :commit, :format
def initialize(commit, format)
@ -12,9 +14,10 @@ module Gitlab
delegate :message, :sha, :id, :author_name, :author_email, :authored_date, to: :commit
def author_url
user = ::User.find_by_any_email(author_email)
user.nil? ? "mailto:#{author_email}" : Gitlab::UrlBuilder.build(user)
def author
strong_memoize(:author) do
::User.find_by_any_email(author_email)
end
end
end
end

View File

@ -27104,7 +27104,7 @@ msgstr ""
msgid "SelfMonitoring|Self monitoring project has been successfully deleted."
msgstr ""
msgid "Send a separate email notification to Developers."
msgid "Send a single email notification to Owners and Maintainers for new alerts."
msgstr ""
msgid "Send confirmation email"

View File

@ -141,7 +141,7 @@
"sql.js": "^0.4.0",
"string-hash": "1.1.3",
"style-loader": "^1.3.0",
"swagger-ui-dist": "^3.32.4",
"swagger-ui-dist": "^3.43.0",
"three": "^0.84.0",
"three-orbit-controls": "^82.1.0",
"three-stl-loader": "^1.0.4",

View File

@ -5,26 +5,35 @@ require 'spec_helper'
RSpec.describe Groups::VariablesController do
include ExternalAuthorizationServiceHelpers
let(:group) { create(:group) }
let(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:variable) { create(:ci_group_variable, group: group) }
let(:access_level) { :owner }
before do
sign_in(user)
group.add_maintainer(user)
group.add_user(user, access_level)
end
describe 'GET #show' do
let!(:variable) { create(:ci_group_variable, group: group) }
subject do
get :show, params: { group_id: group }, format: :json
end
include_examples 'GET #show lists all variables'
context 'when the user is a maintainer' do
let(:access_level) { :maintainer }
it 'returns not found response' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'PATCH #update' do
let!(:variable) { create(:ci_group_variable, group: group) }
let(:owner) { group }
subject do
@ -37,6 +46,19 @@ RSpec.describe Groups::VariablesController do
end
include_examples 'PATCH #update updates variables'
context 'when the user is a maintainer' do
let(:access_level) { :maintainer }
let(:variables_attributes) do
[{ id: variable.id, key: 'new_key' }]
end
it 'returns not found response' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'with external authorization enabled' do
@ -45,8 +67,6 @@ RSpec.describe Groups::VariablesController do
end
describe 'GET #show' do
let!(:variable) { create(:ci_group_variable, group: group) }
it 'is successful' do
get :show, params: { group_id: group }, format: :json
@ -55,9 +75,6 @@ RSpec.describe Groups::VariablesController do
end
describe 'PATCH #update' do
let!(:variable) { create(:ci_group_variable, group: group) }
let(:owner) { group }
it 'is successful' do
patch :update,
params: {

View File

@ -12,7 +12,7 @@ RSpec.describe Profiles::ActiveSessionsController do
it 'invalidates all remember user tokens' do
ActiveSession.set(user, request)
session_id = request.session.id.public_id
session_id = request.session.id.private_id
user.remember_me!
delete :destroy, params: { id: session_id }

View File

@ -15,6 +15,22 @@ FactoryBot.define do
state { :stopped }
end
trait :production do
tier { :production }
end
trait :staging do
tier { :staging }
end
trait :testing do
tier { :testing }
end
trait :development do
tier { :development }
end
trait :with_review_app do |environment|
transient do
ref { 'master' }

View File

@ -104,7 +104,7 @@ RSpec.describe 'User sees feature flag list', :js do
it 'shows empty page' do
expect(page).to have_text 'Get started with feature flags'
expect(page).to have_selector('.btn-success', text: 'New feature flag')
expect(page).to have_selector('.btn-confirm', text: 'New feature flag')
expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure')
end
end

View File

@ -24,7 +24,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
describe 'Settings > Operations' do
describe 'Incidents' do
let(:create_issue) { 'Create an incident. Incidents are created for each alert triggered.' }
let(:send_email) { 'Send a separate email notification to Developers.' }
let(:send_email) { 'Send a single email notification to Owners and Maintainers for new alerts.' }
before do
create(:project_incident_management_setting, send_email: true, project: project)

View File

@ -37,6 +37,6 @@ RSpec.describe 'User searches project settings', :js do
visit project_settings_operations_path(project)
end
it_behaves_like 'can search settings', 'Alerts', 'Incidents'
it_behaves_like 'can search settings', 'Alerts', 'Error tracking'
end
end

View File

@ -251,7 +251,7 @@ describe('AlertManagementTable', () => {
const tooltip = getBinding(issueField.element, 'gl-tooltip');
expect(issueField.text()).toBe(`#1 (closed)`);
expect(issueField.attributes('href')).toBe('/gitlab-org/gitlab/-/issues/1');
expect(issueField.attributes('href')).toBe('/gitlab-org/gitlab/-/issues/incident/1');
expect(issueField.attributes('title')).toBe('My test issue');
expect(tooltip).not.toBe(undefined);
});

View File

@ -78,7 +78,7 @@ exports[`Alert integration settings form default state should match the default
>
<gl-form-checkbox-stub>
<span>
Send a separate email notification to Developers.
Send a single email notification to Owners and Maintainers for new alerts.
</span>
</gl-form-checkbox-stub>
</gl-form-group-stub>

View File

@ -188,6 +188,18 @@ describe('AlertDetails', () => {
});
expect(findMetricsTab().exists()).toBe(false);
});
it('should display "View incident" button that links the issues page when incident exists', () => {
const iid = '3';
mountComponent({
data: { alert: { ...mockAlert, issue: { iid } }, sidebarStatus: false },
provide: { isThreatMonitoringPage: true },
});
expect(findViewIncidentBtn().exists()).toBe(true);
expect(findViewIncidentBtn().attributes('href')).toBe(joinPaths(projectIssuesPath, iid));
expect(findCreateIncidentBtn().exists()).toBe(false);
});
});
describe('Create incident from alert', () => {
@ -198,7 +210,9 @@ describe('AlertDetails', () => {
});
expect(findViewIncidentBtn().exists()).toBe(true);
expect(findViewIncidentBtn().attributes('href')).toBe(joinPaths(projectIssuesPath, iid));
expect(findViewIncidentBtn().attributes('href')).toBe(
joinPaths(projectIssuesPath, 'incident', iid),
);
expect(findCreateIncidentBtn().exists()).toBe(false);
});

View File

@ -0,0 +1,80 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe WikiPageVersionHelper do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { create(:user, username: 'foo') }
let(:commit_with_user) { create(:commit, project: project, author: user)}
let(:commit_without_user) { create(:commit, project: project, author_name: 'Foo', author_email: 'foo@example.com')}
let(:wiki_page_version) { Gitlab::Git::WikiPageVersion.new(commit, nil) }
describe '#wiki_page_version_author_url' do
subject { helper.wiki_page_version_author_url(wiki_page_version) }
context 'when user exists' do
let(:commit) { commit_with_user }
it 'returns the link to the user profile' do
expect(subject).to eq('http://localhost/foo')
end
end
context 'when user does not exist' do
let(:commit) { commit_without_user }
it 'returns the mailto link' do
expect(subject).to eq "mailto:#{commit_without_user.author_email}"
end
end
end
describe '#wiki_page_version_author_avatar' do
let(:commit) { commit_with_user }
subject { helper.wiki_page_version_author_avatar(wiki_page_version) }
it 'returns the user avatar', :aggregate_failures do
avatar = Nokogiri::HTML.parse(subject)
expect(avatar.css('img')[0].attr('class')).to eq('avatar s24 float-none gl-mr-0! lazy')
expect(avatar.css('img')[0].attr('data-src')).not_to be_empty
expect(avatar.css('img')[0].attr('src')).not_to be_empty
end
end
describe '#wiki_page_version_author_header', :aggregate_failures do
let(:commit_with_xss) { create(:commit, project: project, author_email: "#' style=animation-name:blinking-dot onanimationstart=alert(document.domain) other", author_name: "<i>foo</i>") }
let(:header) { Nokogiri::HTML.parse(subject) }
subject { helper.wiki_page_version_author_header(wiki_page_version) }
context 'when user exists' do
let(:commit) { commit_with_user }
it 'renders commit header with user info' do
expect(header.css('a')[0].attr('href')).to eq("http://localhost/foo")
expect(header.css('a')[0].children[2].to_s).to eq("<strong>#{user.name}</strong>")
end
end
context 'when user does not exist' do
let(:commit) { commit_without_user }
it 'renders commit header with info from commit' do
expect(header.css('a')[0].attr('href')).to eq("mailto:#{commit.author_email}")
expect(header.css('a')[0].children[2].to_s).to eq("<strong>#{wiki_page_version.author_name}</strong>")
end
end
context 'when user info has XSS' do
let(:commit) { commit_with_xss }
it 'sets the right href and escapes HTML chars' do
expect(header.css('a')[0].attr('href')).to eq("mailto:#{commit.author_email}")
expect(header.css('a')[0].children[2].to_s).to eq("<strong>&lt;i&gt;foo&lt;/i&gt;</strong>")
end
end
end
end

View File

@ -0,0 +1,84 @@
# frozen_string_literal: true
require 'fast_spec_helper'
RSpec.describe Gitlab::Diff::PairSelector do
subject(:selector) { described_class.new(lines) }
describe '#to_a' do
subject { selector.to_a }
let(:lines) { diff.lines }
let(:diff) do
<<-EOF.strip_heredoc
class Test # 0
- def initialize(test = true) # 1
+ def initialize(test = false) # 2
@test = test # 3
- if true # 4
- @foo = "bar" # 5
+ unless false # 6
+ @foo = "baz" # 7
end
end
end
EOF
end
it 'finds all pairs' do
is_expected.to match_array([[1, 2], [4, 6], [5, 7]])
end
context 'when there are empty lines' do
let(:lines) { ['- bar', '+ baz', ''] }
it { expect { subject }.not_to raise_error }
end
context 'when there are only removals' do
let(:diff) do
<<-EOF.strip_heredoc
- class Test
- def initialize(test = true)
- end
- end
EOF
end
it 'returns empty collection' do
is_expected.to eq([])
end
end
context 'when there are only additions' do
let(:diff) do
<<-EOF.strip_heredoc
+ class Test
+ def initialize(test = true)
+ end
+ end
EOF
end
it 'returns empty collection' do
is_expected.to eq([])
end
end
context 'when there are no changes' do
let(:diff) do
<<-EOF.strip_heredoc
class Test
def initialize(test = true)
end
end
EOF
end
it 'returns empty collection' do
is_expected.to eq([])
end
end
end
end

View File

@ -4,24 +4,24 @@ require 'spec_helper'
RSpec.describe Gitlab::Git::WikiPageVersion do
let_it_be(:project) { create(:project, :public, :repository) }
let(:user) { create(:user, username: 'someone') }
let_it_be(:user) { create(:user, username: 'someone') }
describe '#author_url' do
subject(:author_url) { described_class.new(commit, nil).author_url }
describe '#author' do
subject(:author) { described_class.new(commit, nil).author }
context 'user exists in gitlab' do
let(:commit) { create(:commit, project: project, author: user) }
it 'returns the profile link of the user' do
expect(author_url).to eq('http://localhost/someone')
it 'returns the user' do
expect(author).to eq user
end
end
context 'user does not exist in gitlab' do
let(:commit) { create(:commit, project: project, author_email: "someone@somewebsite.com") }
it 'returns a mailto: url' do
expect(author_url).to eq('mailto:someone@somewebsite.com')
it 'returns nil' do
expect(author).to be_nil
end
end
end

View File

@ -42,17 +42,6 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end
end
describe '#public_id' do
it 'returns an encrypted, url-encoded session id' do
original_session_id = Rack::Session::SessionId.new("!*'();:@&\n=+$,/?%abcd#123[4567]8")
active_session = ActiveSession.new(session_id: original_session_id.public_id)
encrypted_id = active_session.public_id
derived_session_id = Gitlab::CryptoHelper.aes256_gcm_decrypt(encrypted_id)
expect(original_session_id.public_id).to eq derived_session_id
end
end
describe '.list' do
it 'returns all sessions by user' do
Gitlab::Redis::SharedState.with do |redis|
@ -207,89 +196,9 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end
end
end
context 'ActiveSession stored by deprecated rack_session_public_key' do
let(:active_session) { ActiveSession.new(session_id: rack_session.public_id) }
let(:deprecated_active_session_lookup_key) { rack_session.public_id }
before do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:#{deprecated_active_session_lookup_key}",
'')
redis.sadd(described_class.lookup_key_name(user.id),
deprecated_active_session_lookup_key)
end
end
it 'removes deprecated key and stores only new one' do
expected_session_keys = ["session:user:gitlab:#{user.id}:#{rack_session.private_id}",
"session:lookup:user:gitlab:#{user.id}"]
ActiveSession.set(user, request)
Gitlab::Redis::SharedState.with do |redis|
actual_session_keys = redis.scan_each(match: 'session:*').to_a
expect(actual_session_keys).to(match_array(expected_session_keys))
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq [rack_session.private_id]
end
end
end
end
describe '.destroy_with_rack_session_id' do
it 'gracefully handles a nil session ID' do
expect(described_class).not_to receive(:destroy_sessions)
ActiveSession.destroy_with_rack_session_id(user, nil)
end
it 'removes the entry associated with the currently killed user session' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", '')
redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
end
ActiveSession.destroy_with_rack_session_id(user, request.session.id)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each(match: "session:user:gitlab:*")).to match_array [
"session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d",
"session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358"
]
end
end
it 'removes the lookup entry' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
end
ActiveSession.destroy_with_rack_session_id(user, request.session.id)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty
end
end
it 'removes the devise session' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:#{rack_session.private_id}", '')
# Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88
redis.set("session:gitlab:#{rack_session.private_id}", '')
end
ActiveSession.destroy_with_rack_session_id(user, request.session.id)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty
end
end
end
describe '.destroy_with_deprecated_encryption' do
describe '.destroy_session' do
shared_examples 'removes all session data' do
before do
Gitlab::Redis::SharedState.with do |redis|
@ -330,7 +239,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end
context 'destroy called with Rack::Session::SessionId#private_id' do
subject { ActiveSession.destroy_with_deprecated_encryption(user, rack_session.private_id) }
subject { ActiveSession.destroy_session(user, rack_session.private_id) }
it 'calls .destroy_sessions' do
expect(ActiveSession).to(
@ -347,26 +256,6 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
include_examples 'removes all session data'
end
end
context 'destroy called with ActiveSession#public_id (deprecated)' do
let(:active_session) { ActiveSession.new(session_id: rack_session.public_id) }
let(:encrypted_active_session_id) { active_session.public_id }
let(:active_session_lookup_key) { rack_session.public_id }
subject { ActiveSession.destroy_with_deprecated_encryption(user, encrypted_active_session_id) }
it 'calls .destroy_sessions' do
expect(ActiveSession).to(
receive(:destroy_sessions)
.with(anything, user, [encrypted_active_session_id, rack_session.public_id, rack_session.private_id]))
subject
end
context 'ActiveSession with session_id (deprecated)' do
include_examples 'removes all session data'
end
end
end
describe '.destroy_all_but_current' do

View File

@ -2405,6 +2405,7 @@ RSpec.describe Ci::Build do
{ key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false },
{ key: 'CI_DEFAULT_BRANCH', value: project.default_branch, public: true, masked: false },
{ key: 'CI_PROJECT_CONFIG_PATH', value: project.ci_config_path_or_default, public: true, masked: false },
{ key: 'CI_CONFIG_PATH', value: project.ci_config_path_or_default, public: true, masked: false },
{ key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true, masked: false },
{ key: 'CI_PAGES_URL', value: project.pages_url, public: true, masked: false },
{ key: 'CI_DEPENDENCY_PROXY_SERVER', value: "#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}", public: true, masked: false },
@ -2415,7 +2416,6 @@ RSpec.describe Ci::Build do
{ key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true, masked: false },
{ key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true, masked: false },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
{ key: 'CI_CONFIG_PATH', value: pipeline.config_path, public: true, masked: false },
{ key: 'CI_COMMIT_SHA', value: build.sha, public: true, masked: false },
{ key: 'CI_COMMIT_SHORT_SHA', value: build.short_sha, public: true, masked: false },
{ key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true, masked: false },

View File

@ -807,7 +807,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(keys).to eq %w[
CI_PIPELINE_IID
CI_PIPELINE_SOURCE
CI_CONFIG_PATH
CI_COMMIT_SHA
CI_COMMIT_SHORT_SHA
CI_COMMIT_BEFORE_SHA

View File

@ -34,6 +34,39 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
describe '.before_save' do
it 'ensures environment tier when a new object is created' do
environment = build(:environment, name: 'gprd', tier: nil)
expect { environment.save }.to change { environment.tier }.from(nil).to('production')
end
it 'ensures environment tier when an existing object is updated' do
environment = create(:environment, name: 'gprd')
environment.update_column(:tier, nil)
expect { environment.stop! }.to change { environment.reload.tier }.from(nil).to('production')
end
it 'does not overwrite the existing environment tier' do
environment = create(:environment, name: 'gprd', tier: :production)
expect { environment.update!(name: 'gstg') }.not_to change { environment.reload.tier }
end
context 'when environment_tier feature flag is disabled' do
before do
stub_feature_flags(environment_tier: false)
end
it 'does not ensure environment tier' do
environment = build(:environment, name: 'gprd', tier: nil)
expect { environment.save }.not_to change { environment.tier }
end
end
end
describe '.order_by_last_deployed_at' do
let!(:environment1) { create(:environment, project: project) }
let!(:environment2) { create(:environment, project: project) }
@ -195,6 +228,62 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
describe '.for_tier' do
let_it_be(:environment) { create(:environment, :production) }
it 'returns the production environment when searching for production tier' do
expect(described_class.for_tier(:production)).to eq([environment])
end
it 'returns nothing when searching for staging tier' do
expect(described_class.for_tier(:staging)).to be_empty
end
end
describe '#guess_tier' do
using RSpec::Parameterized::TableSyntax
subject { environment.send(:guess_tier) }
let(:environment) { build(:environment, name: name) }
where(:name, :tier) do
'review/feature' | described_class.tiers[:development]
'review/product' | described_class.tiers[:development]
'DEV' | described_class.tiers[:development]
'development' | described_class.tiers[:development]
'trunk' | described_class.tiers[:development]
'test' | described_class.tiers[:testing]
'TEST' | described_class.tiers[:testing]
'testing' | described_class.tiers[:testing]
'testing-prd' | described_class.tiers[:testing]
'acceptance-testing' | described_class.tiers[:testing]
'QC' | described_class.tiers[:testing]
'gstg' | described_class.tiers[:staging]
'staging' | described_class.tiers[:staging]
'stage' | described_class.tiers[:staging]
'Model' | described_class.tiers[:staging]
'MODL' | described_class.tiers[:staging]
'Pre-production' | described_class.tiers[:staging]
'pre' | described_class.tiers[:staging]
'Demo' | described_class.tiers[:staging]
'gprd' | described_class.tiers[:production]
'gprd-cny' | described_class.tiers[:production]
'production' | described_class.tiers[:production]
'Production' | described_class.tiers[:production]
'prod' | described_class.tiers[:production]
'PROD' | described_class.tiers[:production]
'Live' | described_class.tiers[:production]
'canary' | described_class.tiers[:other]
'other' | described_class.tiers[:other]
'EXP' | described_class.tiers[:other]
end
with_them do
it { is_expected.to eq(tier) }
end
end
describe '#expire_etag_cache' do
let(:store) { Gitlab::EtagCaching::Store.new }

View File

@ -4466,7 +4466,11 @@ RSpec.describe Project, factory_default: :keep do
subject { project.predefined_project_variables.to_runner_variables }
specify do
expect(subject).to include({ key: 'CI_PROJECT_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false })
expect(subject).to include
[
{ key: 'CI_PROJECT_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false },
{ key: 'CI_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false }
]
end
context 'when ci config path is overridden' do
@ -4474,7 +4478,13 @@ RSpec.describe Project, factory_default: :keep do
project.update!(ci_config_path: 'random.yml')
end
it { expect(subject).to include({ key: 'CI_PROJECT_CONFIG_PATH', value: 'random.yml', public: true, masked: false }) }
it do
expect(subject).to include
[
{ key: 'CI_PROJECT_CONFIG_PATH', value: 'random.yml', public: true, masked: false },
{ key: 'CI_CONFIG_PATH', value: 'random.yml', public: true, masked: false }
]
end
end
end

View File

@ -3,16 +3,19 @@
require 'spec_helper'
RSpec.describe API::GroupVariables do
let(:group) { create(:group) }
let(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:variable) { create(:ci_group_variable, group: group) }
let(:access_level) {}
before do
group.add_user(user, access_level) if access_level
end
describe 'GET /groups/:id/variables' do
let!(:variable) { create(:ci_group_variable, group: group) }
context 'authorized user with proper permissions' do
before do
group.add_maintainer(user)
end
let(:access_level) { :owner }
it 'returns group variables' do
get api("/groups/#{group.id}/variables", user)
@ -23,6 +26,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
let(:access_level) { :maintainer }
it 'does not return group variables' do
get api("/groups/#{group.id}/variables", user)
@ -40,12 +45,8 @@ RSpec.describe API::GroupVariables do
end
describe 'GET /groups/:id/variables/:key' do
let!(:variable) { create(:ci_group_variable, group: group) }
context 'authorized user with proper permissions' do
before do
group.add_maintainer(user)
end
let(:access_level) { :owner }
it 'returns group variable details' do
get api("/groups/#{group.id}/variables/#{variable.key}", user)
@ -64,6 +65,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
let(:access_level) { :maintainer }
it 'does not return group variable details' do
get api("/groups/#{group.id}/variables/#{variable.key}", user)
@ -82,11 +85,7 @@ RSpec.describe API::GroupVariables do
describe 'POST /groups/:id/variables' do
context 'authorized user with proper permissions' do
let!(:variable) { create(:ci_group_variable, group: group) }
before do
group.add_maintainer(user)
end
let(:access_level) { :owner }
it 'creates variable' do
expect do
@ -124,6 +123,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
let(:access_level) { :maintainer }
it 'does not create variable' do
post api("/groups/#{group.id}/variables", user)
@ -141,12 +142,8 @@ RSpec.describe API::GroupVariables do
end
describe 'PUT /groups/:id/variables/:key' do
let!(:variable) { create(:ci_group_variable, group: group) }
context 'authorized user with proper permissions' do
before do
group.add_maintainer(user)
end
let(:access_level) { :owner }
it 'updates variable data' do
initial_variable = group.variables.reload.first
@ -180,6 +177,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
let(:access_level) { :maintainer }
it 'does not update variable' do
put api("/groups/#{group.id}/variables/#{variable.key}", user)
@ -197,12 +196,8 @@ RSpec.describe API::GroupVariables do
end
describe 'DELETE /groups/:id/variables/:key' do
let!(:variable) { create(:ci_group_variable, group: group) }
context 'authorized user with proper permissions' do
before do
group.add_maintainer(user)
end
let(:access_level) { :owner }
it 'deletes variable' do
expect do
@ -224,6 +219,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
let(:access_level) { :maintainer }
it 'does not delete variable' do
delete api("/groups/#{group.id}/variables/#{variable.key}", user)

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require 'parser/current'
require_relative '../../rubocop/code_reuse_helpers'

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/active_record_association_reload'
RSpec.describe RuboCop::Cop::ActiveRecordAssociationReload do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/api/base'
RSpec.describe RuboCop::Cop::API::Base do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/api/grape_array_missing_coerce'
RSpec.describe RuboCop::Cop::API::GrapeArrayMissingCoerce do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/avoid_becomes'
RSpec.describe RuboCop::Cop::AvoidBecomes do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/avoid_break_from_strong_memoize'
RSpec.describe RuboCop::Cop::AvoidBreakFromStrongMemoize do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers'
RSpec.describe RuboCop::Cop::AvoidKeywordArgumentsInSidekiqWorkers do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/avoid_return_from_blocks'
RSpec.describe RuboCop::Cop::AvoidReturnFromBlocks do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/avoid_route_redirect_leading_slash'
RSpec.describe RuboCop::Cop::AvoidRouteRedirectLeadingSlash do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/ban_catch_throw'

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/finder'
RSpec.describe RuboCop::Cop::CodeReuse::Finder do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/presenter'
RSpec.describe RuboCop::Cop::CodeReuse::Presenter do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/serializer'
RSpec.describe RuboCop::Cop::CodeReuse::Serializer do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/service_class'
RSpec.describe RuboCop::Cop::CodeReuse::ServiceClass do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/worker'
RSpec.describe RuboCop::Cop::CodeReuse::Worker do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/default_scope'
RSpec.describe RuboCop::Cop::DefaultScope do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/destroy_all'
RSpec.describe RuboCop::Cop::DestroyAll do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../rubocop/cop/filename_length'

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/avoid_uploaded_file_from_params'
RSpec.describe RuboCop::Cop::Gitlab::AvoidUploadedFileFromParams do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/bulk_insert'
RSpec.describe RuboCop::Cop::Gitlab::BulkInsert do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/change_timzone'
RSpec.describe RuboCop::Cop::Gitlab::ChangeTimezone do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/const_get_inherit_false'
RSpec.describe RuboCop::Cop::Gitlab::ConstGetInheritFalse do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/duplicate_spec_location'

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/except'
RSpec.describe RuboCop::Cop::Gitlab::Except do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/finder_with_find_by'

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/httparty'
RSpec.describe RuboCop::Cop::Gitlab::HTTParty do # rubocop:disable RSpec/FilePath

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/intersect'
RSpec.describe RuboCop::Cop::Gitlab::Intersect do

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/json'
RSpec.describe RuboCop::Cop::Gitlab::Json do

Some files were not shown because too many files have changed in this diff Show More