Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e464f195ff
commit
429d1abad2
38 changed files with 451 additions and 108 deletions
|
@ -113,13 +113,21 @@ schedule:review-build-cng:
|
||||||
- install_api_client_dependencies_with_apk
|
- install_api_client_dependencies_with_apk
|
||||||
- source scripts/review_apps/review-apps.sh
|
- source scripts/review_apps/review-apps.sh
|
||||||
script:
|
script:
|
||||||
|
- date
|
||||||
- check_kube_domain
|
- check_kube_domain
|
||||||
|
- date
|
||||||
- ensure_namespace
|
- ensure_namespace
|
||||||
|
- date
|
||||||
- install_tiller
|
- install_tiller
|
||||||
|
- date
|
||||||
- install_external_dns
|
- install_external_dns
|
||||||
|
- date
|
||||||
- download_chart
|
- download_chart
|
||||||
|
- date
|
||||||
- deploy || (display_deployment_debug && exit 1)
|
- deploy || (display_deployment_debug && exit 1)
|
||||||
|
- date
|
||||||
- add_license
|
- add_license
|
||||||
|
- date
|
||||||
artifacts:
|
artifacts:
|
||||||
paths: [review_app_url.txt]
|
paths: [review_app_url.txt]
|
||||||
expire_in: 2 days
|
expire_in: 2 days
|
||||||
|
|
22
Gemfile.lock
22
Gemfile.lock
|
@ -95,7 +95,7 @@ GEM
|
||||||
babosa (1.0.2)
|
babosa (1.0.2)
|
||||||
base32 (0.3.2)
|
base32 (0.3.2)
|
||||||
batch-loader (1.4.0)
|
batch-loader (1.4.0)
|
||||||
bcrypt (3.1.12)
|
bcrypt (3.1.13)
|
||||||
bcrypt_pbkdf (1.0.0)
|
bcrypt_pbkdf (1.0.0)
|
||||||
benchmark-ips (2.3.0)
|
benchmark-ips (2.3.0)
|
||||||
benchmark-memory (0.1.2)
|
benchmark-memory (0.1.2)
|
||||||
|
@ -209,10 +209,10 @@ GEM
|
||||||
descendants_tracker (0.0.4)
|
descendants_tracker (0.0.4)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
device_detector (1.0.0)
|
device_detector (1.0.0)
|
||||||
devise (4.6.2)
|
devise (4.7.1)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (>= 4.1.0, < 6.0)
|
railties (>= 4.1.0)
|
||||||
responders
|
responders
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
devise-two-factor (3.0.0)
|
devise-two-factor (3.0.0)
|
||||||
|
@ -488,7 +488,7 @@ GEM
|
||||||
mime-types (~> 3.0)
|
mime-types (~> 3.0)
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.6.0)
|
i18n (1.7.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
i18n_data (0.8.0)
|
i18n_data (0.8.0)
|
||||||
icalendar (2.4.1)
|
icalendar (2.4.1)
|
||||||
|
@ -770,8 +770,8 @@ GEM
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.2.0)
|
rails-html-sanitizer (1.3.0)
|
||||||
loofah (~> 2.2, >= 2.2.2)
|
loofah (~> 2.3)
|
||||||
rails-i18n (5.1.1)
|
rails-i18n (5.1.1)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 5.0, < 6)
|
railties (>= 5.0, < 6)
|
||||||
|
@ -824,9 +824,9 @@ GEM
|
||||||
declarative-option (< 0.2.0)
|
declarative-option (< 0.2.0)
|
||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
request_store (1.3.1)
|
request_store (1.3.1)
|
||||||
responders (2.4.0)
|
responders (2.4.1)
|
||||||
actionpack (>= 4.2.0, < 5.3)
|
actionpack (>= 4.2.0, < 6.0)
|
||||||
railties (>= 4.2.0, < 5.3)
|
railties (>= 4.2.0, < 6.0)
|
||||||
rest-client (2.0.2)
|
rest-client (2.0.2)
|
||||||
http-cookie (>= 1.0.2, < 2.0)
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
mime-types (>= 1.16, < 4.0)
|
mime-types (>= 1.16, < 4.0)
|
||||||
|
@ -1058,8 +1058,8 @@ GEM
|
||||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||||
equalizer (~> 0.0, >= 0.0.9)
|
equalizer (~> 0.0, >= 0.0.9)
|
||||||
vmstat (2.3.0)
|
vmstat (2.3.0)
|
||||||
warden (1.2.7)
|
warden (1.2.8)
|
||||||
rack (>= 1.0)
|
rack (>= 2.0.6)
|
||||||
webfinger (1.1.0)
|
webfinger (1.1.0)
|
||||||
activesupport
|
activesupport
|
||||||
httpclient (>= 2.4)
|
httpclient (>= 2.4)
|
||||||
|
|
|
@ -7,10 +7,10 @@ import { __ } from '~/locale';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'error', label: __('Open errors') },
|
{ key: 'error', label: __('Open errors'), thClass: 'w-70p' },
|
||||||
{ key: 'events', label: __('Events') },
|
{ key: 'events', label: __('Events') },
|
||||||
{ key: 'users', label: __('Users') },
|
{ key: 'users', label: __('Users') },
|
||||||
{ key: 'lastSeen', label: __('Last seen') },
|
{ key: 'lastSeen', label: __('Last seen'), thClass: 'w-15p' },
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
GlEmptyState,
|
GlEmptyState,
|
||||||
|
@ -67,40 +67,39 @@ export default {
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<gl-button class="my-3 ml-auto" variant="primary" :href="externalUrl" target="_blank">
|
<gl-button class="my-3 ml-auto" variant="primary" :href="externalUrl" target="_blank">
|
||||||
{{ __('View in Sentry') }}
|
{{ __('View in Sentry') }}
|
||||||
<icon name="external-link" />
|
<icon name="external-link" class="flex-shrink-0" />
|
||||||
</gl-button>
|
</gl-button>
|
||||||
</div>
|
</div>
|
||||||
<gl-table :items="errors" :fields="$options.fields" :show-empty="true">
|
|
||||||
|
<gl-table :items="errors" :fields="$options.fields" :show-empty="true" fixed stacked="sm">
|
||||||
<template slot="HEAD_events" slot-scope="data">
|
<template slot="HEAD_events" slot-scope="data">
|
||||||
<div class="text-right">{{ data.label }}</div>
|
<div class="text-md-right">{{ data.label }}</div>
|
||||||
</template>
|
</template>
|
||||||
<template slot="HEAD_users" slot-scope="data">
|
<template slot="HEAD_users" slot-scope="data">
|
||||||
<div class="text-right">{{ data.label }}</div>
|
<div class="text-md-right">{{ data.label }}</div>
|
||||||
</template>
|
</template>
|
||||||
<template slot="error" slot-scope="errors">
|
<template slot="error" slot-scope="errors">
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<div class="d-flex">
|
<gl-link :href="errors.item.externalUrl" class="d-flex text-dark" target="_blank">
|
||||||
<gl-link :href="errors.item.externalUrl" class="d-flex text-dark" target="_blank">
|
<strong class="text-truncate">{{ errors.item.title.trim() }}</strong>
|
||||||
<strong>{{ errors.item.title.trim() }}</strong>
|
<icon name="external-link" class="ml-1 flex-shrink-0" />
|
||||||
<icon name="external-link" class="ml-1" />
|
</gl-link>
|
||||||
</gl-link>
|
<span class="text-secondary text-truncate">
|
||||||
<span class="text-secondary ml-2">{{ errors.item.culprit }}</span>
|
{{ errors.item.culprit }}
|
||||||
</div>
|
</span>
|
||||||
{{ errors.item.message || __('No details available') }}
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template slot="events" slot-scope="errors">
|
<template slot="events" slot-scope="errors">
|
||||||
<div class="text-right">{{ errors.item.count }}</div>
|
<div class="text-md-right">{{ errors.item.count }}</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template slot="users" slot-scope="errors">
|
<template slot="users" slot-scope="errors">
|
||||||
<div class="text-right">{{ errors.item.userCount }}</div>
|
<div class="text-md-right">{{ errors.item.userCount }}</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template slot="lastSeen" slot-scope="errors">
|
<template slot="lastSeen" slot-scope="errors">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<icon name="calendar" class="text-secondary mr-1" />
|
|
||||||
<time-ago :time="errors.item.lastSeen" class="text-secondary" />
|
<time-ago :time="errors.item.lastSeen" class="text-secondary" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -32,6 +32,7 @@ export default class LabelsSelect {
|
||||||
$selectbox,
|
$selectbox,
|
||||||
$sidebarCollapsedValue,
|
$sidebarCollapsedValue,
|
||||||
$value,
|
$value,
|
||||||
|
$dropdownMenu,
|
||||||
abilityName,
|
abilityName,
|
||||||
defaultLabel,
|
defaultLabel,
|
||||||
issueUpdateURL,
|
issueUpdateURL,
|
||||||
|
@ -67,6 +68,7 @@ export default class LabelsSelect {
|
||||||
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
|
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
|
||||||
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
|
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
|
||||||
$value = $block.find('.value');
|
$value = $block.find('.value');
|
||||||
|
$dropdownMenu = $dropdown.parent().find('.dropdown-menu');
|
||||||
$loading = $block.find('.block-loading').fadeOut();
|
$loading = $block.find('.block-loading').fadeOut();
|
||||||
fieldName = $dropdown.data('fieldName');
|
fieldName = $dropdown.data('fieldName');
|
||||||
initialSelected = $selectbox
|
initialSelected = $selectbox
|
||||||
|
@ -454,9 +456,21 @@ export default class LabelsSelect {
|
||||||
}
|
}
|
||||||
|
|
||||||
$loading.fadeIn();
|
$loading.fadeIn();
|
||||||
|
const oldLabels = boardsStore.detail.issue.labels;
|
||||||
|
|
||||||
boardsStore.detail.issue
|
boardsStore.detail.issue
|
||||||
.update($dropdown.attr('data-issue-update'))
|
.update($dropdown.attr('data-issue-update'))
|
||||||
|
.then(() => {
|
||||||
|
if (isScopedLabel(label)) {
|
||||||
|
const prevIds = oldLabels.map(label => label.id);
|
||||||
|
const newIds = boardsStore.detail.issue.labels.map(label => label.id);
|
||||||
|
const differentIds = _.difference(prevIds, newIds);
|
||||||
|
$dropdown.data('marked', newIds);
|
||||||
|
$dropdownMenu
|
||||||
|
.find(differentIds.map(id => `[data-label-id="${id}"]`).join(','))
|
||||||
|
.removeClass('is-active');
|
||||||
|
}
|
||||||
|
})
|
||||||
.then(fadeOutLoader)
|
.then(fadeOutLoader)
|
||||||
.catch(fadeOutLoader);
|
.catch(fadeOutLoader);
|
||||||
} else if (handleClick) {
|
} else if (handleClick) {
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const { protocol, host, pathname } = window.location;
|
|
||||||
const shareBtn = document.querySelector('.js-share-btn');
|
const shareBtn = document.querySelector('.js-share-btn');
|
||||||
const embedBtn = document.querySelector('.js-embed-btn');
|
|
||||||
const snippetUrlArea = document.querySelector('.js-snippet-url-area');
|
|
||||||
const embedAction = document.querySelector('.js-embed-action');
|
|
||||||
const url = `${protocol}//${host + pathname}`;
|
|
||||||
|
|
||||||
shareBtn.addEventListener('click', () => {
|
if (shareBtn) {
|
||||||
shareBtn.classList.add('is-active');
|
const { protocol, host, pathname } = window.location;
|
||||||
embedBtn.classList.remove('is-active');
|
|
||||||
snippetUrlArea.value = url;
|
|
||||||
embedAction.innerText = __('Share');
|
|
||||||
});
|
|
||||||
|
|
||||||
embedBtn.addEventListener('click', () => {
|
const embedBtn = document.querySelector('.js-embed-btn');
|
||||||
embedBtn.classList.add('is-active');
|
|
||||||
shareBtn.classList.remove('is-active');
|
const snippetUrlArea = document.querySelector('.js-snippet-url-area');
|
||||||
const scriptTag = `<script src="${url}.js"></script>`;
|
const embedAction = document.querySelector('.js-embed-action');
|
||||||
snippetUrlArea.value = scriptTag;
|
const url = `${protocol}//${host + pathname}`;
|
||||||
embedAction.innerText = __('Embed');
|
|
||||||
});
|
shareBtn.addEventListener('click', () => {
|
||||||
|
shareBtn.classList.add('is-active');
|
||||||
|
embedBtn.classList.remove('is-active');
|
||||||
|
snippetUrlArea.value = url;
|
||||||
|
embedAction.innerText = __('Share');
|
||||||
|
});
|
||||||
|
|
||||||
|
embedBtn.addEventListener('click', () => {
|
||||||
|
embedBtn.classList.add('is-active');
|
||||||
|
shareBtn.classList.remove('is-active');
|
||||||
|
const scriptTag = `<script src="${url}.js"></script>`;
|
||||||
|
snippetUrlArea.value = scriptTag;
|
||||||
|
embedAction.innerText = __('Embed');
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -452,6 +452,8 @@ img.emoji {
|
||||||
.w-0 { width: 0; }
|
.w-0 { width: 0; }
|
||||||
.w-8em { width: 8em; }
|
.w-8em { width: 8em; }
|
||||||
.w-3rem { width: 3rem; }
|
.w-3rem { width: 3rem; }
|
||||||
|
.w-15p { width: 15%; }
|
||||||
|
.w-70p { width: 70%; }
|
||||||
|
|
||||||
.h-12em { height: 12em; }
|
.h-12em { height: 12em; }
|
||||||
.h-32-px { height: 32px;}
|
.h-32-px { height: 32px;}
|
||||||
|
|
|
@ -29,13 +29,15 @@ module UploadsActions
|
||||||
def show
|
def show
|
||||||
return render_404 unless uploader&.exists?
|
return render_404 unless uploader&.exists?
|
||||||
|
|
||||||
if cache_publicly?
|
# We need to reset caching from the applications controller to get rid of the no-store value
|
||||||
# We need to reset caching from the applications controller to get rid of the no-store value
|
headers['Cache-Control'] = ''
|
||||||
headers['Cache-Control'] = ''
|
headers['Pragma'] = ''
|
||||||
expires_in 5.minutes, public: true, must_revalidate: false
|
|
||||||
else
|
ttl, directives = *cache_settings
|
||||||
expires_in 0.seconds, must_revalidate: true, private: true
|
ttl ||= 6.months
|
||||||
end
|
directives ||= { private: true, must_revalidate: true }
|
||||||
|
|
||||||
|
expires_in ttl, directives
|
||||||
|
|
||||||
disposition = uploader.embeddable? ? 'inline' : 'attachment'
|
disposition = uploader.embeddable? ? 'inline' : 'attachment'
|
||||||
|
|
||||||
|
@ -120,8 +122,8 @@ module UploadsActions
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_publicly?
|
def cache_settings
|
||||||
false
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
def model
|
def model
|
||||||
|
|
|
@ -5,11 +5,22 @@ module Groups
|
||||||
class CiCdController < Groups::ApplicationController
|
class CiCdController < Groups::ApplicationController
|
||||||
skip_cross_project_access_check :show
|
skip_cross_project_access_check :show
|
||||||
before_action :authorize_admin_group!
|
before_action :authorize_admin_group!
|
||||||
|
before_action :authorize_update_max_artifacts_size!, only: [:update]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
define_ci_variables
|
define_ci_variables
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if update_group_service.execute
|
||||||
|
flash[:notice] = s_('GroupSettings|Pipeline settings was updated for the group')
|
||||||
|
else
|
||||||
|
flash[:alert] = s_("GroupSettings|There was a problem updating the pipeline settings: %{error_messages}." % { error_messages: group.errors.full_messages })
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to group_settings_ci_cd_path
|
||||||
|
end
|
||||||
|
|
||||||
def reset_registration_token
|
def reset_registration_token
|
||||||
@group.reset_runners_token!
|
@group.reset_runners_token!
|
||||||
|
|
||||||
|
@ -40,6 +51,10 @@ module Groups
|
||||||
return render_404 unless can?(current_user, :admin_group, group)
|
return render_404 unless can?(current_user, :admin_group, group)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authorize_update_max_artifacts_size!
|
||||||
|
return render_404 unless can?(current_user, :update_max_artifacts_size, group)
|
||||||
|
end
|
||||||
|
|
||||||
def auto_devops_params
|
def auto_devops_params
|
||||||
params.require(:group).permit(:auto_devops_enabled)
|
params.require(:group).permit(:auto_devops_enabled)
|
||||||
end
|
end
|
||||||
|
@ -47,6 +62,14 @@ module Groups
|
||||||
def auto_devops_service
|
def auto_devops_service
|
||||||
Groups::AutoDevopsService.new(group, current_user, auto_devops_params)
|
Groups::AutoDevopsService.new(group, current_user, auto_devops_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_group_service
|
||||||
|
Groups::UpdateService.new(group, current_user, update_group_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_group_params
|
||||||
|
params.require(:group).permit(:max_artifacts_size)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,13 +46,19 @@ module Projects
|
||||||
private
|
private
|
||||||
|
|
||||||
def update_params
|
def update_params
|
||||||
params.require(:project).permit(
|
params.require(:project).permit(*permitted_project_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def permitted_project_params
|
||||||
|
[
|
||||||
:runners_token, :builds_enabled, :build_allow_git_fetch,
|
:runners_token, :builds_enabled, :build_allow_git_fetch,
|
||||||
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
|
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
|
||||||
:auto_cancel_pending_pipelines, :ci_config_path,
|
:auto_cancel_pending_pipelines, :ci_config_path,
|
||||||
auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy],
|
auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy],
|
||||||
ci_cd_settings_attributes: [:default_git_depth]
|
ci_cd_settings_attributes: [:default_git_depth]
|
||||||
)
|
].tap do |list|
|
||||||
|
list << :max_artifacts_size if can?(current_user, :update_max_artifacts_size, project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_autodevops_pipeline(service)
|
def run_autodevops_pipeline(service)
|
||||||
|
|
|
@ -81,8 +81,13 @@ class UploadsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_publicly?
|
def cache_settings
|
||||||
User === model || Appearance === model
|
case model
|
||||||
|
when User, Appearance
|
||||||
|
[5.minutes, { public: true, must_revalidate: false }]
|
||||||
|
when Project, Group
|
||||||
|
[5.minutes, { private: true, must_revalidate: true }]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def secret?
|
def secret?
|
||||||
|
|
|
@ -53,7 +53,10 @@ class GroupPolicy < BasePolicy
|
||||||
enable :upload_file
|
enable :upload_file
|
||||||
end
|
end
|
||||||
|
|
||||||
rule { admin }.enable :read_group
|
rule { admin }.policy do
|
||||||
|
enable :read_group
|
||||||
|
enable :update_max_artifacts_size
|
||||||
|
end
|
||||||
|
|
||||||
rule { has_projects }.policy do
|
rule { has_projects }.policy do
|
||||||
enable :read_group
|
enable :read_group
|
||||||
|
|
|
@ -137,6 +137,8 @@ class ProjectPolicy < BasePolicy
|
||||||
# not.
|
# not.
|
||||||
rule { guest | admin }.enable :read_project_for_iids
|
rule { guest | admin }.enable :read_project_for_iids
|
||||||
|
|
||||||
|
rule { admin }.enable :update_max_artifacts_size
|
||||||
|
|
||||||
rule { guest }.enable :guest_access
|
rule { guest }.enable :guest_access
|
||||||
rule { reporter }.enable :reporter_access
|
rule { reporter }.enable :reporter_access
|
||||||
rule { developer }.enable :developer_access
|
rule { developer }.enable :developer_access
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
= f.number_field :max_artifacts_size, class: 'form-control'
|
= f.number_field :max_artifacts_size, class: 'form-control'
|
||||||
.form-text.text-muted
|
.form-text.text-muted
|
||||||
= _("Set the maximum file size for each job's artifacts")
|
= _("Set the maximum file size for each job's artifacts")
|
||||||
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
|
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size-core-only')
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :default_artifacts_expire_in, _('Default artifacts expiration'), class: 'label-bold'
|
= f.label :default_artifacts_expire_in, _('Default artifacts expiration'), class: 'label-bold'
|
||||||
= f.text_field :default_artifacts_expire_in, class: 'form-control'
|
= f.text_field :default_artifacts_expire_in, class: 'form-control'
|
||||||
|
|
|
@ -30,6 +30,6 @@
|
||||||
= f.check_box :lets_encrypt_terms_of_service_accepted, class: 'form-check-input'
|
= f.check_box :lets_encrypt_terms_of_service_accepted, class: 'form-check-input'
|
||||||
= f.label :lets_encrypt_terms_of_service_accepted, class: 'form-check-label' do
|
= f.label :lets_encrypt_terms_of_service_accepted, class: 'form-check-label' do
|
||||||
- terms_of_service_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: lets_encrypt_terms_of_service_admin_application_settings_path }
|
- terms_of_service_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: lets_encrypt_terms_of_service_admin_application_settings_path }
|
||||||
= _("I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end}").html_safe % { link_start: terms_of_service_link_start, link_end: '</a>'.html_safe }
|
= _("I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end} (PDF)").html_safe % { link_start: terms_of_service_link_start, link_end: '</a>'.html_safe }
|
||||||
|
|
||||||
= f.submit _('Save changes'), class: "btn btn-success"
|
= f.submit _('Save changes'), class: "btn btn-success"
|
||||||
|
|
13
app/views/groups/settings/ci_cd/_form.html.haml
Normal file
13
app/views/groups/settings/ci_cd/_form.html.haml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
.row.prepend-top-default
|
||||||
|
.col-lg-12
|
||||||
|
= form_for group, url: group_settings_ci_cd_path(group, anchor: 'js-general-pipeline-settings') do |f|
|
||||||
|
= form_errors(group)
|
||||||
|
%fieldset.builds-feature
|
||||||
|
.form-group
|
||||||
|
= f.label :max_artifacts_size, _('Maximum artifacts size (MB)'), class: 'label-bold'
|
||||||
|
= f.number_field :max_artifacts_size, class: 'form-control'
|
||||||
|
%p.form-text.text-muted
|
||||||
|
= _("Set the maximum file size for each job's artifacts")
|
||||||
|
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size-core-only'), target: '_blank'
|
||||||
|
|
||||||
|
= f.submit _('Save changes'), class: "btn btn-success"
|
|
@ -2,6 +2,21 @@
|
||||||
- page_title "CI / CD"
|
- page_title "CI / CD"
|
||||||
|
|
||||||
- expanded = expanded_by_default?
|
- expanded = expanded_by_default?
|
||||||
|
- general_expanded = @group.errors.empty? ? expanded : true
|
||||||
|
|
||||||
|
-# Given we only have one field in this form which is also admin-only,
|
||||||
|
-# we don't want to show an empty section to non-admin users,
|
||||||
|
- if can?(current_user, :update_max_artifacts_size, @group)
|
||||||
|
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
|
||||||
|
.settings-header
|
||||||
|
%h4
|
||||||
|
= _("General pipelines")
|
||||||
|
%button.btn.js-settings-toggle{ type: 'button' }
|
||||||
|
= expanded ? _('Collapse') : _('Expand')
|
||||||
|
%p
|
||||||
|
= _("Customize your pipeline configuration.")
|
||||||
|
.settings-content
|
||||||
|
= render 'groups/settings/ci_cd/form', group: @group
|
||||||
|
|
||||||
%section.settings#ci-variables.no-animate{ class: ('expanded' if expanded) }
|
%section.settings#ci-variables.no-animate{ class: ('expanded' if expanded) }
|
||||||
.settings-header
|
.settings-header
|
||||||
|
|
|
@ -40,6 +40,15 @@
|
||||||
= _('If any job surpasses this timeout threshold, it will be marked as failed. Human readable time input language is accepted like "1 hour". Values without specification represent seconds.')
|
= _('If any job surpasses this timeout threshold, it will be marked as failed. Human readable time input language is accepted like "1 hour". Values without specification represent seconds.')
|
||||||
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout'), target: '_blank'
|
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout'), target: '_blank'
|
||||||
|
|
||||||
|
- if can?(current_user, :update_max_artifacts_size, @project)
|
||||||
|
%hr
|
||||||
|
.form-group
|
||||||
|
= f.label :max_artifacts_size, _('Maximum artifacts size (MB)'), class: 'label-bold'
|
||||||
|
= f.number_field :max_artifacts_size, class: 'form-control'
|
||||||
|
%p.form-text.text-muted
|
||||||
|
= _("Set the maximum file size for each job's artifacts")
|
||||||
|
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size-core-only'), target: '_blank'
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :ci_config_path, _('Custom CI config path'), class: 'label-bold'
|
= f.label :ci_config_path, _('Custom CI config path'), class: 'label-bold'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# frozen_string_literal: false
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class MigrateExternalDiffsWorker
|
class MigrateExternalDiffsWorker
|
||||||
include ApplicationWorker
|
include ApplicationWorker
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# frozen_string_literal: false
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ScheduleMigrateExternalDiffsWorker
|
class ScheduleMigrateExternalDiffsWorker
|
||||||
include ApplicationWorker
|
include ApplicationWorker
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Worker for updating project statistics.
|
# Worker for updating project statistics.
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add max_artifacts_size fields under project and group settings.
|
||||||
|
merge_request: 18286
|
||||||
|
author:
|
||||||
|
type: added
|
5
changelogs/unreleased/psi-responsive-error-tracking.yml
Normal file
5
changelogs/unreleased/psi-responsive-error-tracking.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix error tracking table layout on small screens
|
||||||
|
merge_request: 18325
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -30,7 +30,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
|
||||||
as: :group,
|
as: :group,
|
||||||
constraints: { group_id: Gitlab::PathRegex.full_namespace_route_regex }) do
|
constraints: { group_id: Gitlab::PathRegex.full_namespace_route_regex }) do
|
||||||
namespace :settings do
|
namespace :settings do
|
||||||
resource :ci_cd, only: [:show], controller: 'ci_cd' do
|
resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do
|
||||||
put :reset_registration_token
|
put :reset_registration_token
|
||||||
patch :update_auto_devops
|
patch :update_auto_devops
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,7 +68,7 @@ Omnibus:
|
||||||
gitaly['enable'] = false
|
gitaly['enable'] = false
|
||||||
|
|
||||||
redis['bind'] = '0.0.0.0'
|
redis['bind'] = '0.0.0.0'
|
||||||
redis['port'] = '6379'
|
redis['port'] = 6379
|
||||||
redis['password'] = 'SECRET_PASSWORD_HERE'
|
redis['password'] = 'SECRET_PASSWORD_HERE'
|
||||||
|
|
||||||
gitlab_rails['auto_migrate'] = false
|
gitlab_rails['auto_migrate'] = false
|
||||||
|
|
|
@ -99,6 +99,7 @@ description: 'Learn how to contribute to GitLab.'
|
||||||
- [Post deployment migrations](post_deployment_migrations.md)
|
- [Post deployment migrations](post_deployment_migrations.md)
|
||||||
- [Background migrations](background_migrations.md)
|
- [Background migrations](background_migrations.md)
|
||||||
- [Swapping tables](swapping_tables.md)
|
- [Swapping tables](swapping_tables.md)
|
||||||
|
- [Deleting exiting migrations](deleting_migrations.md)
|
||||||
|
|
||||||
### Best practices
|
### Best practices
|
||||||
|
|
||||||
|
@ -118,7 +119,7 @@ description: 'Learn how to contribute to GitLab.'
|
||||||
- [Database helper modules](database_helpers.md)
|
- [Database helper modules](database_helpers.md)
|
||||||
- [Code comments](code_comments.md)
|
- [Code comments](code_comments.md)
|
||||||
|
|
||||||
## Case studies
|
### Case studies
|
||||||
|
|
||||||
- [Database case study: Filtering by label](filtering_by_label.md)
|
- [Database case study: Filtering by label](filtering_by_label.md)
|
||||||
- [Database case study: Namespaces storage statistics](namespaces_storage_statistics.md)
|
- [Database case study: Namespaces storage statistics](namespaces_storage_statistics.md)
|
||||||
|
|
33
doc/development/deleting_migrations.md
Normal file
33
doc/development/deleting_migrations.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Delete existing migrations
|
||||||
|
|
||||||
|
When removing existing migrations from the GitLab project, you have to take into account
|
||||||
|
the possibility of the migration already been included in past releases or in the current release, and thus already executed on GitLab.com and/or in self-hosted instances.
|
||||||
|
|
||||||
|
Because of it, it's not possible to delete existing migrations, as that could lead to:
|
||||||
|
|
||||||
|
- Schema inconsistency, as changes introduced into the database were not rollbacked properly.
|
||||||
|
- Leaving a record on the `schema_versions` table, that points out to migration that no longer exists on the codebase.
|
||||||
|
|
||||||
|
Instead of deleting we can opt for disabling the migration.
|
||||||
|
|
||||||
|
## Pre-requisites to disable a migration
|
||||||
|
|
||||||
|
Migrations can be disabled if:
|
||||||
|
|
||||||
|
- They caused a timeout or general issue on GitLab.com.
|
||||||
|
- They are obsoleted, e.g. changes are not necessary due to a feature change.
|
||||||
|
- Migration is a data migration only, i.e. the migration does not change the database schema.
|
||||||
|
|
||||||
|
## How to disable a data migration?
|
||||||
|
|
||||||
|
In order to disable a migration, the following steps apply to all types of migrations:
|
||||||
|
|
||||||
|
1. Turn the migration into a noop by removing the code inside `#up`, `#down`
|
||||||
|
or `#perform` methods, and adding `#no-op` comment instead.
|
||||||
|
1. Add a comment explaining why the code is gone.
|
||||||
|
|
||||||
|
Disabling migrations requires explicit approval of Database Maintainer.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
- [Disable scheduling of productivity analytics](https://gitlab.com/gitlab-org/gitlab/merge_requests/17253)
|
|
@ -29,15 +29,27 @@ If you want to disable it for a specific project, you can do so in
|
||||||
## Maximum artifacts size **(CORE ONLY)**
|
## Maximum artifacts size **(CORE ONLY)**
|
||||||
|
|
||||||
The maximum size of the [job artifacts](../../../administration/job_artifacts.md)
|
The maximum size of the [job artifacts](../../../administration/job_artifacts.md)
|
||||||
can be set in the Admin area of your GitLab instance. The value is in *MB* and
|
can be set at the project level, group level, and at the instance level. The value is in *MB* and
|
||||||
the default is 100MB per job; on GitLab.com it's [set to 1G](../../gitlab_com/index.md#gitlab-cicd).
|
the default is 100MB per job; on GitLab.com it's [set to 1G](../../gitlab_com/index.md#gitlab-cicd).
|
||||||
|
|
||||||
To change it:
|
To change it at the instance level:
|
||||||
|
|
||||||
1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
|
1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
|
||||||
1. Change the value of maximum artifacts size (in MB).
|
1. Change the value of maximum artifacts size (in MB).
|
||||||
1. Hit **Save changes** for the changes to take effect.
|
1. Hit **Save changes** for the changes to take effect.
|
||||||
|
|
||||||
|
at the group level (this will override the instance setting):
|
||||||
|
|
||||||
|
1. Go to **Group > Settings > CI / CD > General Pipelines**.
|
||||||
|
1. Change the value of maximum artifacts size (in MB).
|
||||||
|
1. Hit **Save changes** for the changes to take effect.
|
||||||
|
|
||||||
|
at the project level (this will override the instance and group settings):
|
||||||
|
|
||||||
|
1. Go to **Project > Settings > CI / CD > General Pipelines**.
|
||||||
|
1. Change the value of maximum artifacts size (in MB).
|
||||||
|
1. Hit **Save changes** for the changes to take effect.
|
||||||
|
|
||||||
## Default artifacts expiration **(CORE ONLY)**
|
## Default artifacts expiration **(CORE ONLY)**
|
||||||
|
|
||||||
The default expiration time of the [job artifacts](../../../administration/job_artifacts.md)
|
The default expiration time of the [job artifacts](../../../administration/job_artifacts.md)
|
||||||
|
|
|
@ -4026,6 +4026,9 @@ msgstr ""
|
||||||
msgid "Code owners"
|
msgid "Code owners"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "CodeAnalytics|Max files"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "CodeOwner|Pattern"
|
msgid "CodeOwner|Pattern"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -4840,6 +4843,9 @@ msgstr ""
|
||||||
msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
|
msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Customize your pipeline configuration."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cycle Analytics"
|
msgid "Cycle Analytics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8222,6 +8228,9 @@ msgstr ""
|
||||||
msgid "GroupSettings|New runners registration token has been generated!"
|
msgid "GroupSettings|New runners registration token has been generated!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "GroupSettings|Pipeline settings was updated for the group"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
|
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8234,6 +8243,9 @@ msgstr ""
|
||||||
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
|
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "GroupSettings|There was a problem updating the pipeline settings: %{error_messages}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
|
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -8479,7 +8491,7 @@ msgstr ""
|
||||||
msgid "I forgot my password"
|
msgid "I forgot my password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end}"
|
msgid "I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end} (PDF)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "I'd like to receive updates via email about GitLab"
|
msgid "I'd like to receive updates via email about GitLab"
|
||||||
|
@ -10758,9 +10770,6 @@ msgstr ""
|
||||||
msgid "No deployments found"
|
msgid "No deployments found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "No details available"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "No due date"
|
msgid "No due date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -156,4 +156,58 @@ describe Groups::Settings::CiCdController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'PATCH #update' do
|
||||||
|
subject do
|
||||||
|
patch :update, params: {
|
||||||
|
group_id: group,
|
||||||
|
group: { max_artifacts_size: 10 }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not an admin' do
|
||||||
|
before do
|
||||||
|
group.add_owner(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to have_gitlab_http_status(404) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is an admin' do
|
||||||
|
let(:user) { create(:admin) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
group.add_owner(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to redirect_to(group_settings_ci_cd_path) }
|
||||||
|
|
||||||
|
context 'when service execution went wrong' do
|
||||||
|
let(:update_service) { double }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Groups::UpdateService).to receive(:new).and_return(update_service)
|
||||||
|
allow(update_service).to receive(:execute).and_return(false)
|
||||||
|
allow_any_instance_of(Group).to receive_message_chain(:errors, :full_messages)
|
||||||
|
.and_return(['Error 1'])
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a flash alert' do
|
||||||
|
expect(response).to set_flash[:alert]
|
||||||
|
.to eq("There was a problem updating the pipeline settings: [\"Error 1\"].")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when service execution was successful' do
|
||||||
|
it 'returns a flash notice' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to set_flash[:notice]
|
||||||
|
.to eq('Pipeline settings was updated for the group')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -215,6 +215,30 @@ describe Projects::Settings::CiCdController do
|
||||||
expect(project.ci_default_git_depth).to eq(10)
|
expect(project.ci_default_git_depth).to eq(10)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when max_artifacts_size is specified' do
|
||||||
|
let(:params) { { max_artifacts_size: 10 } }
|
||||||
|
|
||||||
|
context 'and user is not an admin' do
|
||||||
|
it 'does not set max_artifacts_size' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
project.reload
|
||||||
|
expect(project.max_artifacts_size).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and user is an admin' do
|
||||||
|
let(:user) { create(:admin) }
|
||||||
|
|
||||||
|
it 'sets max_artifacts_size' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
project.reload
|
||||||
|
expect(project.max_artifacts_size).to eq(10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
shared_examples 'content not cached without revalidation' do
|
shared_examples 'content 5 min private cached with revalidation' do
|
||||||
it 'ensures content will not be cached without revalidation' do
|
it 'ensures content will not be cached without revalidation' do
|
||||||
expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate')
|
expect(subject['Cache-Control']).to eq('max-age=300, private, must-revalidate')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'content not cached without revalidation and no-store' do
|
shared_examples 'content long term private cached with revalidation' do
|
||||||
it 'ensures content will not be cached without revalidation' do
|
it 'ensures content will not be cached without revalidation' do
|
||||||
# Fixed in newer versions of ActivePack, it will only output a single `private`.
|
expect(subject['Cache-Control']).to eq('max-age=15778476, private, must-revalidate')
|
||||||
expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -285,7 +284,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation' do
|
it_behaves_like 'content 5 min private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' }
|
get :show, params: { model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -305,7 +304,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation and no-store' do
|
it_behaves_like 'content 5 min private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' }
|
get :show, params: { model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -358,7 +357,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation and no-store' do
|
it_behaves_like 'content 5 min private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' }
|
get :show, params: { model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -390,7 +389,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation' do
|
it_behaves_like 'content 5 min private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' }
|
get :show, params: { model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -410,7 +409,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation and no-store' do
|
it_behaves_like 'content 5 min private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' }
|
get :show, params: { model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -454,7 +453,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation and no-store' do
|
it_behaves_like 'content 5 min private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' }
|
get :show, params: { model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -491,7 +490,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation' do
|
it_behaves_like 'content long term private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }
|
get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -511,7 +510,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation and no-store' do
|
it_behaves_like 'content long term private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }
|
get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
@ -564,7 +563,7 @@ describe UploadsController do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'content not cached without revalidation and no-store' do
|
it_behaves_like 'content long term private cached with revalidation' do
|
||||||
subject do
|
subject do
|
||||||
get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }
|
get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ describe 'Issue Boards', :js do
|
||||||
let!(:bug) { create(:label, project: project, name: 'Bug') }
|
let!(:bug) { create(:label, project: project, name: 'Bug') }
|
||||||
let!(:regression) { create(:label, project: project, name: 'Regression') }
|
let!(:regression) { create(:label, project: project, name: 'Regression') }
|
||||||
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
|
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
|
||||||
|
let!(:scoped_label_1) { create(:label, project: project, name: 'Scoped::Label1') }
|
||||||
|
let!(:scoped_label_2) { create(:label, project: project, name: 'Scoped::Label2') }
|
||||||
let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], relative_position: 2) }
|
let!(:issue1) { create(:labeled_issue, project: project, assignees: [user], milestone: milestone, labels: [development], relative_position: 2) }
|
||||||
let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
|
let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
|
||||||
let(:board) { create(:board, project: project) }
|
let(:board) { create(:board, project: project) }
|
||||||
|
@ -27,6 +29,8 @@ describe 'Issue Boards', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_licensed_features(scoped_labels: true)
|
||||||
|
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
@ -309,6 +313,33 @@ describe 'Issue Boards', :js do
|
||||||
expect(card).to have_content(bug.title)
|
expect(card).to have_content(bug.title)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'removes existing scoped label' do
|
||||||
|
click_card(card)
|
||||||
|
|
||||||
|
page.within('.labels') do
|
||||||
|
click_link 'Edit'
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
click_link scoped_label_1.title
|
||||||
|
click_link scoped_label_2.title
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
find('.dropdown-menu-close-icon').click
|
||||||
|
|
||||||
|
page.within('.value') do
|
||||||
|
expect(page).to have_selector('.badge', count: 3)
|
||||||
|
expect(page).not_to have_content(scoped_label_1.title)
|
||||||
|
expect(page).to have_content(scoped_label_2.title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(card).to have_selector('.badge', count: 3)
|
||||||
|
expect(card).not_to have_content(scoped_label_1.title)
|
||||||
|
expect(card).to have_content(scoped_label_2.title)
|
||||||
|
end
|
||||||
|
|
||||||
it 'adds a multiple labels' do
|
it 'adds a multiple labels' do
|
||||||
click_card(card)
|
click_card(card)
|
||||||
|
|
||||||
|
|
22
spec/features/snippets/private_snippets_spec.rb
Normal file
22
spec/features/snippets/private_snippets_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'Private Snippets', :js do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'Private Snippet renders for creator' do
|
||||||
|
private_snippet = create(:personal_snippet, :private, author: user)
|
||||||
|
|
||||||
|
visit snippet_path(private_snippet)
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
expect(page).to have_content(private_snippet.content)
|
||||||
|
expect(page).not_to have_css('.js-embed-btn')
|
||||||
|
expect(page).not_to have_css('.js-share-btn')
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,6 +10,8 @@ describe 'Public Snippets', :js do
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
|
|
||||||
expect(page).to have_content(public_snippet.content)
|
expect(page).to have_content(public_snippet.content)
|
||||||
|
expect(page).to have_css('.js-embed-btn', visible: false)
|
||||||
|
expect(page).to have_css('.js-share-btn', visible: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'Unauthenticated user should see raw public snippets' do
|
it 'Unauthenticated user should see raw public snippets' do
|
||||||
|
|
|
@ -45,7 +45,9 @@ describe 'User creates snippet', :js do
|
||||||
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z})
|
expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z})
|
||||||
|
|
||||||
reqs = inspect_requests { visit(link) }
|
# Adds a cache buster for checking if the image exists as Selenium is now handling the cached regquests
|
||||||
|
# not anymore as requests when they come straight from memory cache.
|
||||||
|
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
|
||||||
expect(reqs.first.status_code).to eq(200)
|
expect(reqs.first.status_code).to eq(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -63,7 +65,7 @@ describe 'User creates snippet', :js do
|
||||||
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
|
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
|
||||||
|
|
||||||
reqs = inspect_requests { visit(link) }
|
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
|
||||||
expect(reqs.first.status_code).to eq(200)
|
expect(reqs.first.status_code).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -88,7 +90,7 @@ describe 'User creates snippet', :js do
|
||||||
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
|
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
|
||||||
|
|
||||||
reqs = inspect_requests { visit(link) }
|
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
|
||||||
expect(reqs.first.status_code).to eq(200)
|
expect(reqs.first.status_code).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -547,4 +547,28 @@ describe GroupPolicy do
|
||||||
groups: [clusterable])
|
groups: [clusterable])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'update_max_artifacts_size' do
|
||||||
|
let(:group) { create(:group, :public) }
|
||||||
|
|
||||||
|
context 'when no user' do
|
||||||
|
let(:current_user) { nil }
|
||||||
|
|
||||||
|
it { expect_disallowed(:update_max_artifacts_size) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'admin' do
|
||||||
|
let(:current_user) { admin }
|
||||||
|
|
||||||
|
it { expect_allowed(:update_max_artifacts_size) }
|
||||||
|
end
|
||||||
|
|
||||||
|
%w(guest reporter developer maintainer owner).each do |role|
|
||||||
|
context role do
|
||||||
|
let(:current_user) { send(role) }
|
||||||
|
|
||||||
|
it { expect_disallowed(:update_max_artifacts_size) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -478,4 +478,28 @@ describe ProjectPolicy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'update_max_artifacts_size' do
|
||||||
|
subject { described_class.new(current_user, project) }
|
||||||
|
|
||||||
|
context 'when no user' do
|
||||||
|
let(:current_user) { nil }
|
||||||
|
|
||||||
|
it { expect_disallowed(:update_max_artifacts_size) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'admin' do
|
||||||
|
let(:current_user) { admin }
|
||||||
|
|
||||||
|
it { expect_allowed(:update_max_artifacts_size) }
|
||||||
|
end
|
||||||
|
|
||||||
|
%w(guest reporter developer maintainer owner).each do |role|
|
||||||
|
context role do
|
||||||
|
let(:current_user) { send(role) }
|
||||||
|
|
||||||
|
it { expect_disallowed(:update_max_artifacts_size) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -148,34 +148,25 @@ describe 'OpenID Connect requests' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# These 2 calls shouldn't actually throw, they should be handled as an
|
|
||||||
# unauthorized request, so we should be able to check the response.
|
|
||||||
#
|
|
||||||
# This was not possible due to an issue with Warden:
|
|
||||||
# https://github.com/hassox/warden/pull/162
|
|
||||||
#
|
|
||||||
# When the patch gets merged and we update Warden, these specs will need to
|
|
||||||
# updated to check the response instead of a raised exception.
|
|
||||||
# https://gitlab.com/gitlab-org/gitlab-foss/issues/40218
|
|
||||||
context 'when user is blocked' do
|
context 'when user is blocked' do
|
||||||
it 'returns authentication error' do
|
it 'redirects to login page' do
|
||||||
access_grant
|
access_grant
|
||||||
user.block!
|
user.block!
|
||||||
|
|
||||||
expect do
|
request_access_token!
|
||||||
request_access_token!
|
|
||||||
end.to raise_error UncaughtThrowError
|
expect(response).to redirect_to('/users/sign_in')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is ldap_blocked' do
|
context 'when user is ldap_blocked' do
|
||||||
it 'returns authentication error' do
|
it 'redirects to login page' do
|
||||||
access_grant
|
access_grant
|
||||||
user.ldap_block!
|
user.ldap_block!
|
||||||
|
|
||||||
expect do
|
request_access_token!
|
||||||
request_access_token!
|
|
||||||
end.to raise_error UncaughtThrowError
|
expect(response).to redirect_to('/users/sign_in')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue