Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-01-13 09:10:52 +00:00
parent e6f170581e
commit ab9c1dbb2d
33 changed files with 405 additions and 363 deletions

View File

@ -352,8 +352,9 @@ end
group :development, :test do
gem 'deprecation_toolkit', '~> 1.5.1', require: false
gem 'bullet', '~> 6.1.0'
gem 'pry-byebug', '~> 3.9.0', platform: :mri
gem 'gitlab-pry-byebug', platform: :mri, require: ['pry-byebug', 'pry-byebug/pry_remote_ext']
gem 'pry-rails', '~> 0.3.9'
gem 'pry-remote'
gem 'awesome_print', require: false

View File

@ -445,6 +445,9 @@ GEM
gitlab-markup (1.7.1)
gitlab-net-dns (0.9.1)
gitlab-pg_query (1.3.1)
gitlab-pry-byebug (3.9.0)
byebug (~> 11.0)
pry (~> 0.13.0)
gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5)
gitlab-styles (6.0.0)
@ -864,11 +867,11 @@ GEM
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.9.0)
byebug (~> 11.0)
pry (~> 0.13.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
pry-remote (0.1.8)
pry (~> 0.9)
slop (~> 3.0)
public_suffix (4.0.6)
puma (5.1.1)
nio4r (~> 2.0)
@ -1131,6 +1134,7 @@ GEM
simplecov-html (0.12.2)
sixarm_ruby_unaccent (1.2.0)
slack-messenger (2.3.4)
slop (3.6.0)
snowplow-tracker (0.6.1)
contracts (~> 0.7, <= 0.11)
spring (2.1.1)
@ -1364,6 +1368,7 @@ DEPENDENCIES
gitlab-mail_room (~> 0.0.8)
gitlab-markup (~> 1.7.1)
gitlab-net-dns (~> 0.9.1)
gitlab-pry-byebug
gitlab-sidekiq-fetcher (= 0.5.2)
gitlab-styles (~> 6.0.0)
gitlab_chronic_duration (~> 0.10.6.2)
@ -1455,8 +1460,8 @@ DEPENDENCIES
png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.12.0)
pry-byebug (~> 3.9.0)
pry-rails (~> 0.3.9)
pry-remote
puma (~> 5.1.1)
puma_worker_killer (~> 0.3.1)
rack (~> 2.2.3)

View File

@ -5,12 +5,10 @@ import MembersTable from '~/members/components/table/members_table.vue';
import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
import { HIDE_ERROR } from '~/members/store/mutation_types';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'GroupMembersApp',
components: { MembersTable, FilterSortContainer, GlAlert },
mixins: [glFeatureFlagsMixin()],
computed: {
...mapState(['showError', 'errorMessage']),
},
@ -36,7 +34,7 @@ export default {
<gl-alert v-if="showError" ref="errorAlert" variant="danger" @dismiss="hideError">{{
errorMessage
}}</gl-alert>
<filter-sort-container v-if="glFeatures.groupMembersFilteredSearch" />
<filter-sort-container />
<members-table />
</div>
</template>

View File

@ -1,16 +1,12 @@
<script>
import { mapState } from 'vuex';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'JiraConnectApp',
mixins: [glFeatureFlagsMixin()],
computed: {
state() {
return this.$root.$data.state || {};
},
error() {
return this.state.error;
},
...mapState(['errorMessage']),
showNewUi() {
return this.glFeatures.newJiraConnectUi;
},
@ -21,7 +17,7 @@ export default {
<template>
<div>
<div v-if="showNewUi">
<h3>{{ s__('Integrations|Linked namespaces') }}</h3>
<h3 data-testid="new-jira-connect-ui-heading">{{ s__('Integrations|Linked namespaces') }}</h3>
</div>
</div>
</template>

View File

@ -1,23 +1,21 @@
import Vue from 'vue';
import Vuex from 'vuex';
import $ from 'jquery';
import setConfigs from '@gitlab/ui/dist/config';
import Translate from '~/vue_shared/translate';
import GlFeatureFlagsPlugin from '~/vue_shared/gl_feature_flags_plugin';
import App from './components/app.vue';
import JiraConnectApp from './components/app.vue';
import { addSubscription, removeSubscription } from '~/jira_connect/api';
import createStore from './store';
import { SET_ERROR_MESSAGE } from './store/mutation_types';
const store = {
state: {
error: '',
},
setErrorMessage(errorMessage) {
this.state.error = errorMessage;
},
};
Vue.use(Vuex);
const store = createStore();
/**
* Initialize necessary form handlers for the Jira Connect app
* Initialize form handlers for the Jira Connect app
*/
const initJiraFormHandlers = () => {
const reqComplete = () => {
@ -27,7 +25,7 @@ const initJiraFormHandlers = () => {
const reqFailed = (res, fallbackErrorMessage) => {
const { responseJSON: { error = fallbackErrorMessage } = {} } = res || {};
store.setErrorMessage(error);
store.commit(SET_ERROR_MESSAGE, error);
// eslint-disable-next-line no-alert
alert(error);
};
@ -79,11 +77,9 @@ function initJiraConnect() {
return new Vue({
el,
data: {
state: store.state,
},
store,
render(createElement) {
return createElement(App, {});
return createElement(JiraConnectApp, {});
},
});
}

View File

@ -0,0 +1,9 @@
import Vuex from 'vuex';
import mutations from './mutations';
import state from './state';
export default () =>
new Vuex.Store({
state,
mutations,
});

View File

@ -0,0 +1 @@
export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';

View File

@ -0,0 +1,7 @@
import { SET_ERROR_MESSAGE } from './mutation_types';
export default {
[SET_ERROR_MESSAGE](state, errorMessage) {
state.errorMessage = errorMessage;
},
};

View File

@ -0,0 +1,3 @@
export default () => ({
errorMessage: undefined,
});

View File

@ -3,17 +3,15 @@ import NewBranchForm from '~/new_branch_form';
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
import initNewPipeline from '~/pipeline_new/index';
document.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('js-new-pipeline');
const el = document.getElementById('js-new-pipeline');
if (el) {
initNewPipeline();
} else {
new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new
if (el) {
initNewPipeline();
} else {
new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new
setupNativeFormVariableList({
container: $('.js-ci-variable-list-section'),
formField: 'variables_attributes',
});
}
});
setupNativeFormVariableList({
container: $('.js-ci-variable-list-section'),
formField: 'variables_attributes',
});
}

View File

@ -45,7 +45,8 @@
a {
font-family: $monospace-font;
display: block;
display: flex;
justify-content: flex-end;
font-size: $code-font-size !important;
white-space: nowrap;

View File

@ -103,14 +103,6 @@ class ApplicationController < ActionController::Base
head :forbidden, retry_after: Gitlab::Auth::UniqueIpsLimiter.config.unique_ips_limit_time_window
end
rescue_from GRPC::Unavailable, Gitlab::Git::CommandError do |exception|
log_exception(exception)
headers['Retry-After'] = exception.retry_after if exception.respond_to?(:retry_after)
render_503
end
def redirect_back_or_default(default: root_path, options: {})
redirect_back(fallback_location: default, **options)
end
@ -246,19 +238,6 @@ class ApplicationController < ActionController::Base
head :unprocessable_entity
end
def render_503
respond_to do |format|
format.html do
render(
file: Rails.root.join("public", "503"),
layout: false,
status: :service_unavailable
)
end
format.any { head :service_unavailable }
end
end
def no_cache_headers
DEFAULT_GITLAB_NO_CACHE_HEADERS.each do |k, v|
headers[k] = v

View File

@ -14,10 +14,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
before_action do
push_frontend_feature_flag(:group_members_filtered_search, @group, default_enabled: true)
end
skip_before_action :check_two_factor_requirement, only: :leave
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
:approve_access_request, :leave, :resend_invite,

View File

@ -94,7 +94,7 @@ class ProjectsController < Projects::ApplicationController
redirect_to(edit_project_path(@project, anchor: 'js-general-project-settings'))
end
else
flash.now[:alert] = result[:message]
flash[:alert] = result[:message]
@project.reset
format.html { render_edit }

View File

@ -3,7 +3,7 @@
.container
.gl-mt-3
- if Gitlab.ee? && License.feature_available?(:devops_adoption)
- if Gitlab.ee? && Feature.enabled?(:devops_adoption_feature, default_enabled: false) && License.feature_available?(:devops_adoption)
= render_if_exists 'admin/dev_ops_report/devops_tabs'
- else
= render 'report'

View File

@ -3,8 +3,6 @@
- show_invited_members = can_manage_members && @invited_members.exists?
- show_access_requests = can_manage_members && @requesters.exists?
- invited_active = params[:search_invited].present? || params[:invited_members_page].present?
- filtered_search_enabled = Feature.enabled?(:group_members_filtered_search, @group, default_enabled: true)
- form_item_label_css_class = 'label-bold gl-mr-2 gl-mb-0 gl-py-2 align-self-md-center'
.js-remove-member-modal
.project-members-page.gl-mt-3
@ -51,52 +49,23 @@
%span.badge.badge-pill= @requesters.count
.tab-content
#tab-members.tab-pane{ class: ('active' unless invited_active) }
- unless filtered_search_enabled
= render 'shared/members/tab_pane/header' do
= render 'shared/members/tab_pane/title' do
= html_escape(_('Members with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
= form_tag group_group_members_path(@group), method: :get, class: 'user-search-form gl-display-flex gl-md-align-items-center gl-flex-wrap gl-flex-direction-column gl-md-flex-direction-row gl-mx-n3 gl-my-n3', data: { testid: 'user-search-form' } do
.gl-px-3.gl-py-2
.search-control-wrap.gl-relative
= render 'shared/members/search_field'
- if can_manage_members
= render 'shared/members/tab_pane/form_item' do
= label_tag '2fa', _('2FA'), class: form_item_label_css_class
= render 'shared/members/filter_2fa_dropdown'
= render 'shared/members/tab_pane/form_item' do
= label_tag :sort_by, _('Sort by'), class: form_item_label_css_class
= render 'shared/members/sort_dropdown'
.js-group-members-list{ data: group_members_list_data_attributes(@group, @members) }
.loading
.spinner.spinner-md
= paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil }
- if @group.shared_with_group_links.any?
#tab-groups.tab-pane
- unless filtered_search_enabled
= render 'shared/members/tab_pane/header' do
= render 'shared/members/tab_pane/title' do
= html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
.js-group-linked-list{ data: linked_groups_list_data_attributes(@group) }
.loading
.spinner.spinner-md
- if show_invited_members
#tab-invited-members.tab-pane{ class: ('active' if invited_active) }
- unless filtered_search_enabled
= render 'shared/members/tab_pane/header' do
= render 'shared/members/tab_pane/title' do
= html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
= form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do
= render 'shared/members/search_field', name: 'search_invited'
.js-group-invited-members-list{ data: group_members_list_data_attributes(@group, @invited_members) }
.loading
.spinner.spinner-md
= paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab', params: { page: nil }
- if show_access_requests
#tab-access-requests.tab-pane
- unless filtered_search_enabled
= render 'shared/members/tab_pane/header' do
= render 'shared/members/tab_pane/title' do
= html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
.js-group-access-requests-list{ data: group_members_list_data_attributes(@group, @requesters) }
.loading
.spinner.spinner-md

View File

@ -0,0 +1,5 @@
---
title: Correctly handle Gitaly being unavailable in more locations
merge_request: 51222
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fix cut off line number in file blame
merge_request: 51259
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Remove pagination from Deployment Frequency API endpoint
merge_request: 51137
author:
type: changed

View File

@ -1,8 +1,8 @@
---
name: group_members_filtered_search
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48272
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/289911
milestone: '13.7'
name: devops_adoption_feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46005
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/271568
milestone: '13.6'
type: development
group: group::access
default_enabled: true
group: group::optimize
default_enabled: false

View File

@ -90,8 +90,8 @@ the configuration below accordingly.
### Making local PlantUML accessible using custom GitLab setup
The PlantUML server runs locally on your server, so it is not accessible
externally. As such, it is necessary to catch external PlantUML calls and
redirect them to the local server.
externally by default. As such, it is necessary to catch external PlantUML
calls and redirect them to the local server.
The idea is to redirect each call to `https://gitlab.example.com/-/plantuml/`
to the local PlantUML server `http://plantuml:8080/` or `http://localhost:8080/plantuml/`, depending on your setup.
@ -112,6 +112,12 @@ To activate the changes, run the following command:
sudo gitlab-ctl reconfigure
```
Note that the redirection through GitLab **must** be configured
when running [GitLab with TLS](https://docs.gitlab.com/omnibus/settings/ssl.html)
due to PlantUML's use of the insecure HTTP protocol. Newer browsers such
as [Google Chrome 86+](https://www.chromestatus.com/feature/4926989725073408)
do not load insecure HTTP resources on a page served over HTTPS.
### Security
PlantUML has features that allows fetching network resources.

View File

@ -14,6 +14,7 @@ type: reference, api
> - `author_username` and `author_username` were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 12.10.
> - `reference` was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20354) in GitLab 12.10 in favour of `references`.
> - `with_merge_status_recheck` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31890) in GitLab 13.0.
> - `reviewer_username` and `reviewer_id` were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8.
Every API call to merge requests must be authenticated.
@ -92,13 +93,15 @@ Parameters:
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `approver_ids` **(STARTER)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
| `approved_by_ids` **(STARTER)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
| `reviewer_id` | integer | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
| `reviewer_username` | string | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `source_branch` | string | no | Return merge requests with the given source branch. |
| `target_branch` | string | no | Return merge requests with the given target branch. |
| `search` | string | no | Search merge requests against their `title` and `description`. |
| `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. |
| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests. |
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `my_reaction_emoji`. |
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
| `environment` | string | no | Returns merge requests deployed to the given environment. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
| `deployed_before` | datetime | no | Return merge requests deployed before the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
| `deployed_after` | datetime | no | Return merge requests deployed after the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
@ -153,6 +156,14 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
"web_url": "https://gitlab.example.com/axel.block"
}],
"reviewers": [{
"id": 2,
"name": "Sam Bauch",
"username": "kenyatta_oconnell",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
}],
"source_project_id": 2,
"target_project_id": 3,
"labels": [
@ -268,11 +279,15 @@ Parameters:
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `approver_ids` **(STARTER)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
| `approved_by_ids` **(STARTER)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
| `reviewer_id` | integer | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
| `reviewer_username` | string | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#reviewer) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `source_branch` | string | no | Return merge requests with the given source branch. |
| `target_branch` | string | no | Return merge requests with the given target branch. |
| `search` | string | no | Search merge requests against their `title` and `description`. |
| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests. |
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
```json
[
@ -324,6 +339,14 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
"web_url": "https://gitlab.example.com/axel.block"
}],
"reviewers": [{
"id": 2,
"name": "Sam Bauch",
"username": "kenyatta_oconnell",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
}],
"source_project_id": 2,
"target_project_id": 3,
"labels": [
@ -432,11 +455,14 @@ Parameters:
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 9.5)_. |
| `approver_ids` **(STARTER)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
| `approved_by_ids` **(STARTER)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
| `reviewer_id` | integer | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#enable-or-disable-merge-request-reviewers) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. |
| `reviewer_username` | string | no | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/getting_started.md#enable-or-disable-merge-request-reviewers) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14016) in GitLab 10.0)_. |
| `source_branch` | string | no | Return merge requests with the given source branch. |
| `target_branch` | string | no | Return merge requests with the given target branch. |
| `search` | string | no | Search merge requests against their `title` and `description`. |
| `non_archived` | boolean | no | Return merge requests from non archived projects only. Default is true. _(Introduced in [GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23809))_. |
| `not` | Hash | no | Return merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. |
```json
[
@ -488,6 +514,14 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
"web_url": "https://gitlab.example.com/axel.block"
}],
"reviewers": [{
"id": 2,
"name": "Sam Bauch",
"username": "kenyatta_oconnell",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
}],
"source_project_id": 2,
"target_project_id": 3,
"labels": [
@ -618,6 +652,14 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
"web_url": "https://gitlab.example.com/axel.block"
}],
"reviewers": [{
"id": 2,
"name": "Sam Bauch",
"username": "kenyatta_oconnell",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/956c92487c6f6f7616b536927e22c9a0?s=80&d=identicon",
"web_url": "http://gitlab.example.com//kenyatta_oconnell"
}],
"source_project_id": 2,
"target_project_id": 3,
"labels": [

View File

@ -38,7 +38,7 @@ collected before this feature is available.
## DevOps Adoption **(ULTIMATE)**
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247112) in GitLab 13.7.
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247112) in GitLab 13.7 as a [Beta feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta).
The DevOps Adoption tab shows you which segments of your organization are using the most essential features of GitLab:
@ -61,3 +61,21 @@ DevOps Adoption allows you to:
- Find the groups that have adopted certain features and can provide guidance to other groups on how to use those features.
![DevOps Report](img/dev_ops_adoption_v13_7.png)
### Disable or enable DevOps Adoption
DevOps Adoption is deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can opt to enable it.
To enable it:
```ruby
Feature.enable(:devops_adoption_feature)
```
To disable it:
```ruby
Feature.disable(:devops_adoption_feature)
```

View File

@ -215,10 +215,7 @@ To remove a member from a group:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6.
> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/228675) in GitLab 13.7.
> - Improvements are [deployed behind a feature flag](../feature_flags.md), enabled by default.
> - Improvements are enabled on GitLab.com.
> - Improvements are recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable improvements](#enable-or-disable-improvements-to-the-ability-to-filter-and-sort-group-members). **(CORE ONLY)**
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/289911) in GitLab 13.8.
The following sections illustrate how you can filter and sort members in a group. To view these options,
navigate to your desired group, go to **Members**, and include the noted search terms.
@ -269,30 +266,6 @@ You can sort members by **Account**, **Access granted**, **Max role**, or **Last
![Group members sort](img/group_members_sort_13_7.png)
### Enable or disable improvements to the ability to filter and sort group members **(CORE ONLY)**
Group member filtering and sorting improvements are deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can opt to disable the improvements.
To disable them:
```ruby
# For the instance
Feature.disable(:group_members_filtered_search)
# For a single group
Feature.disable(:group_members_filtered_search, Group.find(<group id>))
```
To enable them:
```ruby
# For the instance
Feature.enable(:group_members_filtered_search)
# For a single group
Feature.enable(:group_members_filtered_search, Group.find(<group id>))
```
## Changing the default branch protection of a group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7583) in GitLab 12.9.

