Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6609e5ea75
commit
7ff2de7c12
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
cdb02af5b322de1f4091b39c349579b2e335b914
|
||||
14b985f2d0add8bd3b6fbf01d06e3bbafcb12f6c
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
8.64.0
|
||||
8.64.2
|
||||
|
|
|
|||
3
Gemfile
3
Gemfile
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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.'),
|
||||
|
|
|
|||
|
|
@ -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')],
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update project page buttons to conform to design system
|
||||
merge_request: 53260
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change the button to Primary Blue
|
||||
merge_request: 55204
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update text for email config under Incidents in Settings > Operations
|
||||
merge_request: 55753
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow alert to link to incidents
|
||||
merge_request: 55426
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add tier column to the environments table
|
||||
merge_request: 55471
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Move CI_CONFIG_PATH as project variable and deprecate CI_PROJECT_CONFIG_PATH
|
||||
merge_request: 54498
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix double scrollbars in some dropdowns
|
||||
merge_request: 54837
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'Workhorse: prevent escaped router path traversal'
|
||||
merge_request:
|
||||
author:
|
||||
type: security
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'Workhorse: Stop logging when path is excluded'
|
||||
merge_request:
|
||||
author:
|
||||
type: security
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix horizontal alignment of MR Changes cog menu dropdown checkboxes
|
||||
merge_request: 55591
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -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
|
||||
|
|
@ -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!
|
||||
|
||||
##
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
6c52ab55814241b37014949976c4f3a0c63bea0a4f9a301735cc9f4c509f433d
|
||||
|
|
@ -0,0 +1 @@
|
|||
e1641d84828e3d87aea626dbce6b3b2d231d08fcf1475991fe8d11714cdb0af0
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`. |
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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><i>foo</i></strong>")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rubocop'
|
||||
require 'parser/current'
|
||||
require_relative '../../rubocop/code_reuse_helpers'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rubocop'
|
||||
|
||||
require_relative '../../../rubocop/cop/ban_catch_throw'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rubocop'
|
||||
require 'rubocop/rspec/support'
|
||||
require_relative '../../../rubocop/cop/filename_length'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rubocop'
|
||||
|
||||
require_relative '../../../../rubocop/cop/gitlab/duplicate_spec_location'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rubocop'
|
||||
|
||||
require_relative '../../../../rubocop/cop/gitlab/finder_with_find_by'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue