Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2019-10-14 15:06:07 +00:00
parent e464f195ff
commit 429d1abad2
38 changed files with 451 additions and 108 deletions

View File

@ -113,13 +113,21 @@ schedule:review-build-cng:
- install_api_client_dependencies_with_apk
- source scripts/review_apps/review-apps.sh
script:
- date
- check_kube_domain
- date
- ensure_namespace
- date
- install_tiller
- date
- install_external_dns
- date
- download_chart
- date
- deploy || (display_deployment_debug && exit 1)
- date
- add_license
- date
artifacts:
paths: [review_app_url.txt]
expire_in: 2 days

View File

@ -95,7 +95,7 @@ GEM
babosa (1.0.2)
base32 (0.3.2)
batch-loader (1.4.0)
bcrypt (3.1.12)
bcrypt (3.1.13)
bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0)
benchmark-memory (0.1.2)
@ -209,10 +209,10 @@ GEM
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
devise (4.6.2)
devise (4.7.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 6.0)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise-two-factor (3.0.0)
@ -488,7 +488,7 @@ GEM
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.6.0)
i18n (1.7.0)
concurrent-ruby (~> 1.0)
i18n_data (0.8.0)
icalendar (2.4.1)
@ -770,8 +770,8 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.2.0)
loofah (~> 2.2, >= 2.2.2)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
rails-i18n (5.1.1)
i18n (>= 0.7, < 2)
railties (>= 5.0, < 6)
@ -824,9 +824,9 @@ GEM
declarative-option (< 0.2.0)
uber (< 0.2.0)
request_store (1.3.1)
responders (2.4.0)
actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3)
responders (2.4.1)
actionpack (>= 4.2.0, < 6.0)
railties (>= 4.2.0, < 6.0)
rest-client (2.0.2)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
@ -1058,8 +1058,8 @@ GEM
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
vmstat (2.3.0)
warden (1.2.7)
rack (>= 1.0)
warden (1.2.8)
rack (>= 2.0.6)
webfinger (1.1.0)
activesupport
httpclient (>= 2.4)

View File

@ -7,10 +7,10 @@ import { __ } from '~/locale';
export default {
fields: [
{ key: 'error', label: __('Open errors') },
{ key: 'error', label: __('Open errors'), thClass: 'w-70p' },
{ key: 'events', label: __('Events') },
{ key: 'users', label: __('Users') },
{ key: 'lastSeen', label: __('Last seen') },
{ key: 'lastSeen', label: __('Last seen'), thClass: 'w-15p' },
],
components: {
GlEmptyState,
@ -67,40 +67,39 @@ export default {
<div class="d-flex justify-content-end">
<gl-button class="my-3 ml-auto" variant="primary" :href="externalUrl" target="_blank">
{{ __('View in Sentry') }}
<icon name="external-link" />
<icon name="external-link" class="flex-shrink-0" />
</gl-button>
</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">
<div class="text-right">{{ data.label }}</div>
<div class="text-md-right">{{ data.label }}</div>
</template>
<template slot="HEAD_users" slot-scope="data">
<div class="text-right">{{ data.label }}</div>
<div class="text-md-right">{{ data.label }}</div>
</template>
<template slot="error" slot-scope="errors">
<div class="d-flex flex-column">
<div class="d-flex">
<gl-link :href="errors.item.externalUrl" class="d-flex text-dark" target="_blank">
<strong>{{ errors.item.title.trim() }}</strong>
<icon name="external-link" class="ml-1" />
</gl-link>
<span class="text-secondary ml-2">{{ errors.item.culprit }}</span>
</div>
{{ errors.item.message || __('No details available') }}
<gl-link :href="errors.item.externalUrl" class="d-flex text-dark" target="_blank">
<strong class="text-truncate">{{ errors.item.title.trim() }}</strong>
<icon name="external-link" class="ml-1 flex-shrink-0" />
</gl-link>
<span class="text-secondary text-truncate">
{{ errors.item.culprit }}
</span>
</div>
</template>
<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 slot="users" slot-scope="errors">
<div class="text-right">{{ errors.item.userCount }}</div>
<div class="text-md-right">{{ errors.item.userCount }}</div>
</template>
<template slot="lastSeen" slot-scope="errors">
<div class="d-flex align-items-center">
<icon name="calendar" class="text-secondary mr-1" />
<time-ago :time="errors.item.lastSeen" class="text-secondary" />
</div>
</template>

View File

@ -32,6 +32,7 @@ export default class LabelsSelect {
$selectbox,
$sidebarCollapsedValue,
$value,
$dropdownMenu,
abilityName,
defaultLabel,
issueUpdateURL,
@ -67,6 +68,7 @@ export default class LabelsSelect {
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
$value = $block.find('.value');
$dropdownMenu = $dropdown.parent().find('.dropdown-menu');
$loading = $block.find('.block-loading').fadeOut();
fieldName = $dropdown.data('fieldName');
initialSelected = $selectbox
@ -454,9 +456,21 @@ export default class LabelsSelect {
}
$loading.fadeIn();
const oldLabels = boardsStore.detail.issue.labels;
boardsStore.detail.issue
.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)
.catch(fadeOutLoader);
} else if (handleClick) {

View File

@ -1,25 +1,30 @@
import { __ } from '~/locale';
export default () => {
const { protocol, host, pathname } = window.location;
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', () => {
shareBtn.classList.add('is-active');
embedBtn.classList.remove('is-active');
snippetUrlArea.value = url;
embedAction.innerText = __('Share');
});
if (shareBtn) {
const { protocol, host, pathname } = window.location;
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');
});
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', () => {
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');
});
}
};

View File

@ -452,6 +452,8 @@ img.emoji {
.w-0 { width: 0; }
.w-8em { width: 8em; }
.w-3rem { width: 3rem; }
.w-15p { width: 15%; }
.w-70p { width: 70%; }
.h-12em { height: 12em; }
.h-32-px { height: 32px;}

View File

@ -29,13 +29,15 @@ module UploadsActions
def show
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
headers['Cache-Control'] = ''
expires_in 5.minutes, public: true, must_revalidate: false
else
expires_in 0.seconds, must_revalidate: true, private: true
end
# We need to reset caching from the applications controller to get rid of the no-store value
headers['Cache-Control'] = ''
headers['Pragma'] = ''
ttl, directives = *cache_settings
ttl ||= 6.months
directives ||= { private: true, must_revalidate: true }
expires_in ttl, directives
disposition = uploader.embeddable? ? 'inline' : 'attachment'
@ -120,8 +122,8 @@ module UploadsActions
nil
end
def cache_publicly?
false
def cache_settings
[]
end
def model

View File

@ -5,11 +5,22 @@ module Groups
class CiCdController < Groups::ApplicationController
skip_cross_project_access_check :show
before_action :authorize_admin_group!
before_action :authorize_update_max_artifacts_size!, only: [:update]
def show
define_ci_variables
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
@group.reset_runners_token!
@ -40,6 +51,10 @@ module Groups
return render_404 unless can?(current_user, :admin_group, group)
end
def authorize_update_max_artifacts_size!
return render_404 unless can?(current_user, :update_max_artifacts_size, group)
end
def auto_devops_params
params.require(:group).permit(:auto_devops_enabled)
end
@ -47,6 +62,14 @@ module Groups
def auto_devops_service
Groups::AutoDevopsService.new(group, current_user, auto_devops_params)
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

View File

@ -46,13 +46,19 @@ module Projects
private
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,
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
:auto_cancel_pending_pipelines, :ci_config_path,
auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy],
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
def run_autodevops_pipeline(service)

View File

@ -81,8 +81,13 @@ class UploadsController < ApplicationController
end
end
def cache_publicly?
User === model || Appearance === model
def cache_settings
case model
when User, Appearance
[5.minutes, { public: true, must_revalidate: false }]
when Project, Group
[5.minutes, { private: true, must_revalidate: true }]
end
end
def secret?

View File

@ -53,7 +53,10 @@ class GroupPolicy < BasePolicy
enable :upload_file
end
rule { admin }.enable :read_group
rule { admin }.policy do
enable :read_group
enable :update_max_artifacts_size
end
rule { has_projects }.policy do
enable :read_group

View File

@ -137,6 +137,8 @@ class ProjectPolicy < BasePolicy
# not.
rule { guest | admin }.enable :read_project_for_iids
rule { admin }.enable :update_max_artifacts_size
rule { guest }.enable :guest_access
rule { reporter }.enable :reporter_access
rule { developer }.enable :developer_access

View File

@ -34,7 +34,7 @@
= f.number_field :max_artifacts_size, class: 'form-control'
.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')
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size-core-only')
.form-group
= f.label :default_artifacts_expire_in, _('Default artifacts expiration'), class: 'label-bold'
= f.text_field :default_artifacts_expire_in, class: 'form-control'

View File

@ -30,6 +30,6 @@
= 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
- 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"

View 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"

View File

@ -2,6 +2,21 @@
- page_title "CI / CD"
- 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) }
.settings-header

View File

@ -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.')
= 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
.form-group
= f.label :ci_config_path, _('Custom CI config path'), class: 'label-bold'

View File

@ -1,4 +1,4 @@
# frozen_string_literal: false
# frozen_string_literal: true
class MigrateExternalDiffsWorker
include ApplicationWorker

View File

@ -1,4 +1,4 @@
# frozen_string_literal: false
# frozen_string_literal: true
class ScheduleMigrateExternalDiffsWorker
include ApplicationWorker

View File

@ -1,4 +1,3 @@
# frozen_string_literal: true
# Worker for updating project statistics.

View File

@ -0,0 +1,5 @@
---
title: Add max_artifacts_size fields under project and group settings.
merge_request: 18286
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix error tracking table layout on small screens
merge_request: 18325
author:
type: fixed

View File

@ -30,7 +30,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
as: :group,
constraints: { group_id: Gitlab::PathRegex.full_namespace_route_regex }) 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
patch :update_auto_devops
end

View File

@ -68,7 +68,7 @@ Omnibus:
gitaly['enable'] = false
redis['bind'] = '0.0.0.0'
redis['port'] = '6379'
redis['port'] = 6379
redis['password'] = 'SECRET_PASSWORD_HERE'
gitlab_rails['auto_migrate'] = false

View File

@ -99,6 +99,7 @@ description: 'Learn how to contribute to GitLab.'
- [Post deployment migrations](post_deployment_migrations.md)
- [Background migrations](background_migrations.md)
- [Swapping tables](swapping_tables.md)
- [Deleting exiting migrations](deleting_migrations.md)
### Best practices
@ -118,7 +119,7 @@ description: 'Learn how to contribute to GitLab.'
- [Database helper modules](database_helpers.md)
- [Code comments](code_comments.md)
## Case studies
### Case studies
- [Database case study: Filtering by label](filtering_by_label.md)
- [Database case study: Namespaces storage statistics](namespaces_storage_statistics.md)

View 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)

View File

@ -29,15 +29,27 @@ If you want to disable it for a specific project, you can do so in
## Maximum artifacts size **(CORE ONLY)**
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).
To change it:
To change it at the instance level:
1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
1. Change the value of maximum artifacts size (in MB).
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)**
The default expiration time of the [job artifacts](../../../administration/job_artifacts.md)

View File

@ -4026,6 +4026,9 @@ msgstr ""
msgid "Code owners"
msgstr ""
msgid "CodeAnalytics|Max files"
msgstr ""
msgid "CodeOwner|Pattern"
msgstr ""
@ -4840,6 +4843,9 @@ msgstr ""
msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
msgid "Customize your pipeline configuration."
msgstr ""
msgid "Cycle Analytics"
msgstr ""
@ -8222,6 +8228,9 @@ msgstr ""
msgid "GroupSettings|New runners registration token has been generated!"
msgstr ""
msgid "GroupSettings|Pipeline settings was updated for the group"
msgstr ""
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@ -8234,6 +8243,9 @@ msgstr ""
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
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."
msgstr ""
@ -8479,7 +8491,7 @@ msgstr ""
msgid "I forgot my password"
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 ""
msgid "I'd like to receive updates via email about GitLab"
@ -10758,9 +10770,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""

View File

@ -156,4 +156,58 @@ describe Groups::Settings::CiCdController do
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

View File

@ -215,6 +215,30 @@ describe Projects::Settings::CiCdController do
expect(project.ci_default_git_depth).to eq(10)
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

View File

@ -1,16 +1,15 @@
# frozen_string_literal: true
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
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
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
# Fixed in newer versions of ActivePack, it will only output a single `private`.
expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store')
expect(subject['Cache-Control']).to eq('max-age=15778476, private, must-revalidate')
end
end
@ -285,7 +284,7 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
it_behaves_like 'content 5 min private cached with revalidation' do
subject do
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)
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
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)
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
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)
end
it_behaves_like 'content not cached without revalidation' do
it_behaves_like 'content 5 min private cached with revalidation' do
subject do
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)
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
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)
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
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)
end
it_behaves_like 'content not cached without revalidation' do
it_behaves_like 'content long term private cached with revalidation' do
subject do
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)
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
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)
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
get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }

View File

@ -14,6 +14,8 @@ describe 'Issue Boards', :js do
let!(:bug) { create(:label, project: project, name: 'Bug') }
let!(:regression) { create(:label, project: project, name: 'Regression') }
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!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
let(:board) { create(:board, project: project) }
@ -27,6 +29,8 @@ describe 'Issue Boards', :js do
end
before do
stub_licensed_features(scoped_labels: true)
project.add_maintainer(user)
sign_in(user)
@ -309,6 +313,33 @@ describe 'Issue Boards', :js do
expect(card).to have_content(bug.title)
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
click_card(card)

View 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

View File

@ -10,6 +10,8 @@ describe 'Public Snippets', :js do
wait_for_requests
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
it 'Unauthenticated user should see raw public snippets' do

View File

@ -45,7 +45,9 @@ describe 'User creates snippet', :js do
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})
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)
end
end
@ -63,7 +65,7 @@ describe 'User creates snippet', :js do
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})
reqs = inspect_requests { visit(link) }
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
expect(reqs.first.status_code).to eq(200)
end
@ -88,7 +90,7 @@ describe 'User creates snippet', :js do
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})
reqs = inspect_requests { visit(link) }
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
expect(reqs.first.status_code).to eq(200)
end

View File

@ -547,4 +547,28 @@ describe GroupPolicy do
groups: [clusterable])
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

View File

@ -478,4 +478,28 @@ describe ProjectPolicy do
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

View File

@ -148,34 +148,25 @@ describe 'OpenID Connect requests' do
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
it 'returns authentication error' do
it 'redirects to login page' do
access_grant
user.block!
expect do
request_access_token!
end.to raise_error UncaughtThrowError
request_access_token!
expect(response).to redirect_to('/users/sign_in')
end
end
context 'when user is ldap_blocked' do
it 'returns authentication error' do
it 'redirects to login page' do
access_grant
user.ldap_block!
expect do
request_access_token!
end.to raise_error UncaughtThrowError
request_access_token!
expect(response).to redirect_to('/users/sign_in')
end
end
end