View File

@ -21,6 +21,9 @@ module API
coerce_with: Validations::Validators::CheckAssigneesCount.coerce,
desc: 'Return merge requests which are assigned to the user with the given username'
mutually_exclusive :assignee_id, :assignee_username
optional :reviewer_username,
type: String,
desc: 'Return merge requests which have the user as a reviewer with the given username'
optional :labels,
type: Array[String],
@ -32,6 +35,11 @@ module API
params :merge_requests_base_params do
use :merge_requests_negatable_params
optional :reviewer_id,
types: [Integer, String],
integer_none_any: true,
desc: 'Return merge requests which have the user as a reviewer with the given ID'
mutually_exclusive :reviewer_id, :reviewer_username
optional :state,
type: String,
values: %w[opened closed locked merged all],
@ -72,6 +80,10 @@ module API
optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
optional :not, type: Hash, desc: 'Parameters to negate' do
use :merge_requests_negatable_params
optional :reviewer_id,
types: Integer,
desc: 'Return merge requests which have the user as a reviewer with the given ID'
mutually_exclusive :reviewer_id, :reviewer_username
end
optional :deployed_before,

View File

@ -14114,9 +14114,6 @@ msgstr ""
msgid "Groups with access to %{strong_open}%{project_name}%{strong_close}"
msgstr ""
msgid "Groups with access to %{strong_start}%{group_name}%{strong_end}"
msgstr ""
msgid "GroupsDropdown|Frequently visited"
msgstr ""
@ -17367,9 +17364,6 @@ msgstr ""
msgid "Members can be added by project %{i_open}Maintainers%{i_close} or %{i_open}Owners%{i_close}"
msgstr ""
msgid "Members invited to %{strong_start}%{group_name}%{strong_end}"
msgstr ""
msgid "Members invited to %{strong_start}%{project_name}%{strong_end}"
msgstr ""
@ -17388,9 +17382,6 @@ msgstr ""
msgid "Members of a group may only view projects they have permission to access"
msgstr ""
msgid "Members with access to %{strong_start}%{group_name}%{strong_end}"
msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
@ -30911,9 +30902,6 @@ msgstr ""
msgid "Users requesting access to"
msgstr ""
msgid "Users requesting access to %{strong_start}%{group_name}%{strong_end}"
msgstr ""
msgid "Users requesting access to %{strong_start}%{project_name}%{strong_end}"
msgstr ""
@ -32938,6 +32926,9 @@ msgstr ""
msgid "can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'"
msgstr ""
msgid "can't be enabled because signed commits are required for this project"
msgstr ""
msgid "cannot be a date in the past"
msgstr ""

