Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-09-21 15:09:44 +00:00
parent 7985071975
commit b85aae44f9
75 changed files with 689 additions and 230 deletions

View file

@ -1,5 +1,4 @@
import './polyfills';
import './jquery';
import './bootstrap';
import './vue';
import '../lib/utils/axios_utils';

View file

@ -1,4 +0,0 @@
import 'jquery';
// common jQuery plugins
import 'jquery-ujs';

View file

@ -1,4 +1,5 @@
import $ from 'jquery';
import { Rails } from '~/lib/utils/rails_ujs';
import { rstrip } from './lib/utils/common_utils';
function openConfirmDangerModal($form, $modal, text) {
@ -21,9 +22,16 @@ function openConfirmDangerModal($form, $modal, text) {
$submit.disable();
}
});
$('.js-confirm-danger-submit', $modal)
.off('click')
.on('click', () => $form.submit());
.on('click', () => {
if ($form.data('remote')) {
Rails.fire($form[0], 'submit');
} else {
$form.submit();
}
});
}
function getModal($btn) {

View file

@ -1,5 +1,3 @@
import $ from 'jquery';
/*
This module provides easy access to the CSRF token and caches
it for re-use. It also exposes some values commonly used in relation
@ -20,7 +18,6 @@ If you need to compose a headers object, use the spread operator:
see also http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
and https://github.com/rails/jquery-rails/blob/v4.3.1/vendor/assets/javascripts/jquery_ujs.js#L59-L62
*/
const csrf = {
init() {
const tokenEl = document.querySelector('meta[name=csrf-token]');
@ -52,9 +49,4 @@ const csrf = {
csrf.init();
// use our cached token for any $.rails-generated AJAX requests
if ($.rails) {
$.rails.csrfToken = () => csrf.token;
}
export default csrf;

View file

@ -0,0 +1,20 @@
import Rails from '@rails/ujs';
export const initRails = () => {
// eslint-disable-next-line no-underscore-dangle
if (!window._rails_loaded) {
Rails.start();
// Count XHR requests for tests. See spec/support/helpers/wait_for_requests.rb
window.pendingRailsUJSRequests = 0;
document.body.addEventListener('ajax:complete', () => {
window.pendingRailsUJSRequests -= 1;
});
document.body.addEventListener('ajax:beforeSend', () => {
window.pendingRailsUJSRequests += 1;
});
}
};
export { Rails };

View file

@ -11,6 +11,7 @@ import './behaviors';
// lib/utils
import applyGitLabUIConfig from '@gitlab/ui/dist/config';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { initRails } from '~/lib/utils/rails_ujs';
import {
handleLocationHash,
addSelectOnFocusBehaviour,
@ -169,6 +170,8 @@ function deferredInitialisation() {
// Adding a helper class to activate animations only after all is rendered
setTimeout(() => $body.addClass('page-initialised'), 1000);
initRails();
}
document.addEventListener('DOMContentLoaded', () => {

View file

@ -1,4 +1,5 @@
import $ from 'jquery';
import { Rails } from '~/lib/utils/rails_ujs';
import { disableButtonIfEmptyField } from '~/lib/utils/common_utils';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
@ -54,8 +55,9 @@ export default class Members {
formSubmit(e, $el = null) {
const $this = e ? $(e.currentTarget) : $el;
const { $toggle, $dateInput } = this.getMemberListItems($this);
const formEl = $this.closest('form').get(0);
$this.closest('form').trigger('submit.rails');
Rails.fire(formEl, 'submit');
$toggle.disable();
$dateInput.disable();

View file

@ -1,6 +1,5 @@
<script>
/* eslint-disable vue/no-v-html */
import { GlEmptyState } from '@gitlab/ui';
import { GlEmptyState, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { metricStates } from '../constants';
@ -8,6 +7,9 @@ export default {
components: {
GlEmptyState,
},
directives: {
SafeHtml,
},
props: {
documentationPath: {
type: String,
@ -100,7 +102,7 @@ export default {
:compact="true"
>
<template v-if="currentState.slottedDescription" #description>
<div v-html="currentState.slottedDescription"></div>
<div v-safe-html="currentState.slottedDescription"></div>
</template>
</gl-empty-state>
</template>

View file

@ -1,3 +1,4 @@
import { initRails } from '~/lib/utils/rails_ujs';
import { deprecatedCreateFlash as Flash } from '~/flash';
import { __, sprintf } from '~/locale';
import { getParameterByName } from '~/lib/utils/common_utils';
@ -11,6 +12,8 @@ export default function leaveByUrl(namespaceType) {
const param = getParameterByName(PARAMETER_NAME);
if (!param) return;
initRails();
const leaveLink = document.querySelector(LEAVE_LINK_SELECTOR);
if (leaveLink) {
leaveLink.click();

View file

@ -45,7 +45,7 @@ export default {
},
},
computed: {
...mapGetters(['getDiscussion']),
...mapGetters(['getDiscussion', 'suggestionsCount']),
discussion() {
if (!this.note.isDraft) return {};
@ -125,6 +125,7 @@ export default {
<suggestions
v-if="hasSuggestion && !isEditing"
:suggestions="note.suggestions"
:suggestions-count="suggestionsCount"
:batch-suggestions-info="batchSuggestionsInfo"
:note-html="note.note_html"
:line-type="lineType"

View file

@ -231,3 +231,6 @@ export const getDiscussion = state => discussionId =>
state.discussions.find(discussion => discussion.id === discussionId);
export const commentsDisabled = state => state.commentsDisabled;
export const suggestionsCount = (state, getters) =>
Object.values(getters.notesById).filter(n => n.suggestions.length).length;

View file

@ -1,4 +1,5 @@
import $ from 'jquery';
import { Rails } from '~/lib/utils/rails_ujs';
import { deprecatedCreateFlash as Flash } from './flash';
import { __ } from '~/locale';
@ -21,10 +22,12 @@ export default function notificationsDropdown() {
form.find('.js-notifications-icon').toggleClass('hidden');
}
form.find('#notification_setting_level').val(notificationLevel);
form.submit();
Rails.fire(form[0], 'submit');
});
$(document).on('ajax:success', '.notification-form', (e, data) => {
$(document).on('ajax:success', '.notification-form', e => {
const data = e.detail[0];
if (data.saved) {
$(e.currentTarget)
.closest('.js-notification-dropdown')

View file

@ -195,7 +195,8 @@ export default {
:disabled="isSubmitButtonDisabled"
:loading="isSubmitting"
type="submit"
class="js-add-issuable-form-add-button float-left qa-add-issue-button"
class="js-add-issuable-form-add-button float-left"
data-qa-selector="add_issue_button"
>
{{ __('Add') }}
</gl-button>

View file

@ -219,7 +219,8 @@ export default {
:value="inputValue"
:placeholder="inputPlaceholder"
type="text"
class="js-add-issuable-form-input add-issuable-form-input qa-add-issue-input"
class="js-add-issuable-form-input add-issuable-form-input"
data-qa-selector="add_issue_field"
@input="onInput"
@focus="onFocus"
@blur="onBlur"

View file

@ -119,7 +119,7 @@ export default {
{{ section.title }}
</h5>
<ul :key="`section-body-${index}`" class="list-unstyled gl-m-0">
<li v-for="link in section.links" :key="link.url">
<li v-for="link in section.links" :key="link.url" class="gl-display-flex">
<gl-link
:href="link.directAssetUrl || link.url"
class="gl-display-flex gl-align-items-center gl-line-height-24"

View file

@ -26,11 +26,14 @@ export default {
methods: {
listenForQuickActions() {
$(document).on('ajax:success', '.gfm-form', this.quickActionListened);
eventHub.$on('timeTrackingUpdated', data => {
this.quickActionListened(null, data);
this.quickActionListened({ detail: [data] });
});
},
quickActionListened(e, data) {
quickActionListened(e) {
const data = e.detail[0];
const subscribedCommands = ['spend_time', 'time_estimate'];
let changedCommands;
if (data !== undefined) {

View file

@ -27,6 +27,11 @@ export default {
type: String,
required: true,
},
suggestionsCount: {
type: Number,
required: false,
default: 0,
},
},
computed: {
batchSuggestionsCount() {
@ -62,6 +67,7 @@ export default {
<div class="md-suggestion">
<suggestion-diff-header
class="qa-suggestion-diff-header js-suggestion-diff-header"
:suggestions-count="suggestionsCount"
:can-apply="suggestion.appliable && suggestion.current_user.can_apply && !disabled"
:is-applied="suggestion.applied"
:is-batched="isBatched"

View file

@ -42,6 +42,11 @@ export default {
required: false,
default: null,
},
suggestionsCount: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
@ -127,7 +132,7 @@ export default {
</div>
<div v-else class="d-flex align-items-center">
<gl-button
v-if="canBeBatched && !isDisableButton"
v-if="suggestionsCount > 1 && canBeBatched && !isDisableButton"
class="btn-inverted js-add-to-batch-btn btn-grouped"
:disabled="isDisableButton"
@click="addSuggestionToBatch"

View file

@ -38,6 +38,11 @@ export default {
type: String,
required: true,
},
suggestionsCount: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
@ -77,12 +82,12 @@ export default {
this.isRendered = true;
},
generateDiff(suggestionIndex) {
const { suggestions, disabled, batchSuggestionsInfo, helpPagePath } = this;
const { suggestions, disabled, batchSuggestionsInfo, helpPagePath, suggestionsCount } = this;
const suggestion =
suggestions && suggestions[suggestionIndex] ? suggestions[suggestionIndex] : {};
const SuggestionDiffComponent = Vue.extend(SuggestionDiff);
const suggestionDiff = new SuggestionDiffComponent({
propsData: { disabled, suggestion, batchSuggestionsInfo, helpPagePath },
propsData: { disabled, suggestion, batchSuggestionsInfo, helpPagePath, suggestionsCount },
});
suggestionDiff.$on('apply', ({ suggestionId, callback }) => {

View file

@ -14,7 +14,7 @@ module Groups::GroupMembersHelper
end
def linked_groups_data_json(group_links)
GroupGroupLinkSerializer.new.represent(group_links).to_json
GroupGroupLinkSerializer.new.represent(group_links, { current_user: current_user }).to_json
end
def members_data_json(group, members)
@ -47,10 +47,10 @@ module Groups::GroupMembersHelper
}
}.merge(member_created_by_data(member.created_by))
if user.present?
data[:user] = member_user_data(user)
else
if member.invite?
data[:invite] = member_invite_data(member)
elsif user.present?
data[:user] = member_user_data(user)
end
data
@ -77,6 +77,17 @@ module Groups::GroupMembersHelper
avatar_url: avatar_icon_for_user(user, AVATAR_SIZE),
blocked: user.blocked?,
two_factor_enabled: user.two_factor_enabled?
}.merge(member_user_status_data(user.status))
end
def member_user_status_data(status)
return {} unless status.present?
{
status: {
emoji: status.emoji,
message_html: status.message_html
}
}
end

View file

@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
VERSION = '0.20.1'
VERSION = '0.21.0'
self.table_name = 'clusters_applications_runners'

View file

@ -29,12 +29,6 @@ class Commit
delegate :repository, to: :container
delegate :project, to: :repository, allow_nil: true
DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines]
# Commits above this size will not be rendered in HTML
DIFF_HARD_LIMIT_FILES = 1000
DIFF_HARD_LIMIT_LINES = 50000
MIN_SHA_LENGTH = Gitlab::Git::Commit::MIN_SHA_LENGTH
COMMIT_SHA_PATTERN = /\h{#{MIN_SHA_LENGTH},40}/.freeze
EXACT_COMMIT_SHA_PATTERN = /\A#{COMMIT_SHA_PATTERN}\z/.freeze
@ -80,10 +74,30 @@ class Commit
sha[0..MIN_SHA_LENGTH]
end
def max_diff_options
def diff_safe_lines
Gitlab::Git::DiffCollection.default_limits[:max_lines]
end
def diff_hard_limit_files(project: nil)
if Feature.enabled?(:increased_diff_limits, project)
2000
else
1000
end
end
def diff_hard_limit_lines(project: nil)
if Feature.enabled?(:increased_diff_limits, project)
75000
else
50000
end
end
def max_diff_options(project: nil)
{
max_files: DIFF_HARD_LIMIT_FILES,
max_lines: DIFF_HARD_LIMIT_LINES
max_files: diff_hard_limit_files(project: project),
max_lines: diff_hard_limit_lines(project: project)
}
end

View file

@ -631,7 +631,7 @@ class MergeRequest < ApplicationRecord
def diff_size
# Calling `merge_request_diff.diffs.real_size` will also perform
# highlighting, which we don't need here.
merge_request_diff&.real_size || diff_stats&.real_size || diffs.real_size
merge_request_diff&.real_size || diff_stats&.real_size(project: project) || diffs.real_size
end
def modified_paths(past_merge_request_diff: nil, fallback_on_overflow: false)

View file

@ -651,7 +651,7 @@ class MergeRequestDiff < ApplicationRecord
if compare.commits.empty?
new_attributes[:state] = :empty
else
diff_collection = compare.diffs(Commit.max_diff_options)
diff_collection = compare.diffs(Commit.max_diff_options(project: merge_request.project))
new_attributes[:real_size] = diff_collection.real_size
if diff_collection.any?

View file

@ -322,8 +322,6 @@ class Note < ApplicationRecord
end
def contributor?
return false unless ::Feature.enabled?(:show_contributor_on_note, project)
project&.team&.contributor?(self.author_id)
end

View file

@ -7,11 +7,8 @@ class Service < ApplicationRecord
include Importable
include ProjectServicesLoggable
include DataFields
include IgnorableColumns
include FromUnion
ignore_columns %i[default], remove_with: '13.5', remove_after: '2020-10-22'
SERVICE_NAMES = %w[
alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker discord
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira

View file

@ -1,12 +1,22 @@
# frozen_string_literal: true
class GroupGroupLinkEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :created_at
expose :expires_at do |group_link|
group_link.expires_at&.to_time
end
expose :can_update do |group_link|
can_manage?(group_link)
end
expose :can_remove do |group_link|
can_manage?(group_link)
end
expose :access_level do
expose :human_access, as: :string_value
expose :group_access, as: :integer_value
@ -23,4 +33,14 @@ class GroupGroupLinkEntity < Grape::Entity
expose :shared_with_group, merge: true, using: GroupBasicEntity
end
private
def current_user
options[:current_user]
end
def can_manage?(group_link)
can?(current_user, :admin_group_member, group_link.shared_group)
end
end

View file

@ -2,7 +2,7 @@
module Ci
class UpdateBuildStateService
Result = Struct.new(:status, keyword_init: true)
Result = Struct.new(:status, :backoff, keyword_init: true)
ACCEPT_TIMEOUT = 5.minutes.freeze
@ -28,7 +28,9 @@ module Ci
private
def accept_build_state!
if Time.current - ensure_pending_state.created_at > ACCEPT_TIMEOUT
state_created = ensure_pending_state.created_at
if Time.current - state_created > ACCEPT_TIMEOUT
metrics.increment_trace_operation(operation: :discarded)
return update_build_state!
@ -40,7 +42,9 @@ module Ci
metrics.increment_trace_operation(operation: :accepted)
Result.new(status: 202)
::Gitlab::Ci::Runner::Backoff.new(state_created).then do |backoff|
Result.new(status: 202, backoff: backoff.to_seconds)
end
end
def overwrite_trace!

View file

@ -1,4 +1,4 @@
- too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES
- too_big = diff_file.diff_lines.count > Commit.diff_safe_lines
- if too_big
.suppressed-container
%a.show-suppressed-diff.cursor-pointer.js-show-suppressed-diff= _("Changes suppressed. Click to show.")

View file

@ -0,0 +1,5 @@
---
title: Display Contributor badges on notes
merge_request: 42576
author: Mycroft Kang @TaehyeokKang
type: added

View file

@ -0,0 +1,5 @@
---
title: Fix clickable width of release asset links
merge_request: 42757
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Hides batch suggestions button if there is only 1 suggestion
merge_request: 42681
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Update GitLab Runner Helm Chart to 0.21.0
merge_request: 42844
author:
type: other

View file

@ -1,7 +1,7 @@
---
name: show_contributor_on_note
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40198
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249179
group: group::project management
name: increased_diff_limits
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40357
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241185
group: group::source_code
type: development
default_enabled: false

View file

@ -24,7 +24,8 @@ Parameters:
- `path` (optional) - The path inside repository. Used to get content of subdirectories
- `ref` (optional) - The name of a repository branch or tag or if not given the default branch
- `recursive` (optional) - Boolean value used to get a recursive tree (false by default)
- `per_page` (optional) - Number of results to show per page. If not specified, defaults to `20`
- `per_page` (optional) - Number of results to show per page. If not specified, defaults to `20`.
Read more on [pagination](README.md#pagination).
```json
[

View file

@ -41,15 +41,14 @@ The JWT's payload looks like this:
"nbf": 1585798372, # Not valid before
"exp": 1585713886, # Expire at
"sub": "job_1212", # Subject (job id)
"namespace_id": "1",
"namespace_path": "mygroup",
"project_id": "22",
"project_path": "mygroup/myproject",
"user_id": "42",
"user_login": "myuser",
"user_email": "myuser@example.com",
"pipeline_id": "1212",
"job_id": "1212",
"namespace_id": "1", # Use this to scope to group or user level namespace by id
"namespace_path": "mygroup", # Use this to scope to group or user level namespace by path
"project_id": "22", #
"project_path": "mygroup/myproject", #
"user_id": "42", # Id of the user executing the job
"user_email": "myuser@example.com", # Email of the user executing the job
"pipeline_id": "1212", #
"job_id": "1212", #
"ref": "auto-deploy-2020-04-01", # Git ref for this job
"ref_type": "branch", # Git ref type, branch or tag
"ref_protected": "true" # true if this git ref is protected, false otherwise

View file

@ -15,5 +15,5 @@ You can find the Frontend Architecture experts on the [team page](https://about.
## Examples
You can find documentation about the desired architecture for a new feature
built with Vue.js [here](vue.md).
You can find [documentation about the desired architecture](vue.md) for a new
feature built with Vue.js.

View file

@ -1,9 +1,10 @@
# Icons and SVG Illustrations
We manage our own Icon and Illustration library in the [`gitlab-svgs`](https://gitlab.com/gitlab-org/gitlab-svgs) repository.
This repository is published on [npm](https://www.npmjs.com/package/@gitlab/svgs) and managed as a dependency via yarn.
You can browse all available Icons and Illustrations [here](https://gitlab-org.gitlab.io/gitlab-svgs).
To upgrade to a new version run `yarn upgrade @gitlab/svgs`.
We manage our own icon and illustration library in the [`gitlab-svgs`](https://gitlab.com/gitlab-org/gitlab-svgs)
repository. This repository is published on [npm](https://www.npmjs.com/package/@gitlab/svgs),
and is managed as a dependency with yarn. You can browse all available
[icons and illustrations](https://gitlab-org.gitlab.io/gitlab-svgs). To upgrade
to a new version run `yarn upgrade @gitlab/svgs`.
## Icons
@ -21,10 +22,11 @@ To use a sprite Icon in HAML or Rails we use a specific helper function :
sprite_icon(icon_name, size: nil, css_class: '')
```
- **icon_name** Use the icon_name that you can find in the SVG Sprite
([Overview is available here](https://gitlab-org.gitlab.io/gitlab-svgs)).
- **size (optional)** Use one of the following sizes : 16, 24, 32, 48, 72 (this will be translated into a `s16` class)
- **css_class (optional)** If you want to add additional CSS classes
- **icon_name**: Use the icon_name for the SVG sprite in the list of
([GitLab SVGs](https://gitlab-org.gitlab.io/gitlab-svgs)).
- **size (optional)**: Use one of the following sizes : 16, 24, 32, 48, 72 (this
will be translated into a `s16` class)
- **css_class (optional)**: If you want to add additional CSS classes.
**Example**
@ -66,10 +68,12 @@ export default {
</template>
```
- **name** Name of the Icon in the SVG Sprite ([Overview is available here](https://gitlab-org.gitlab.io/gitlab-svgs)).
- **size (optional)** Number value for the size which is then mapped to a specific CSS class
(Available Sizes: 8, 12, 16, 18, 24, 32, 48, 72 are mapped to `sXX` CSS classes)
- **class (optional)** Additional CSS Classes to add to the SVG tag.
- **name**: Name of the icon of the SVG sprite, as listed in the
([GitLab SVG Previewer](https://gitlab-org.gitlab.io/gitlab-svgs)).
- **size: (optional)** Number value for the size which is then mapped to a
specific CSS class (Available sizes: 8, 12, 16, 18, 24, 32, 48, 72 are mapped
to `sXX` CSS classes)
- **class (optional)**: Additional CSS classes to add to the SVG tag.
### Usage in HTML/JS

View file

@ -101,7 +101,10 @@ Our code is automatically formatted with [Prettier](https://prettier.io) to foll
### Editor
The easiest way to include prettier in your workflow is by setting up your preferred editor (all major editors are supported) accordingly. We suggest setting up prettier to run automatically when each file is saved. Find [here](https://prettier.io/docs/en/editors.html) the best way to set it up in your preferred editor.
The recommended method to include Prettier in your workflow is to set up your
preferred editor (all major editors are supported) accordingly. We suggest
setting up Prettier to run when each file is saved. For instructions about using
Prettier in your preferred editor, see the [Prettier documentation](https://prettier.io/docs/en/editors.html).
Please take care that you only let Prettier format the same file types as the global Yarn script does (`.js`, `.vue`, `.graphql`, and `.scss`). In VSCode by example you can easily exclude file formats in your settings file:

View file

@ -32,8 +32,9 @@ When using Vuex at GitLab, separate these concerns into different files to impro
└── mutation_types.js # mutation types
```
The following example shows an application that lists and adds users to the state.
(For a more complex example implementation take a look at the security applications store in [here](https://gitlab.com/gitlab-org/gitlab/tree/master/ee/app/assets/javascripts/vue_shared/security_reports/store))
The following example shows an application that lists and adds users to the
state. (For a more complex example implementation, review the security
applications stored in this [repository](https://gitlab.com/gitlab-org/gitlab/tree/master/ee/app/assets/javascripts/vue_shared/security_reports/store)).
### `index.js`

View file

@ -12,7 +12,7 @@ WAI-ARIA (the Accessible Rich Internet Applications specification) defines a way
The `role` attribute describes the role the element plays in the context of the document.
Check the list of WAI-ARIA roles [here](https://www.w3.org/TR/wai-aria-1.1/#landmark_roles)
Review the list of [WAI-ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#landmark_roles).
## Icons

View file

@ -82,7 +82,8 @@ This Ruby Regex specialty can have security impact, as often regular expressions
#### Examples
GitLab specific examples can be found [here](https://gitlab.com/gitlab-org/gitlab/-/issues/36029#note_251262187) and [there](https://gitlab.com/gitlab-org/gitlab/-/issues/33569).
GitLab-specific examples can be found in the following [path traversal](https://gitlab.com/gitlab-org/gitlab/-/issues/36029#note_251262187)
and [open redirect](https://gitlab.com/gitlab-org/gitlab/-/issues/33569) issues.
Another example would be this fictional Ruby on Rails controller:

View file

@ -24,7 +24,7 @@ connections to GitLab repositories.
## Requirements
To support SSH, GitLab requires the installation of the OpenSSH client, which
comes pre-installed on GNU/Linux and macOS, but not on Windows.
comes pre-installed on GNU/Linux and macOS, as well as on Windows 10.
Make sure that your system includes SSH version 6.5 or newer, as that excludes
the now insecure MD5 signature scheme. The following command returns the version of

View file

@ -181,6 +181,7 @@ module API
.new(job, declared_params(include_missing: false))
service.execute.then do |result|
header 'X-GitLab-Trace-Update-Interval', result.backoff
status result.status
end
end

View file

@ -0,0 +1,75 @@
# frozen_string_literal: true
module Gitlab
module Ci
module Runner
##
# Runner Backoff class is an implementation of an exponential backoff
# used when a runner communicates with GitLab. We typically use it when a
# runner retries sending a build status after we created a build pending
# state.
#
# Backoff is calculated based on the backoff slot which is always a power
# of 2:
#
# 0s - 3s duration -> 1 second backoff
# 4s - 7s duration -> 2 seconds backoff
# 8s - 15s duration -> 4 seconds backoff
# 16s - 31s duration -> 8 seconds backoff
# 32s - 63s duration -> 16 seconds backoff
# 64s - 127s duration -> 32 seconds backoff
# 127s - 256s+ duration -> 64 seconds backoff
#
# It means that first 15 requests made by a runner will need to respect
# following backoffs:
#
# 0s -> 1 second backoff (backoff started, slot 0, 2^0 backoff)
# 1s -> 1 second backoff
# 2s -> 1 second backoff
# 3s -> 1 seconds backoff
# (slot 1 - 2^1 backoff)
# 4s -> 2 seconds backoff
# 6s -> 2 seconds backoff
# (slot 2 - 2^2 backoff)
# 8s -> 4 seconds backoff
# 12s -> 4 seconds backoff
# (slot 3 - 2^3 backoff)
# 16s -> 8 seconds backoff
# 24s -> 8 seconds backoff
# (slot 4 - 2^4 backoff)
# 32s -> 16 seconds backoff
# 48s -> 16 seconds backoff
# (slot 5 - 2^5 backoff)
# 64s -> 32 seconds backoff
# 96s -> 32 seconds backoff
# (slot 6 - 2^6 backoff)
# 128s -> 64 seconds backoff
#
# There is a cap on the backoff - it will never exceed 64 seconds.
#
class Backoff
def initialize(started)
@started = started
if duration < 0
raise ArgumentError, 'backoff duration negative'
end
end
def duration
Time.current - @started
end
def slot
return 0 if duration <= 1
Math.log(duration, 2).floor - 1
end
def to_seconds
2**[slot, 6].min
end
end
end
end
end

View file

@ -11,7 +11,7 @@ module Gitlab
super(merge_request_diff,
project: merge_request_diff.project,
diff_options: diff_options,
diff_options: merged_diff_options(diff_options),
diff_refs: merge_request_diff.diff_refs,
fallback_diff_refs: merge_request_diff.fallback_diff_refs)
end
@ -64,6 +64,11 @@ module Gitlab
diff_stats_cache.read || super
end
end
def merged_diff_options(diff_options)
max_diff_options = ::Commit.max_diff_options(project: @merge_request_diff.project)
diff_options.present? ? diff_options.merge(max_diff_options) : max_diff_options
end
end
end
end

View file

@ -7,19 +7,23 @@ module Gitlab
class DiffCollection
include Enumerable
DEFAULT_LIMITS = { max_files: 100, max_lines: 5000 }.freeze
attr_reader :limits
delegate :max_files, :max_lines, :max_bytes, :safe_max_files, :safe_max_lines, :safe_max_bytes, to: :limits
def self.default_limits
{ max_files: 100, max_lines: 5000 }
end
def self.limits(options = {})
limits = {}
limits[:max_files] = options.fetch(:max_files, DEFAULT_LIMITS[:max_files])
limits[:max_lines] = options.fetch(:max_lines, DEFAULT_LIMITS[:max_lines])
defaults = default_limits
limits[:max_files] = options.fetch(:max_files, defaults[:max_files])
limits[:max_lines] = options.fetch(:max_lines, defaults[:max_lines])
limits[:max_bytes] = limits[:max_files] * 5.kilobytes # Average 5 KB per file
limits[:safe_max_files] = [limits[:max_files], DEFAULT_LIMITS[:max_files]].min
limits[:safe_max_lines] = [limits[:max_lines], DEFAULT_LIMITS[:max_lines]].min
limits[:safe_max_files] = [limits[:max_files], defaults[:max_files]].min
limits[:safe_max_lines] = [limits[:max_lines], defaults[:max_lines]].min
limits[:safe_max_bytes] = limits[:safe_max_files] * 5.kilobytes # Average 5 KB per file
limits[:max_patch_bytes] = Gitlab::Git::Diff.patch_hard_limit_bytes

View file

@ -22,8 +22,8 @@ module Gitlab
@collection.map(&:path)
end
def real_size
max_files = ::Commit.max_diff_options[:max_files]
def real_size(project: nil)
max_files = ::Commit.max_diff_options(project: project)[:max_files]
if paths.size > max_files
"#{max_files}+"
else

View file

@ -21061,9 +21061,6 @@ msgstr ""
msgid "Release|Something went wrong while saving the release details"
msgstr ""
msgid "Remediated: needs review"
msgstr ""
msgid "Remediations"
msgstr ""
@ -22424,6 +22421,9 @@ msgstr ""
msgid "SecurityConfiguration|Available for on-demand DAST"
msgstr ""
msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the Merge Request."
msgstr ""
msgid "SecurityConfiguration|Configure"
msgstr ""
@ -22457,6 +22457,9 @@ msgstr ""
msgid "SecurityConfiguration|Not enabled"
msgstr ""
msgid "SecurityConfiguration|SAST Analyzers"
msgstr ""
msgid "SecurityConfiguration|SAST Configuration"
msgstr ""
@ -26822,6 +26825,9 @@ msgstr ""
msgid "Total cores (CPUs)"
msgstr ""
msgid "Total days to completion"
msgstr ""
msgid "Total issues"
msgstr ""
@ -28329,6 +28335,9 @@ msgstr ""
msgid "Vulnerability|%{scannerName} (version %{scannerVersion})"
msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
msgid "Vulnerability|Class"
msgstr ""

View file

@ -46,6 +46,7 @@
"@gitlab/ui": "21.4.2",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-1",
"@rails/ujs": "^6.0.3-2",
"@sentry/browser": "^5.22.3",
"@sourcegraph/code-host-integration": "0.0.50",
"@toast-ui/editor": "^2.4.0",
@ -99,7 +100,6 @@
"jed": "^1.1.1",
"jest-transform-graphql": "^2.1.0",
"jquery": "^3.5.0",
"jquery-ujs": "1.2.2",
"jquery.caret": "^0.3.1",
"jquery.waitforimages": "^2.2.0",
"js-cookie": "^2.2.1",

View file

@ -43,7 +43,7 @@ module QA
end
view 'app/assets/javascripts/related_issues/components/related_issuable_input.vue' do
element :add_issue_input
element :add_issue_field
end
view 'app/assets/javascripts/related_issues/components/related_issues_block.vue' do
@ -57,8 +57,8 @@ module QA
def relate_issue(issue)
click_element(:related_issues_plus_button)
fill_element(:add_issue_input, issue.web_url)
send_keys_to_element(:add_issue_input, :enter)
fill_element(:add_issue_field, issue.web_url)
send_keys_to_element(:add_issue_field, :enter)
end
def related_issuable_item

View file

@ -9,20 +9,14 @@ module QA
def wait_for_requests(skip_finished_loading_check: false)
Waiter.wait_until(log: false) do
finished_all_ajax_requests? && finished_all_axios_requests? && (!skip_finished_loading_check ? finished_loading?(wait: 1) : true)
finished_all_ajax_requests? && (!skip_finished_loading_check ? finished_loading?(wait: 1) : true)
end
rescue Repeater::WaitExceededError
raise $!, 'Page did not fully load. This could be due to an unending async request or loading icon.'
end
def finished_all_axios_requests?
Capybara.page.evaluate_script('window.pendingRequests || 0').zero? # rubocop:disable Style/NumericPredicate
end
def finished_all_ajax_requests?
return true if Capybara.page.evaluate_script('typeof jQuery === "undefined"')
Capybara.page.evaluate_script('jQuery.active').zero? # rubocop:disable Style/NumericPredicate
Capybara.page.evaluate_script('window.pendingRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate
end
def finished_loading?(wait: DEFAULT_MAX_WAIT_TIME)

View file

@ -3,7 +3,6 @@
describe QA::Support::WaitForRequests do
describe '.wait_for_requests' do
before do
allow(subject).to receive(:finished_all_axios_requests?).and_return(true)
allow(subject).to receive(:finished_all_ajax_requests?).and_return(true)
allow(subject).to receive(:finished_loading?).and_return(true)
end

View file

@ -7,6 +7,7 @@ RSpec.describe 'Expand and collapse diffs', :js do
let(:project) { create(:project, :repository) }
before do
stub_feature_flags(increased_diff_limits: false)
sign_in(create(:admin))
# Ensure that undiffable.md is in .gitattributes

View file

@ -7,6 +7,7 @@ RSpec.describe 'User expands diff', :js do
let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) }
before do
stub_feature_flags(increased_diff_limits: false)
visit(diffs_project_merge_request_path(project, merge_request))
wait_for_requests

View file

@ -130,7 +130,9 @@ RSpec.describe 'User comments on a diff', :js do
wait_for_requests
end
end
end
files.each_with_index do |file, index|
page.within("[id='#{file[:hash]}']") do
expect(page).not_to have_content('Applied')

View file

@ -18,7 +18,9 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button
click_link 'On mention'
wait_for_requests
page.within('.notification-dropdown') do
expect(page).not_to have_css('.gl-spinner')
end
click_notifications_button
expect(find('.update-notification.is-active')).to have_content('On mention')
@ -30,7 +32,9 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button
click_link 'Disabled'
wait_for_requests
page.within('.notification-dropdown') do
expect(page).not_to have_css('.gl-spinner')
end
expect(page).to have_css('.notifications-icon[data-testid="notifications-off-icon"]')
end

View file

@ -1,10 +1,12 @@
{
"type": "object",
"required": ["id", "created_at", "expires_at", "access_level"],
"required": ["id", "created_at", "expires_at", "can_update", "can_remove", "access_level"],
"properties": {
"id": { "type": "integer" },
"created_at": { "type": "date-time" },
"expires_at": { "type": ["date-time", "null"] },
"can_update": { "type": "boolean" },
"can_remove": { "type": "boolean" },
"access_level": {
"type": "object",
"required": ["integer_value", "string_value"],

View file

@ -62,7 +62,18 @@
"avatar_url": { "type": ["string", "null"] },
"web_url": { "type": "string" },
"blocked": { "type": "boolean" },
"two_factor_enabled": { "type": "boolean" }
"two_factor_enabled": { "type": "boolean" },
"status": {
"type": "object",
"required": [
"emoji",
"message_html"
],
"properties": {
"emoji": { "type": "string" },
"message_html": { "type": "string" }
}
}
}
},
"invite": {

View file

@ -1,79 +1,139 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GroupEmptyState Renders an empty state for BAD_QUERY 1`] = `
<gl-empty-state-stub
compact="true"
primarybuttonlink="/path/to/settings"
primarybuttontext="Verify configuration"
svgpath="/path/to/empty-group-illustration.svg"
title="Query cannot be processed"
/>
exports[`GroupEmptyState given state BAD_QUERY passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
"description": null,
"primaryButtonLink": "/path/to/settings",
"primaryButtonText": "Verify configuration",
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "/path/to/empty-group-illustration.svg",
"title": "Query cannot be processed",
}
`;
exports[`GroupEmptyState Renders an empty state for BAD_QUERY 2`] = `"The Prometheus server responded with \\"bad request\\". Please check your queries are correct and are supported in your Prometheus version. <a href=\\"/path/to/docs\\">More information</a>"`;
exports[`GroupEmptyState Renders an empty state for CONNECTION_FAILED 1`] = `
<gl-empty-state-stub
compact="true"
description="We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating."
primarybuttonlink="/path/to/settings"
primarybuttontext="Verify configuration"
svgpath="/path/to/empty-group-illustration.svg"
title="Connection failed"
/>
exports[`GroupEmptyState given state BAD_QUERY renders the slotted content 1`] = `
<div>
<div>
The Prometheus server responded with "bad request". Please check your queries are correct and are supported in your Prometheus version.
<a
href="/path/to/docs"
>
More information
</a>
</div>
</div>
`;
exports[`GroupEmptyState Renders an empty state for CONNECTION_FAILED 2`] = `undefined`;
exports[`GroupEmptyState Renders an empty state for FOO STATE 1`] = `
<gl-empty-state-stub
compact="true"
description="An error occurred while loading the data. Please try again."
svgpath="/path/to/empty-group-illustration.svg"
title="An error has occurred"
/>
exports[`GroupEmptyState given state CONNECTION_FAILED passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
"description": "We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating.",
"primaryButtonLink": "/path/to/settings",
"primaryButtonText": "Verify configuration",
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "/path/to/empty-group-illustration.svg",
"title": "Connection failed",
}
`;
exports[`GroupEmptyState Renders an empty state for FOO STATE 2`] = `undefined`;
exports[`GroupEmptyState given state CONNECTION_FAILED renders the slotted content 1`] = `<div />`;
exports[`GroupEmptyState Renders an empty state for LOADING 1`] = `
<gl-empty-state-stub
compact="true"
description="Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available."
svgpath="/path/to/empty-group-illustration.svg"
title="Waiting for performance data"
/>
exports[`GroupEmptyState given state FOO STATE passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
"description": "An error occurred while loading the data. Please try again.",
"primaryButtonLink": null,
"primaryButtonText": null,
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "/path/to/empty-group-illustration.svg",
"title": "An error has occurred",
}
`;
exports[`GroupEmptyState Renders an empty state for LOADING 2`] = `undefined`;
exports[`GroupEmptyState given state FOO STATE renders the slotted content 1`] = `<div />`;
exports[`GroupEmptyState Renders an empty state for NO_DATA 1`] = `
<gl-empty-state-stub
compact="true"
svgpath="/path/to/empty-group-illustration.svg"
title="No data to display"
/>
exports[`GroupEmptyState given state LOADING passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
"description": "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.",
"primaryButtonLink": null,
"primaryButtonText": null,
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "/path/to/empty-group-illustration.svg",
"title": "Waiting for performance data",
}
`;
exports[`GroupEmptyState Renders an empty state for NO_DATA 2`] = `"The data source is connected, but there is no data to display. <a href=\\"/path/to/docs\\">More information</a>"`;
exports[`GroupEmptyState given state LOADING renders the slotted content 1`] = `<div />`;
exports[`GroupEmptyState Renders an empty state for TIMEOUT 1`] = `
<gl-empty-state-stub
compact="true"
svgpath="/path/to/empty-group-illustration.svg"
title="Connection timed out"
/>
exports[`GroupEmptyState given state NO_DATA passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
"description": null,
"primaryButtonLink": null,
"primaryButtonText": null,
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "/path/to/empty-group-illustration.svg",
"title": "No data to display",
}
`;
exports[`GroupEmptyState Renders an empty state for TIMEOUT 2`] = `"Charts can't be displayed as the request for data has timed out. <a href=\\"/path/to/docs\\">More information</a>"`;
exports[`GroupEmptyState Renders an empty state for UNKNOWN_ERROR 1`] = `
<gl-empty-state-stub
compact="true"
description="An error occurred while loading the data. Please try again."
svgpath="/path/to/empty-group-illustration.svg"
title="An error has occurred"
/>
exports[`GroupEmptyState given state NO_DATA renders the slotted content 1`] = `
<div>
<div>
The data source is connected, but there is no data to display.
<a
href="/path/to/docs"
>
More information
</a>
</div>
</div>
`;
exports[`GroupEmptyState Renders an empty state for UNKNOWN_ERROR 2`] = `undefined`;
exports[`GroupEmptyState given state TIMEOUT passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
"description": null,
"primaryButtonLink": null,
"primaryButtonText": null,
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "/path/to/empty-group-illustration.svg",
"title": "Connection timed out",
}
`;
exports[`GroupEmptyState given state TIMEOUT renders the slotted content 1`] = `
<div>
<div>
Charts can't be displayed as the request for data has timed out.
<a
href="/path/to/docs"
>
More information
</a>
</div>
</div>
`;
exports[`GroupEmptyState given state UNKNOWN_ERROR passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
"description": "An error occurred while loading the data. Please try again.",
"primaryButtonLink": null,
"primaryButtonText": null,
"secondaryButtonLink": null,
"secondaryButtonText": null,
"svgPath": "/path/to/empty-group-illustration.svg",
"title": "An error has occurred",
}
`;
exports[`GroupEmptyState given state UNKNOWN_ERROR renders the slotted content 1`] = `<div />`;

View file

@ -1,7 +1,13 @@
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import { metricStates } from '~/monitoring/constants';
const MockGlEmptyState = {
props: GlEmptyState.props,
template: '<div><slot name="description"></slot></div>',
};
function createComponent(props) {
return shallowMount(GroupEmptyState, {
propsData: {
@ -10,11 +16,20 @@ function createComponent(props) {
settingsPath: '/path/to/settings',
svgPath: '/path/to/empty-group-illustration.svg',
},
stubs: {
GlEmptyState: MockGlEmptyState,
},
});
}
describe('GroupEmptyState', () => {
const supportedStates = [
let wrapper;
afterEach(() => {
wrapper.destroy();
});
describe.each([
metricStates.NO_DATA,
metricStates.TIMEOUT,
metricStates.CONNECTION_FAILED,
@ -22,13 +37,17 @@ describe('GroupEmptyState', () => {
metricStates.LOADING,
metricStates.UNKNOWN_ERROR,
'FOO STATE', // does not fail with unknown states
];
])('given state %s', selectedState => {
beforeEach(() => {
wrapper = createComponent({ selectedState });
});
it.each(supportedStates)('Renders an empty state for %s', selectedState => {
const wrapper = createComponent({ selectedState });
it('renders the slotted content', () => {
expect(wrapper.element).toMatchSnapshot();
});
expect(wrapper.element).toMatchSnapshot();
// slot is not rendered by the stub, test it separately
expect(wrapper.vm.currentState.slottedDescription).toMatchSnapshot();
it('passes the expected props to GlEmptyState', () => {
expect(wrapper.find(MockGlEmptyState).props()).toMatchSnapshot();
});
});
});

View file

@ -1,4 +1,6 @@
import Vue from 'vue';
import 'jquery';
import * as jqueryMatchers from 'custom-jquery-matchers';
import { config as testUtilsConfig } from '@vue/test-utils';
import Translate from '~/vue_shared/translate';
@ -9,7 +11,6 @@ import customMatchers from './matchers';
import './helpers/dom_shims';
import './helpers/jquery';
import '~/commons/jquery';
import '~/commons/bootstrap';
process.on('unhandledRejection', global.promiseRejectionHandler);

View file

@ -10,6 +10,7 @@ exports[`Suggestion Diff component matches snapshot 1`] = `
helppagepath="path_to_docs"
isapplyingbatch="true"
isbatched="true"
suggestionscount="0"
/>
<table

View file

@ -57,7 +57,9 @@ describe('Suggestion Diff component', () => {
});
it('renders apply suggestion and add to batch buttons', () => {
createComponent();
createComponent({
suggestionsCount: 2,
});
const applyBtn = findApplyButton();
const addToBatchBtn = findAddToBatchButton();
@ -104,7 +106,9 @@ describe('Suggestion Diff component', () => {
describe('when add to batch is clicked', () => {
it('emits addToBatch', () => {
createComponent();
createComponent({
suggestionsCount: 2,
});
findAddToBatchButton().vm.$emit('click');

View file

@ -5,6 +5,8 @@ require "spec_helper"
RSpec.describe Groups::GroupMembersHelper do
include MembersPresentation
let_it_be(:current_user) { create(:user) }
describe '.group_member_select_options' do
let(:group) { create(:group) }
@ -20,6 +22,10 @@ RSpec.describe Groups::GroupMembersHelper do
describe '#linked_groups_data_json' do
include_context 'group_group_link'
before do
allow(helper).to receive(:current_user).and_return(current_user)
end
it 'matches json schema' do
json = helper.linked_groups_data_json(shared_group.shared_with_group_links)
@ -28,7 +34,6 @@ RSpec.describe Groups::GroupMembersHelper do
end
describe '#members_data_json' do
let(:current_user) { create(:user) }
let(:group) { create(:group) }
before do
@ -48,6 +53,14 @@ RSpec.describe Groups::GroupMembersHelper do
let(:group_member) { create(:group_member, group: group, created_by: current_user) }
it_behaves_like 'group_members.json'
context 'with user status set' do
let(:user) { create(:user) }
let!(:status) { create(:user_status, user: user) }
let(:group_member) { create(:group_member, group: group, user: user, created_by: current_user) }
it_behaves_like 'group_members.json'
end
end
context 'for an invited group member' do

View file

@ -0,0 +1,96 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rspec-parameterized'
require 'active_support/testing/time_helpers'
RSpec.describe Gitlab::Ci::Runner::Backoff do
include ActiveSupport::Testing::TimeHelpers
describe '#duration' do
it 'returns backoff duration from start' do
freeze_time do
described_class.new(5.minutes.ago).then do |backoff|
expect(backoff.duration).to eq 5.minutes
end
end
end
end
describe '#slot' do
using RSpec::Parameterized::TableSyntax
where(:started, :slot) do
1 | 0
2 | 0
3 | 0
4 | 1
5 | 1
6 | 1
7 | 1
8 | 2
9 | 2
10 | 2
15 | 2
16 | 3
31 | 3
32 | 4
63 | 4
64 | 5
127 | 5
128 | 6
250 | 6
310 | 7
520 | 8
999 | 8
end
with_them do
it 'falls into an appropaite backoff slot' do
freeze_time do
backoff = described_class.new(started.seconds.ago)
expect(backoff.slot).to eq slot
end
end
end
end
describe '#to_seconds' do
using RSpec::Parameterized::TableSyntax
where(:started, :backoff) do
1 | 1
2 | 1
3 | 1
4 | 2
5 | 2
6 | 2
7 | 2
8 | 4
9 | 4
10 | 4
15 | 4
16 | 8
31 | 8
32 | 16
63 | 16
64 | 32
127 | 32
128 | 64
250 | 64
310 | 64
520 | 64
999 | 64
end
with_them do
it 'calculates backoff based on an appropriate slot' do
freeze_time do
described_class.new(started.seconds.ago).then do |delay|
expect(delay.to_seconds).to eq backoff
end
end
end
end
end
end

View file

@ -531,7 +531,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do
let(:iterator) { [fake_diff(1, 1)] * 4 }
before do
stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: 2, max_lines: max_lines })
allow(Gitlab::Git::DiffCollection)
.to receive(:default_limits)
.and_return({ max_files: 2, max_lines: max_lines })
end
it 'prunes diffs by default even little ones' do
@ -556,7 +558,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do
end
before do
stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: max_files, max_lines: 80 })
allow(Gitlab::Git::DiffCollection)
.to receive(:default_limits)
.and_return({ max_files: max_files, max_lines: 80 })
end
it 'prunes diffs by default even little ones' do
@ -581,7 +585,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do
end
before do
stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: max_files, max_lines: 80 })
allow(Gitlab::Git::DiffCollection)
.to receive(:default_limits)
.and_return({ max_files: max_files, max_lines: 80 })
end
it 'prunes diffs by default even little ones' do
@ -665,8 +671,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do
end
before do
stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS',
{ max_files: max_files, max_lines: 80 })
allow(Gitlab::Git::DiffCollection)
.to receive(:default_limits)
.and_return({ max_files: max_files, max_lines: 80 })
end
it 'considers size of diffs before the offset for prunning' do

View file

@ -13,6 +13,10 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
let(:client) { described_class.new(repository) }
describe '#diff_from_parent' do
before do
stub_feature_flags(increased_diff_limits: false)
end
context 'when a commit has a parent' do
it 'sends an RPC request with the parent ID as left commit' do
request = Gitaly::CommitDiffRequest.new(

View file

@ -46,64 +46,59 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when status is given' do
it 'mark job as succeeded' do
it 'marks job as succeeded' do
update_job(state: 'success')
job.reload
expect(job).to be_success
expect(job.reload).to be_success
expect(response.header).not_to have_key('X-GitLab-Trace-Update-Interval')
end
it 'mark job as failed' do
it 'marks job as failed' do
update_job(state: 'failed')
job.reload
expect(job).to be_failed
expect(job.reload).to be_failed
expect(job).to be_unknown_failure
expect(response.header).not_to have_key('X-GitLab-Trace-Update-Interval')
end
context 'when failure_reason is script_failure' do
before do
update_job(state: 'failed', failure_reason: 'script_failure')
job.reload
end
it { expect(job).to be_script_failure }
it { expect(job.reload).to be_script_failure }
end
context 'when failure_reason is runner_system_failure' do
before do
update_job(state: 'failed', failure_reason: 'runner_system_failure')
job.reload
end
it { expect(job).to be_runner_system_failure }
it { expect(job.reload).to be_runner_system_failure }
end
context 'when failure_reason is unrecognized value' do
before do
update_job(state: 'failed', failure_reason: 'what_is_this')
job.reload
end
it { expect(job).to be_unknown_failure }
it { expect(job.reload).to be_unknown_failure }
end
context 'when failure_reason is job_execution_timeout' do
before do
update_job(state: 'failed', failure_reason: 'job_execution_timeout')
job.reload
end
it { expect(job).to be_job_execution_timeout }
it { expect(job.reload).to be_job_execution_timeout }
end
context 'when failure_reason is unmet_prerequisites' do
before do
update_job(state: 'failed', failure_reason: 'unmet_prerequisites')
job.reload
end
it { expect(job).to be_unmet_prerequisites }
it { expect(job.reload).to be_unmet_prerequisites }
end
context 'when unmigrated live trace chunks exist' do
@ -119,6 +114,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
expect(job.pending_state).to be_present
expect(response).to have_gitlab_http_status(:accepted)
expect(response.header['X-GitLab-Trace-Update-Interval']).to be > 0
end
end
@ -151,6 +147,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
expect(job.reload).to be_success
expect(job.pending_state).not_to be_present
expect(response).to have_gitlab_http_status(:ok)
expect(response.header).not_to have_key('X-GitLab-Trace-Update-Interval')
end
end
end

View file

@ -1140,7 +1140,7 @@ RSpec.describe API::MergeRequests do
context 'when a merge request has more than the changes limit' do
it "returns a string indicating that more changes were made" do
stub_const('Commit::DIFF_HARD_LIMIT_FILES', 5)
allow(Commit).to receive(:diff_hard_limit_files).and_return(5)
merge_request_overflow = create(:merge_request, :simple,
author: user,

View file

@ -402,7 +402,9 @@ RSpec.describe API::Repositories do
end
it "returns an empty string when the diff overflows" do
stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: 2, max_lines: 2 })
allow(Gitlab::Git::DiffCollection)
.to receive(:default_limits)
.and_return({ max_files: 2, max_lines: 2 })
get api(route, current_user), params: { from: 'master', to: 'feature' }

View file

@ -5,9 +5,27 @@ require 'spec_helper'
RSpec.describe GroupGroupLinkEntity do
include_context 'group_group_link'
subject(:json) { described_class.new(group_group_link).to_json }
let_it_be(:current_user) { create(:user) }
let(:entity) { described_class.new(group_group_link) }
before do
allow(entity).to receive(:current_user).and_return(current_user)
end
it 'matches json schema' do
expect(json).to match_schema('entities/group_group_link')
expect(entity.to_json).to match_schema('entities/group_group_link')
end
context 'a user with :admin_group_member permissions' do
before do
allow(entity).to receive(:can?).with(current_user, :admin_group_member, shared_group).and_return(true)
end
it 'sets `can_update` and `can_remove` to `true`' do
json = entity.as_json
expect(json[:can_update]).to be true
expect(json[:can_remove]).to be true
end
end
end

View file

@ -100,6 +100,12 @@ RSpec.describe Ci::UpdateBuildStateService do
expect(result.status).to eq 200
end
it 'does not set a backoff value' do
result = subject.execute
expect(result.backoff).to be_nil
end
it 'increments trace finalized operation metric' do
execute_with_stubbed_metrics!
@ -126,6 +132,12 @@ RSpec.describe Ci::UpdateBuildStateService do
expect(result.status).to eq 202
end
it 'sets a request backoff value' do
result = subject.execute
expect(result.backoff.to_i).to be > 0
end
it 'schedules live chunks for migration' do
expect(Ci::BuildTraceChunkFlushWorker)
.to receive(:perform_async)

View file

@ -48,17 +48,10 @@ module WaitForRequests
def finished_all_js_requests?
return true unless javascript_test?
finished_all_ajax_requests? &&
finished_all_axios_requests?
end
def finished_all_axios_requests?
Capybara.page.evaluate_script('window.pendingRequests || 0').zero? # rubocop:disable Style/NumericPredicate
finished_all_ajax_requests?
end
def finished_all_ajax_requests?
return true if Capybara.page.evaluate_script('typeof jQuery === "undefined"')
Capybara.page.evaluate_script('jQuery.active').zero? # rubocop:disable Style/NumericPredicate
Capybara.page.evaluate_script('window.pendingRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate
end
end

View file

@ -1076,6 +1076,11 @@
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.3-1.tgz#9b9eb8858a6507162911007d355d9a206e1c5caa"
integrity sha512-szFhWD+V5TAxVNVIG16klgq+ypqA5k5AecLarTTrXgOG8cawVbQdOAwLbCmzkwiQ60rGSxAFoC1u2LrzxSK2Aw==
"@rails/ujs@^6.0.3-2":
version "6.0.3-2"
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.0.3-2.tgz#e14c1f29086858215ce7ccd9ad6d8888c458b4a3"
integrity sha512-WcpIEftNCfGDEgk6KerOugiet75Mir5q/HT1yt3dDhpBI91BaZ15lfSQIsZwMw2nyeDz9A9QBz8dAFAd4gXIzg==
"@sentry/browser@^5.22.3":
version "5.22.3"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.22.3.tgz#7a64bd1cf01bf393741a3e4bf35f82aa927f5b4e"
@ -7068,13 +7073,6 @@ jmespath@0.15.0:
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=
jquery-ujs@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.2.tgz#6a8ef1020e6b6dda385b90a4bddc128c21c56397"
integrity sha1-ao7xAg5rbdo4W5CkvdwSjCHFY5c=
dependencies:
jquery ">=1.8.0"
jquery.caret@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/jquery.caret/-/jquery.caret-0.3.1.tgz#9c093318faf327eff322e826ca9f3241368bc7b8"
@ -7085,7 +7083,7 @@ jquery.waitforimages@^2.2.0:
resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b"
integrity sha1-Y/IxMQVaGwYNyRPm2HS8ybnmsWs=
"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^3.5.0:
"jquery@>= 1.9.1", jquery@^3.5.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==