View File

@ -211,21 +211,6 @@ RSpec.describe ProjectsController do
end
end
context 'when the storage is not available', :broken_storage do
let_it_be(:project) { create(:project, :broken_storage) }
before do
project.add_developer(user)
sign_in(user)
end
it 'renders a 503' do
get :show, params: { namespace_id: project.namespace, id: project }
expect(response).to have_gitlab_http_status(:service_unavailable)
end
end
context "project with empty repo" do
let_it_be(:empty_project) { create(:project_empty_repo, :public) }
@ -616,7 +601,7 @@ RSpec.describe ProjectsController do
expect { update_project path: 'renamed_path' }
.not_to change { project.reload.path }
expect(controller).to set_flash.now[:alert].to(s_('UpdateProject|Cannot rename project because it contains container registry tags!'))
expect(controller).to set_flash[:alert].to(s_('UpdateProject|Cannot rename project because it contains container registry tags!'))
expect(response).to have_gitlab_http_status(:ok)
end
end

View File

@ -16,174 +16,92 @@ RSpec.describe 'Groups > Members > Sort members', :js do
sign_in(owner)
end
context 'when `group_members_filtered_search` feature flag is enabled' do
def expect_sort_by(text, sort_direction)
within('[data-testid="members-sort-dropdown"]') do
expect(page).to have_css('button[aria-haspopup="true"]', text: text)
expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end
end
it 'sorts by account by default' do
visit_members_list(sort: nil)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect_sort_by('Account', :asc)
end
it 'sorts by max role ascending' do
visit_members_list(sort: :access_level_asc)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect_sort_by('Max role', :asc)
end
it 'sorts by max role descending' do
visit_members_list(sort: :access_level_desc)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect_sort_by('Max role', :desc)
end
it 'sorts by access granted ascending' do
visit_members_list(sort: :last_joined)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect_sort_by('Access granted', :asc)
end
it 'sorts by access granted descending' do
visit_members_list(sort: :oldest_joined)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect_sort_by('Access granted', :desc)
end
it 'sorts by account ascending' do
visit_members_list(sort: :name_asc)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect_sort_by('Account', :asc)
end
it 'sorts by account descending' do
visit_members_list(sort: :name_desc)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect_sort_by('Account', :desc)
end
it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :recent_sign_in)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect_sort_by('Last sign-in', :asc)
end
it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :oldest_sign_in)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect_sort_by('Last sign-in', :desc)
def expect_sort_by(text, sort_direction)
within('[data-testid="members-sort-dropdown"]') do
expect(page).to have_css('button[aria-haspopup="true"]', text: text)
expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end
end
context 'when `group_members_filtered_search` feature flag is disabled' do
dropdown_toggle_selector = '[data-testid="user-sort-dropdown"] [data-testid="dropdown-toggle"]'
it 'sorts by account by default' do
visit_members_list(sort: nil)
before do
stub_feature_flags(group_members_filtered_search: false)
end
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
it 'sorts alphabetically by default' do
visit_members_list(sort: nil)
expect_sort_by('Account', :asc)
end
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
end
it 'sorts by max role ascending' do
visit_members_list(sort: :access_level_asc)
it 'sorts by access level ascending' do
visit_members_list(sort: :access_level_asc)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, ascending')
end
expect_sort_by('Max role', :asc)
end
it 'sorts by access level descending' do
visit_members_list(sort: :access_level_desc)
it 'sorts by max role descending' do
visit_members_list(sort: :access_level_desc)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, descending')
end
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
it 'sorts by last joined' do
visit_members_list(sort: :last_joined)
expect_sort_by('Max role', :desc)
end
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Last joined')
end
it 'sorts by access granted ascending' do
visit_members_list(sort: :last_joined)
it 'sorts by oldest joined' do
visit_members_list(sort: :oldest_joined)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest joined')
end
expect_sort_by('Access granted', :asc)
end
it 'sorts by name ascending' do
visit_members_list(sort: :name_asc)
it 'sorts by access granted descending' do
visit_members_list(sort: :oldest_joined)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
end
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
it 'sorts by name descending' do
visit_members_list(sort: :name_desc)
expect_sort_by('Access granted', :desc)
end
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Name, descending')
end
it 'sorts by account ascending' do
visit_members_list(sort: :name_asc)
it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :recent_sign_in)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Recent sign in')
end
expect_sort_by('Account', :asc)
end
it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :oldest_sign_in)
it 'sorts by account descending' do
visit_members_list(sort: :name_desc)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest sign in')
end
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect_sort_by('Account', :desc)
end
it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :recent_sign_in)
expect(first_row.text).to include(owner.name)
expect(second_row.text).to include(developer.name)
expect_sort_by('Last sign-in', :asc)
end
it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :oldest_sign_in)
expect(first_row.text).to include(developer.name)
expect(second_row.text).to include(owner.name)
expect_sort_by('Last sign-in', :desc)
end
def visit_members_list(sort:)

View File

@ -87,21 +87,9 @@ describe('GroupMembersApp', () => {
});
});
describe.each`
featureFlagValue | exists
${true} | ${true}
${false} | ${false}
`(
'when `group_members_filtered_search` feature flag is $featureFlagValue',
({ featureFlagValue, exists }) => {
it(`${exists ? 'renders' : 'does not render'} FilterSortContainer`, () => {
createComponent(
{},
{ provide: { glFeatures: { groupMembersFilteredSearch: featureFlagValue } } },
);
it('renders `FilterSortContainer`', () => {
createComponent();
expect(findFilterSortContainer().exists()).toBe(exists);
});
},
);
expect(findFilterSortContainer().exists()).toBe(true);
});
});

View File

@ -1,17 +1,22 @@
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import JiraConnectApp from '~/jira_connect/components/app.vue';
describe('JiraConnectApp', () => {
let wrapper;
const findHeader = () => wrapper.findByTestId('new-jira-connect-ui-heading');
const findHeaderText = () => findHeader().text();
const createComponent = (options = {}) => {
wrapper = shallowMount(JiraConnectApp, {
provide: {
glFeatures: { newJiraConnectUi: true },
},
...options,
});
wrapper = extendedWrapper(
shallowMount(JiraConnectApp, {
provide: {
glFeatures: { newJiraConnectUi: true },
},
...options,
}),
);
};
afterEach(() => {
@ -19,9 +24,6 @@ describe('JiraConnectApp', () => {
wrapper = null;
});
const findHeader = () => wrapper.find('h3');
const findHeaderText = () => findHeader().text();
describe('template', () => {
it('renders new UI', () => {
createComponent();

View File

@ -0,0 +1,18 @@
import mutations from '~/jira_connect/store/mutations';
import state from '~/jira_connect/store/state';
describe('JiraConnect store mutations', () => {
let localState;
beforeEach(() => {
localState = state();
});
describe('SET_ERROR_MESSAGE', () => {
it('sets error message', () => {
mutations.SET_ERROR_MESSAGE(localState, 'test error');
expect(localState.errorMessage).toBe('test error');
});
});
});

View File

@ -440,6 +440,7 @@ RSpec.describe API::MergeRequests do
milestone: milestone,
author: user,
assignees: [user],
reviewers: [user2],
source_project: project,
target_project: project,
source_branch: 'what',
@ -498,6 +499,71 @@ RSpec.describe API::MergeRequests do
expect(mr['assignee']['id']).not_to eq(user2.id)
end
end
context 'filter by reviewer' do
context 'with reviewer_id' do
context 'with an id' do
let(:params) { { not: { reviewer_id: user2.id } } }
it 'returns merge requests that do not have the given reviewer' do
get api(endpoint_path, user), params: { not: { reviewer_id: user2.id } }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an(Array)
expect(json_response.length).to eq(4)
expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request2)
end
end
context 'with Any' do
let(:params) { { not: { reviewer_id: 'Any' } } }
it 'returns a 400' do
# Any is not supported for negated filter
get api(endpoint_path, user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('not[reviewer_id] is invalid')
end
end
context 'with None' do
let(:params) { { not: { reviewer_id: 'None' } } }
it 'returns a 400' do
# None is not supported for negated filter
get api(endpoint_path, user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('not[reviewer_id] is invalid')
end
end
end
context 'with reviewer_username' do
let(:params) { { not: { reviewer_username: user2.username } } }
it 'returns merge requests that do not have the given reviewer' do
get api(endpoint_path, user), params: params
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an(Array)
expect(json_response.length).to eq(4)
expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request2)
end
end
context 'when both reviewer_id and reviewer_username' do
let(:params) { { not: { reviewer_id: user2.id, reviewer_username: user2.username } } }
it 'returns a 400' do
get api('/merge_requests', user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('not[reviewer_id], not[reviewer_username] are mutually exclusive')
end
end
end
end
context 'source_branch param' do
@ -666,6 +732,79 @@ RSpec.describe API::MergeRequests do
end
end
context 'filter by reviewer' do
let_it_be(:review_requested_mr1) do
create(:merge_request, :unique_branches, author: user, reviewers: [user2], source_project: project2, target_project: project2)
end
let_it_be(:review_requested_mr2) do
create(:merge_request, :unique_branches, author: user2, reviewers: [user], source_project: project2, target_project: project2)
end
let(:params) { { scope: :all } }
context 'with reviewer_id' do
let(:params) { super().merge(reviewer_id: reviewer_id) }
context 'with an id' do
let(:reviewer_id) { user2.id }
it 'returns review requested merge requests for the given user' do
get api('/merge_requests', user), params: params
expect_response_contain_exactly(review_requested_mr1.id)
end
end
context 'with Any' do
let(:reviewer_id) { 'Any' }
it 'returns review requested merge requests for any user' do
get api('/merge_requests', user), params: params
expect_response_contain_exactly(review_requested_mr1.id, review_requested_mr2.id)
end
end
context 'with None' do
let(:reviewer_id) { 'None' }
it 'returns merge requests that has no assigned reviewers' do
get api('/merge_requests', user), params: params
expect_response_contain_exactly(
merge_request.id,
merge_request_closed.id,
merge_request_merged.id,
merge_request_locked.id,
merge_request2.id
)
end
end
end
context 'with reviewer_username' do
let(:params) { super().merge(reviewer_username: user2.username) }
it 'returns review requested merge requests for the given user' do
get api('/merge_requests', user), params: params
expect_response_contain_exactly(review_requested_mr1.id)
end
end
context 'with both reviewer_id and reviewer_username' do
let(:params) { super().merge(reviewer_id: user2.id, reviewer_username: user2.username) }
it 'returns a 400' do
get api('/merge_requests', user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('reviewer_id, reviewer_username are mutually exclusive')
end
end
end
it 'returns an array of merge requests assigned to the given user' do
merge_request3 = create(:merge_request, :simple, author: user, assignees: [user2], source_project: project2, target_project: project2, source_branch: 'other-branch')

View File

@ -77,30 +77,6 @@ RSpec.shared_examples Repositories::GitHttpController do
end
end
end
context 'with exceptions' do
before do
allow(controller).to receive(:authenticate_user).and_return(true)
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
end
it 'returns 503 with GRPC Unavailable' do
allow(controller).to receive(:access_check).and_raise(GRPC::Unavailable)
get :info_refs, params: params
expect(response).to have_gitlab_http_status(:service_unavailable)
end
it 'returns 503 with timeout error' do
allow(controller).to receive(:access_check).and_raise(Gitlab::GitAccess::TimeoutError)
get :info_refs, params: params
expect(response).to have_gitlab_http_status(:service_unavailable)
expect(response.body).to eq 'Gitlab::GitAccess::TimeoutError'
end
end
end
describe 'POST #git_upload_pack' do