Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
234dc40a12
commit
e9322e019b
|
@ -1,7 +1,7 @@
|
||||||
// NOTE: This module will be used in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52044
|
// NOTE: This module will be used in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52044
|
||||||
import { memoize } from 'lodash';
|
import { memoize } from 'lodash';
|
||||||
|
|
||||||
export const RECAPTCHA_API_URL_PREFIX = 'https://www.google.com/recaptcha/api.js';
|
export const RECAPTCHA_API_URL_PREFIX = window.gon.recaptcha_api_server_url;
|
||||||
export const RECAPTCHA_ONLOAD_CALLBACK_NAME = 'recaptchaOnloadCallback';
|
export const RECAPTCHA_ONLOAD_CALLBACK_NAME = 'recaptchaOnloadCallback';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -40,24 +40,31 @@ export default {
|
||||||
return this.item.type === ITEM_TYPE.GROUP;
|
return this.item.type === ITEM_TYPE.GROUP;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
displayValue(value) {
|
||||||
|
return this.isGroup && value !== undefined;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="stats gl-text-gray-500">
|
<div class="stats gl-text-gray-500">
|
||||||
<item-stats-value
|
<item-stats-value
|
||||||
v-if="isGroup"
|
v-if="displayValue(item.subgroupCount)"
|
||||||
:title="__('Subgroups')"
|
:title="__('Subgroups')"
|
||||||
:value="item.subgroupCount"
|
:value="item.subgroupCount"
|
||||||
css-class="number-subgroups gl-ml-5"
|
css-class="number-subgroups gl-ml-5"
|
||||||
icon-name="folder-o"
|
icon-name="folder-o"
|
||||||
|
data-testid="subgroups-count"
|
||||||
/>
|
/>
|
||||||
<item-stats-value
|
<item-stats-value
|
||||||
v-if="isGroup"
|
v-if="displayValue(item.projectCount)"
|
||||||
:title="__('Projects')"
|
:title="__('Projects')"
|
||||||
:value="item.projectCount"
|
:value="item.projectCount"
|
||||||
css-class="number-projects gl-ml-5"
|
css-class="number-projects gl-ml-5"
|
||||||
icon-name="bookmark"
|
icon-name="bookmark"
|
||||||
|
data-testid="projects-count"
|
||||||
/>
|
/>
|
||||||
<item-stats-value
|
<item-stats-value
|
||||||
v-if="isGroup"
|
v-if="isGroup"
|
||||||
|
|
|
@ -64,6 +64,8 @@ import syntaxHighlight from './syntax_highlight';
|
||||||
// </div>
|
// </div>
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// <100ms is typically indistinguishable from "instant" for users, but allows for re-rendering
|
||||||
|
const FAST_DELAY_FOR_RERENDER = 75;
|
||||||
// Store the `location` object, allowing for easier stubbing in tests
|
// Store the `location` object, allowing for easier stubbing in tests
|
||||||
let { location } = window;
|
let { location } = window;
|
||||||
|
|
||||||
|
@ -83,6 +85,8 @@ export default class MergeRequestTabs {
|
||||||
this.peek = document.getElementById('js-peek');
|
this.peek = document.getElementById('js-peek');
|
||||||
this.paddingTop = 16;
|
this.paddingTop = 16;
|
||||||
|
|
||||||
|
this.scrollPositions = {};
|
||||||
|
|
||||||
this.commitsTab = document.querySelector('.tab-content .commits.tab-pane');
|
this.commitsTab = document.querySelector('.tab-content .commits.tab-pane');
|
||||||
|
|
||||||
this.currentTab = null;
|
this.currentTab = null;
|
||||||
|
@ -136,11 +140,30 @@ export default class MergeRequestTabs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeScroll() {
|
||||||
|
if (this.currentTab) {
|
||||||
|
this.scrollPositions[this.currentTab] = document.documentElement.scrollTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recallScroll(action) {
|
||||||
|
const storedPosition = this.scrollPositions[action];
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: storedPosition && storedPosition > 0 ? storedPosition : 0,
|
||||||
|
left: 0,
|
||||||
|
behavior: 'auto',
|
||||||
|
});
|
||||||
|
}, FAST_DELAY_FOR_RERENDER);
|
||||||
|
}
|
||||||
|
|
||||||
clickTab(e) {
|
clickTab(e) {
|
||||||
if (e.currentTarget) {
|
if (e.currentTarget) {
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.storeScroll();
|
||||||
|
|
||||||
const { action } = e.currentTarget.dataset || {};
|
const { action } = e.currentTarget.dataset || {};
|
||||||
|
|
||||||
if (isMetaClick(e)) {
|
if (isMetaClick(e)) {
|
||||||
|
@ -210,8 +233,14 @@ export default class MergeRequestTabs {
|
||||||
this.resetViewContainer();
|
this.resetViewContainer();
|
||||||
this.mountPipelinesView();
|
this.mountPipelinesView();
|
||||||
} else {
|
} else {
|
||||||
this.mergeRequestTabPanes.querySelector('#notes').style.display = 'block';
|
const notesTab = this.mergeRequestTabs.querySelector('.notes-tab');
|
||||||
this.mergeRequestTabs.querySelector('.notes-tab').classList.add('active');
|
const notesPane = this.mergeRequestTabPanes.querySelector('#notes');
|
||||||
|
if (notesPane) {
|
||||||
|
notesPane.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (notesTab) {
|
||||||
|
notesTab.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
if (bp.getBreakpointSize() !== 'xs') {
|
if (bp.getBreakpointSize() !== 'xs') {
|
||||||
this.expandView();
|
this.expandView();
|
||||||
|
@ -221,6 +250,8 @@ export default class MergeRequestTabs {
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.detail-page-description').renderGFM();
|
$('.detail-page-description').renderGFM();
|
||||||
|
|
||||||
|
this.recallScroll(action);
|
||||||
} else if (action === this.currentAction) {
|
} else if (action === this.currentAction) {
|
||||||
// ContentTop is used to handle anything at the top of the page before the main content
|
// ContentTop is used to handle anything at the top of the page before the main content
|
||||||
const mainContentContainer = document.querySelector('.content-wrapper');
|
const mainContentContainer = document.querySelector('.content-wrapper');
|
||||||
|
|
|
@ -111,8 +111,11 @@ class GroupPolicy < BasePolicy
|
||||||
enable :read_issue_board
|
enable :read_issue_board
|
||||||
enable :read_group_member
|
enable :read_group_member
|
||||||
enable :read_custom_emoji
|
enable :read_custom_emoji
|
||||||
|
enable :read_counts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
rule { ~public_group & ~has_access }.prevent :read_counts
|
||||||
|
|
||||||
rule { ~can?(:read_group) }.policy do
|
rule { ~can?(:read_group) }.policy do
|
||||||
prevent :read_design_activity
|
prevent :read_design_activity
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,9 +37,13 @@ class GroupChildEntity < Grape::Entity
|
||||||
if: lambda { |_instance, _options| project? }
|
if: lambda { |_instance, _options| project? }
|
||||||
|
|
||||||
# Group only attributes
|
# Group only attributes
|
||||||
expose :children_count, :parent_id, :project_count, :subgroup_count,
|
expose :children_count, :parent_id,
|
||||||
unless: lambda { |_instance, _options| project? }
|
unless: lambda { |_instance, _options| project? }
|
||||||
|
|
||||||
|
expose :subgroup_count, if: lambda { |group| access_group_counts?(group) }
|
||||||
|
|
||||||
|
expose :project_count, if: lambda { |group| access_group_counts?(group) }
|
||||||
|
|
||||||
expose :leave_path, unless: lambda { |_instance, _options| project? } do |instance|
|
expose :leave_path, unless: lambda { |_instance, _options| project? } do |instance|
|
||||||
leave_group_members_path(instance)
|
leave_group_members_path(instance)
|
||||||
end
|
end
|
||||||
|
@ -52,10 +56,6 @@ class GroupChildEntity < Grape::Entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :number_projects_with_delimiter, unless: lambda { |_instance, _options| project? } do |instance|
|
|
||||||
number_with_delimiter(instance.project_count)
|
|
||||||
end
|
|
||||||
|
|
||||||
expose :number_users_with_delimiter, unless: lambda { |_instance, _options| project? } do |instance|
|
expose :number_users_with_delimiter, unless: lambda { |_instance, _options| project? } do |instance|
|
||||||
number_with_delimiter(instance.member_count)
|
number_with_delimiter(instance.member_count)
|
||||||
end
|
end
|
||||||
|
@ -66,6 +66,10 @@ class GroupChildEntity < Grape::Entity
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def access_group_counts?(group)
|
||||||
|
!project? && can?(request.current_user, :read_counts, group)
|
||||||
|
end
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def membership
|
def membership
|
||||||
return unless request.current_user
|
return unless request.current_user
|
||||||
|
|
|
@ -40,10 +40,6 @@ class GroupEntity < Grape::Entity
|
||||||
GroupsFinder.new(request.current_user, parent: group).execute.any?
|
GroupsFinder.new(request.current_user, parent: group).execute.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :number_projects_with_delimiter do |group|
|
|
||||||
number_with_delimiter(GroupProjectsFinder.new(group: group, current_user: request.current_user).execute.count)
|
|
||||||
end
|
|
||||||
|
|
||||||
expose :number_users_with_delimiter do |group|
|
expose :number_users_with_delimiter do |group|
|
||||||
number_with_delimiter(group.users.count)
|
number_with_delimiter(group.users.count)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,15 +5,19 @@ module Packages
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def find_or_create_package!(package_type, name: params[:name], version: params[:version])
|
def find_or_create_package!(package_type, name: params[:name], version: params[:version])
|
||||||
|
# safe_find_or_create_by! was originally called here.
|
||||||
|
# We merely switched to `find_or_create_by!`
|
||||||
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
project
|
project
|
||||||
.packages
|
.packages
|
||||||
.with_package_type(package_type)
|
.with_package_type(package_type)
|
||||||
.safe_find_or_create_by!(name: name, version: version) do |package|
|
.find_or_create_by!(name: name, version: version) do |package|
|
||||||
package.status = params[:status] if params[:status]
|
package.status = params[:status] if params[:status]
|
||||||
package.creator = package_creator
|
package.creator = package_creator
|
||||||
|
|
||||||
add_build_info(package)
|
add_build_info(package)
|
||||||
end
|
end
|
||||||
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_package!(package_type, attrs = {})
|
def create_package!(package_type, attrs = {})
|
||||||
|
|
|
@ -23,8 +23,3 @@ running on.
|
||||||
|
|
||||||
[strace-parser](https://gitlab.com/wchandler/strace-parser) is a small tool to analyze
|
[strace-parser](https://gitlab.com/wchandler/strace-parser) is a small tool to analyze
|
||||||
and summarize raw `strace` data.
|
and summarize raw `strace` data.
|
||||||
|
|
||||||
## Pritaly
|
|
||||||
|
|
||||||
[Pritaly](https://gitlab.com/wchandler/pritaly) takes Gitaly logs and colorizes output
|
|
||||||
or converts the logs to JSON.
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ type: reference
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/50144) in GitLab 11.3.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/50144) in GitLab 11.3.
|
||||||
|
|
||||||
Interactive web terminals give the user access to a terminal in GitLab for
|
Interactive web terminals give the user access to a terminal in GitLab for
|
||||||
running one-off commands for their CI pipeline, enabling debugging with SSH. Since this is giving the user
|
running one-off commands for their CI pipeline. You can think of it like a method for
|
||||||
|
debugging with SSH, but done directly from the job page. Since this is giving the user
|
||||||
shell access to the environment where [GitLab Runner](https://docs.gitlab.com/runner/)
|
shell access to the environment where [GitLab Runner](https://docs.gitlab.com/runner/)
|
||||||
is deployed, some [security precautions](../../administration/integration/terminal.md#security) were
|
is deployed, some [security precautions](../../administration/integration/terminal.md#security) were
|
||||||
taken to protect the users.
|
taken to protect the users.
|
||||||
|
|
|
@ -99,7 +99,8 @@ are very appreciative of the work done by translators and proofreaders!
|
||||||
- André Gama - [GitLab](https://gitlab.com/andregamma), [Crowdin](https://crowdin.com/profile/ToeOficial)
|
- André Gama - [GitLab](https://gitlab.com/andregamma), [Crowdin](https://crowdin.com/profile/ToeOficial)
|
||||||
- Eduardo Addad de Oliveira - [GitLab](https://gitlab.com/eduardoaddad), [Crowdin](https://crowdin.com/profile/eduardoaddad)
|
- Eduardo Addad de Oliveira - [GitLab](https://gitlab.com/eduardoaddad), [Crowdin](https://crowdin.com/profile/eduardoaddad)
|
||||||
- Romanian
|
- Romanian
|
||||||
- Proofreaders needed.
|
- Mircea Pop - [GitLab](https://gitlab.com/eeex)[Crowdin](https://crowdin.com/profile/eex)
|
||||||
|
- Rareș Pița - [GitLab](https://gitlab.com/dlphin)[Crowdin](https://crowdin.com/profile/dlphin)
|
||||||
- Russian
|
- Russian
|
||||||
- Nikita Grylov - [GitLab](https://gitlab.com/nixel2007), [Crowdin](https://crowdin.com/profile/nixel2007)
|
- Nikita Grylov - [GitLab](https://gitlab.com/nixel2007), [Crowdin](https://crowdin.com/profile/nixel2007)
|
||||||
- Alexy Lustin - [GitLab](https://gitlab.com/allustin), [Crowdin](https://crowdin.com/profile/lustin)
|
- Alexy Lustin - [GitLab](https://gitlab.com/allustin), [Crowdin](https://crowdin.com/profile/lustin)
|
||||||
|
|
|
@ -60,6 +60,8 @@ Redis version 6.0 or higher is recommended, as this is what ships with
|
||||||
|
|
||||||
The necessary hard drive space largely depends on the size of the repositories you want to store in GitLab but as a *rule of thumb* you should have at least as much free space as all your repositories combined take up.
|
The necessary hard drive space largely depends on the size of the repositories you want to store in GitLab but as a *rule of thumb* you should have at least as much free space as all your repositories combined take up.
|
||||||
|
|
||||||
|
The Omnibus GitLab package requires about 2.5 GB of storage space for installation.
|
||||||
|
|
||||||
If you want to be flexible about growing your hard drive space in the future consider mounting it using [logical volume management (LVM)](https://en.wikipedia.org/wiki/Logical_volume_management) so you can add more hard drives when you need them.
|
If you want to be flexible about growing your hard drive space in the future consider mounting it using [logical volume management (LVM)](https://en.wikipedia.org/wiki/Logical_volume_management) so you can add more hard drives when you need them.
|
||||||
|
|
||||||
Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume.
|
Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume.
|
||||||
|
|
|
@ -4,7 +4,7 @@ group: Integrations
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||||
---
|
---
|
||||||
|
|
||||||
# Configure the Jira integration in GitLab
|
# Configure the Jira integration in GitLab **(FREE)**
|
||||||
|
|
||||||
You can set up the [Jira integration](index.md#jira-integration)
|
You can set up the [Jira integration](index.md#jira-integration)
|
||||||
by configuring your project settings in GitLab.
|
by configuring your project settings in GitLab.
|
||||||
|
|
|
@ -108,3 +108,68 @@ clicking the **Resolve** button near the top of the page.
|
||||||
Marking an error as resolved indicates that the error has stopped firing events. If a GitLab issue is linked to the error, then the issue closes.
|
Marking an error as resolved indicates that the error has stopped firing events. If a GitLab issue is linked to the error, then the issue closes.
|
||||||
|
|
||||||
If another event occurs, the error reverts to unresolved.
|
If another event occurs, the error reverts to unresolved.
|
||||||
|
|
||||||
|
## Integrated error tracking
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329596) in GitLab 14.3.
|
||||||
|
|
||||||
|
Integrated error tracking is a lightweight alternative to Sentry backend.
|
||||||
|
You still use Sentry SDK with your application. But you don't need to deploy Sentry
|
||||||
|
or set up for cloud-hosted Sentry. Instead, you use GitLab as a backend for it.
|
||||||
|
|
||||||
|
Sentry backend automatically assigns a Data Source Name (DSN) for every project you create.
|
||||||
|
GitLab does the same. You should be able to find a DSN for your project in the GitLab error tracking
|
||||||
|
settings. By using a GitLab-provided DSN, your application connects to GitLab to report an error.
|
||||||
|
Those errors are stored in the GitLab database and rendered by the GitLab UI, in the same way as
|
||||||
|
Sentry integration.
|
||||||
|
|
||||||
|
### Feature flag
|
||||||
|
|
||||||
|
The integrated error tracking feature is behind a feature flag that is disabled by default.
|
||||||
|
|
||||||
|
To enable it:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Feature.enable(:integrated_error_tracking)
|
||||||
|
```
|
||||||
|
|
||||||
|
To disable it:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Feature.disable(:integrated_error_tracking)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Project settings
|
||||||
|
|
||||||
|
The feature should be enabled on the project level. However, there is no UI to enable this feature yet.
|
||||||
|
You must use the GitLab API to enable it.
|
||||||
|
|
||||||
|
#### How to enable
|
||||||
|
|
||||||
|
1. Enable the `integrated` error tracking setting for your project:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --request PATCH --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||||
|
"https://gitlab.example.com/api/v4/projects/PROJECT_ID/error_tracking/settings?active=true&integrated=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Create a client key (DSN) to use with Sentry SDK in your application. Make sure to save the
|
||||||
|
response, as it contains a DSN:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||||
|
"https://gitlab.example.com/api/v4/projects/PROJECT_ID/error_tracking/client_keys"
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Take the DSN from the previous step and configure your Sentry SDK with it. Errors are now
|
||||||
|
reported to the GitLab collector and are visible in the [GitLab UI](#error-tracking-list).
|
||||||
|
|
||||||
|
#### How to disable
|
||||||
|
|
||||||
|
To disable the feature, run this command. This is the same command as the one that enables the
|
||||||
|
feature, but with a `false` value instead:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --request PATCH --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||||
|
"https://gitlab.example.com/api/v4/projects/PROJECT_ID/error_tracking/settings?active=false&integrated=false"
|
||||||
|
```
|
||||||
|
|
|
@ -107,11 +107,11 @@ Download and install Go (for Linux, 64-bit):
|
||||||
# Remove former Go installation folder
|
# Remove former Go installation folder
|
||||||
sudo rm -rf /usr/local/go
|
sudo rm -rf /usr/local/go
|
||||||
|
|
||||||
curl --remote-name --progress "https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
|
curl --remote-name --progress-bar "https://dl.google.com/go/go1.15.12.linux-amd64.tar.gz"
|
||||||
echo '512103d7ad296467814a6e3f635631bd35574cab3369a97a323c9a585ccaa569 go1.13.5.linux-amd64.tar.gz' | shasum -a256 -c - && \
|
echo 'bbdb935699e0b24d90e2451346da76121b2412d30930eabcd80907c230d098b7 go1.15.12.linux-amd64.tar.gz' | shasum -a256 -c - && \
|
||||||
sudo tar -C /usr/local -xzf go1.13.5.linux-amd64.tar.gz
|
sudo tar -C /usr/local -xzf go1.15.12.linux-amd64.tar.gz
|
||||||
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
|
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
|
||||||
rm go1.13.5.linux-amd64.tar.gz
|
rm go1.15.12.linux-amd64.tar.gz
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,11 @@ When a fast-forward merge is not possible, the user is given the option to rebas
|
||||||
|
|
||||||
## Enabling fast-forward merges
|
## Enabling fast-forward merges
|
||||||
|
|
||||||
1. Navigate to your project's **Settings** and search for the 'Merge method'
|
1. On the top bar, select **Menu > Projects** and find your project.
|
||||||
1. Select the **Fast-forward merge** option
|
1. On the left sidebar, select **Settings > General**.
|
||||||
1. Hit **Save changes** for the changes to take effect
|
1. Expand **Merge requests**.
|
||||||
|
1. In the **Merge method** section, select **Fast-forward merge**.
|
||||||
|
1. Select **Save changes**.
|
||||||
|
|
||||||
Now, when you visit the merge request page, you can accept it
|
Now, when you visit the merge request page, you can accept it
|
||||||
**only if a fast-forward merge is possible**.
|
**only if a fast-forward merge is possible**.
|
||||||
|
|
|
@ -21,6 +21,8 @@ module Gitlab
|
||||||
gon.sentry_environment = Gitlab.config.sentry.environment
|
gon.sentry_environment = Gitlab.config.sentry.environment
|
||||||
end
|
end
|
||||||
|
|
||||||
|
gon.recaptcha_api_server_url = ::Recaptcha.configuration.api_server_url
|
||||||
|
gon.recaptcha_sitekey = Gitlab::CurrentSettings.recaptcha_site_key
|
||||||
gon.gitlab_url = Gitlab.config.gitlab.url
|
gon.gitlab_url = Gitlab.config.gitlab.url
|
||||||
gon.revision = Gitlab.revision
|
gon.revision = Gitlab.revision
|
||||||
gon.feature_category = Gitlab::ApplicationContext.current_context_attribute(:feature_category).presence
|
gon.feature_category = Gitlab::ApplicationContext.current_context_attribute(:feature_category).presence
|
||||||
|
|
|
@ -29646,6 +29646,9 @@ msgstr ""
|
||||||
msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
|
msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityOrchestration|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|Network"
|
msgid "SecurityOrchestration|Network"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -29676,9 +29679,18 @@ msgstr ""
|
||||||
msgid "SecurityOrchestration|Select security project"
|
msgid "SecurityOrchestration|Select security project"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityOrchestration|Sorry, your filter produced no results."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|There was a problem creating the new security policy"
|
msgid "SecurityOrchestration|There was a problem creating the new security policy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityOrchestration|This project does not contain any security policies."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityOrchestration|To widen your search, change filters above or select a different security policy project."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityOrchestration|Update scan execution policies"
|
msgid "SecurityOrchestration|Update scan execution policies"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -227,8 +227,8 @@ RSpec.describe Groups::ChildrenController do
|
||||||
|
|
||||||
context 'when rendering hierarchies' do
|
context 'when rendering hierarchies' do
|
||||||
# When loading hierarchies we load the all the ancestors for matched projects
|
# When loading hierarchies we load the all the ancestors for matched projects
|
||||||
# in 1 separate query
|
# in 2 separate queries
|
||||||
let(:extra_queries_for_hierarchies) { 1 }
|
let(:extra_queries_for_hierarchies) { 2 }
|
||||||
|
|
||||||
def get_filtered_list
|
def get_filtered_list
|
||||||
get :index, params: { group_id: group.to_param, filter: 'filter' }, format: :json
|
get :index, params: { group_id: group.to_param, filter: 'filter' }, format: :json
|
||||||
|
|
|
@ -90,9 +90,10 @@ RSpec.describe 'Group Packages & Registries settings' do
|
||||||
expect(page).to have_content('Do not allow duplicates')
|
expect(page).to have_content('Do not allow duplicates')
|
||||||
|
|
||||||
fill_in 'Exceptions', with: ')'
|
fill_in 'Exceptions', with: ')'
|
||||||
|
|
||||||
|
# simulate blur event
|
||||||
|
find('#maven-duplicated-settings-regex-input').native.send_keys(:tab)
|
||||||
end
|
end
|
||||||
# simulate blur event
|
|
||||||
find('body').click
|
|
||||||
|
|
||||||
expect(page).to have_content('is an invalid regexp')
|
expect(page).to have_content('is an invalid regexp')
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||||
import ItemStats from '~/groups/components/item_stats.vue';
|
import ItemStats from '~/groups/components/item_stats.vue';
|
||||||
import ItemStatsValue from '~/groups/components/item_stats_value.vue';
|
import ItemStatsValue from '~/groups/components/item_stats_value.vue';
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ describe('ItemStats', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const createComponent = (props = {}) => {
|
const createComponent = (props = {}) => {
|
||||||
wrapper = shallowMount(ItemStats, {
|
wrapper = shallowMountExtended(ItemStats, {
|
||||||
propsData: { ...defaultProps, ...props },
|
propsData: { ...defaultProps, ...props },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -46,5 +46,31 @@ describe('ItemStats', () => {
|
||||||
expect(findItemStatsValue().props('cssClass')).toBe('project-stars');
|
expect(findItemStatsValue().props('cssClass')).toBe('project-stars');
|
||||||
expect(wrapper.find('.last-updated').exists()).toBe(true);
|
expect(wrapper.find('.last-updated').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('group specific rendering', () => {
|
||||||
|
describe.each`
|
||||||
|
provided | state | data
|
||||||
|
${true} | ${'displays'} | ${null}
|
||||||
|
${false} | ${'does not display'} | ${{ subgroupCount: undefined, projectCount: undefined }}
|
||||||
|
`('when provided = $provided', ({ provided, state, data }) => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const item = {
|
||||||
|
...mockParentGroupItem,
|
||||||
|
...data,
|
||||||
|
type: ITEM_TYPE.GROUP,
|
||||||
|
};
|
||||||
|
|
||||||
|
createComponent({ item });
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each`
|
||||||
|
entity | testId
|
||||||
|
${'subgroups'} | ${'subgroups-count'}
|
||||||
|
${'projects'} | ${'projects-count'}
|
||||||
|
`(`${state} $entity count`, ({ testId }) => {
|
||||||
|
expect(wrapper.findByTestId(testId).exists()).toBe(provided);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,6 +34,44 @@ describe('MergeRequestTabs', () => {
|
||||||
gl.mrWidget = {};
|
gl.mrWidget = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('clickTab', () => {
|
||||||
|
let params;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
document.documentElement.scrollTop = 100;
|
||||||
|
|
||||||
|
params = {
|
||||||
|
metaKey: false,
|
||||||
|
ctrlKey: false,
|
||||||
|
which: 1,
|
||||||
|
stopImmediatePropagation() {},
|
||||||
|
preventDefault() {},
|
||||||
|
currentTarget: {
|
||||||
|
getAttribute(attr) {
|
||||||
|
return attr === 'href' ? 'a/tab/url' : null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stores the current scroll position if there's an active tab", () => {
|
||||||
|
testContext.class.currentTab = 'someTab';
|
||||||
|
|
||||||
|
testContext.class.clickTab(params);
|
||||||
|
|
||||||
|
expect(testContext.class.scrollPositions.someTab).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't store a scroll position if there's no active tab", () => {
|
||||||
|
// this happens on first load, and we just don't want to store empty values in the `null` property
|
||||||
|
testContext.class.currentTab = null;
|
||||||
|
|
||||||
|
testContext.class.clickTab(params);
|
||||||
|
|
||||||
|
expect(testContext.class.scrollPositions).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('opensInNewTab', () => {
|
describe('opensInNewTab', () => {
|
||||||
const windowTarget = '_blank';
|
const windowTarget = '_blank';
|
||||||
let clickTabParams;
|
let clickTabParams;
|
||||||
|
@ -258,6 +296,7 @@ describe('MergeRequestTabs', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.spyOn(mainContent, 'getBoundingClientRect').mockReturnValue({ top: 10 });
|
jest.spyOn(mainContent, 'getBoundingClientRect').mockReturnValue({ top: 10 });
|
||||||
jest.spyOn(tabContent, 'getBoundingClientRect').mockReturnValue({ top: 100 });
|
jest.spyOn(tabContent, 'getBoundingClientRect').mockReturnValue({ top: 100 });
|
||||||
|
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
||||||
jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
|
jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
|
||||||
return selector === '.content-wrapper' ? mainContent : tabContent;
|
return selector === '.content-wrapper' ? mainContent : tabContent;
|
||||||
});
|
});
|
||||||
|
@ -267,8 +306,6 @@ describe('MergeRequestTabs', () => {
|
||||||
it('calls window scrollTo with options if document has scrollBehavior', () => {
|
it('calls window scrollTo with options if document has scrollBehavior', () => {
|
||||||
document.documentElement.style.scrollBehavior = '';
|
document.documentElement.style.scrollBehavior = '';
|
||||||
|
|
||||||
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
|
||||||
|
|
||||||
testContext.class.tabShown('commits', 'foobar');
|
testContext.class.tabShown('commits', 'foobar');
|
||||||
|
|
||||||
expect(window.scrollTo.mock.calls[0][0]).toEqual({ top: 39, behavior: 'smooth' });
|
expect(window.scrollTo.mock.calls[0][0]).toEqual({ top: 39, behavior: 'smooth' });
|
||||||
|
@ -276,11 +313,50 @@ describe('MergeRequestTabs', () => {
|
||||||
|
|
||||||
it('calls window scrollTo with two args if document does not have scrollBehavior', () => {
|
it('calls window scrollTo with two args if document does not have scrollBehavior', () => {
|
||||||
jest.spyOn(document.documentElement, 'style', 'get').mockReturnValue({});
|
jest.spyOn(document.documentElement, 'style', 'get').mockReturnValue({});
|
||||||
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
|
||||||
|
|
||||||
testContext.class.tabShown('commits', 'foobar');
|
testContext.class.tabShown('commits', 'foobar');
|
||||||
|
|
||||||
expect(window.scrollTo.mock.calls[0]).toEqual([0, 39]);
|
expect(window.scrollTo.mock.calls[0]).toEqual([0, 39]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when switching tabs', () => {
|
||||||
|
const SCROLL_TOP = 100;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
||||||
|
testContext.class.mergeRequestTabs = document.createElement('div');
|
||||||
|
testContext.class.mergeRequestTabPanes = document.createElement('div');
|
||||||
|
testContext.class.currentTab = 'tab';
|
||||||
|
testContext.class.scrollPositions = { newTab: SCROLL_TOP };
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls to the stored position, if one is stored', () => {
|
||||||
|
testContext.class.tabShown('newTab');
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(250);
|
||||||
|
|
||||||
|
expect(window.scrollTo.mock.calls[0][0]).toEqual({
|
||||||
|
top: SCROLL_TOP,
|
||||||
|
left: 0,
|
||||||
|
behavior: 'auto',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls to 0, if no position is stored', () => {
|
||||||
|
testContext.class.tabShown('unknownTab');
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(250);
|
||||||
|
|
||||||
|
expect(window.scrollTo.mock.calls[0][0]).toEqual({ top: 0, left: 0, behavior: 'auto' });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,7 @@ RSpec.describe GroupPolicy do
|
||||||
|
|
||||||
it do
|
it do
|
||||||
expect_allowed(:read_group)
|
expect_allowed(:read_group)
|
||||||
|
expect_allowed(:read_counts)
|
||||||
expect_allowed(*read_group_permissions)
|
expect_allowed(*read_group_permissions)
|
||||||
expect_disallowed(:upload_file)
|
expect_disallowed(:upload_file)
|
||||||
expect_disallowed(*reporter_permissions)
|
expect_disallowed(*reporter_permissions)
|
||||||
|
@ -30,6 +31,7 @@ RSpec.describe GroupPolicy do
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect_disallowed(:read_group) }
|
it { expect_disallowed(:read_group) }
|
||||||
|
it { expect_disallowed(:read_counts) }
|
||||||
it { expect_disallowed(*read_group_permissions) }
|
it { expect_disallowed(*read_group_permissions) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -42,6 +44,7 @@ RSpec.describe GroupPolicy do
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect_disallowed(:read_group) }
|
it { expect_disallowed(:read_group) }
|
||||||
|
it { expect_disallowed(:read_counts) }
|
||||||
it { expect_disallowed(*read_group_permissions) }
|
it { expect_disallowed(*read_group_permissions) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,6 +248,7 @@ RSpec.describe GroupPolicy do
|
||||||
let(:current_user) { nil }
|
let(:current_user) { nil }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
expect_disallowed(:read_counts)
|
||||||
expect_disallowed(*read_group_permissions)
|
expect_disallowed(*read_group_permissions)
|
||||||
expect_disallowed(*guest_permissions)
|
expect_disallowed(*guest_permissions)
|
||||||
expect_disallowed(*reporter_permissions)
|
expect_disallowed(*reporter_permissions)
|
||||||
|
@ -258,6 +262,7 @@ RSpec.describe GroupPolicy do
|
||||||
let(:current_user) { guest }
|
let(:current_user) { guest }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
expect_allowed(:read_counts)
|
||||||
expect_allowed(*read_group_permissions)
|
expect_allowed(*read_group_permissions)
|
||||||
expect_allowed(*guest_permissions)
|
expect_allowed(*guest_permissions)
|
||||||
expect_disallowed(*reporter_permissions)
|
expect_disallowed(*reporter_permissions)
|
||||||
|
@ -271,6 +276,7 @@ RSpec.describe GroupPolicy do
|
||||||
let(:current_user) { reporter }
|
let(:current_user) { reporter }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
expect_allowed(:read_counts)
|
||||||
expect_allowed(*read_group_permissions)
|
expect_allowed(*read_group_permissions)
|
||||||
expect_allowed(*guest_permissions)
|
expect_allowed(*guest_permissions)
|
||||||
expect_allowed(*reporter_permissions)
|
expect_allowed(*reporter_permissions)
|
||||||
|
@ -284,6 +290,7 @@ RSpec.describe GroupPolicy do
|
||||||
let(:current_user) { developer }
|
let(:current_user) { developer }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
expect_allowed(:read_counts)
|
||||||
expect_allowed(*read_group_permissions)
|
expect_allowed(*read_group_permissions)
|
||||||
expect_allowed(*guest_permissions)
|
expect_allowed(*guest_permissions)
|
||||||
expect_allowed(*reporter_permissions)
|
expect_allowed(*reporter_permissions)
|
||||||
|
@ -297,6 +304,7 @@ RSpec.describe GroupPolicy do
|
||||||
let(:current_user) { maintainer }
|
let(:current_user) { maintainer }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
expect_allowed(:read_counts)
|
||||||
expect_allowed(*read_group_permissions)
|
expect_allowed(*read_group_permissions)
|
||||||
expect_allowed(*guest_permissions)
|
expect_allowed(*guest_permissions)
|
||||||
expect_allowed(*reporter_permissions)
|
expect_allowed(*reporter_permissions)
|
||||||
|
@ -310,6 +318,7 @@ RSpec.describe GroupPolicy do
|
||||||
let(:current_user) { owner }
|
let(:current_user) { owner }
|
||||||
|
|
||||||
it do
|
it do
|
||||||
|
expect_allowed(:read_counts)
|
||||||
expect_allowed(*read_group_permissions)
|
expect_allowed(*read_group_permissions)
|
||||||
expect_allowed(*guest_permissions)
|
expect_allowed(*guest_permissions)
|
||||||
expect_allowed(*reporter_permissions)
|
expect_allowed(*reporter_permissions)
|
||||||
|
|
|
@ -87,7 +87,7 @@ RSpec.describe GroupChildEntity do
|
||||||
expect(json[:children_count]).to eq(2)
|
expect(json[:children_count]).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
%w[children_count leave_path parent_id number_projects_with_delimiter number_users_with_delimiter project_count subgroup_count].each do |attribute|
|
%w[children_count leave_path parent_id number_users_with_delimiter project_count subgroup_count].each do |attribute|
|
||||||
it "includes #{attribute}" do
|
it "includes #{attribute}" do
|
||||||
expect(json[attribute.to_sym]).to be_present
|
expect(json[attribute.to_sym]).to be_present
|
||||||
end
|
end
|
||||||
|
@ -114,6 +114,40 @@ RSpec.describe GroupChildEntity do
|
||||||
it_behaves_like 'group child json'
|
it_behaves_like 'group child json'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'for a private group' do
|
||||||
|
let(:object) do
|
||||||
|
create(:group, :private)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'user is member of the group' do
|
||||||
|
before do
|
||||||
|
object.add_owner(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes the counts' do
|
||||||
|
expect(json.keys).to include(*%i(project_count subgroup_count))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'user is not a member of the group' do
|
||||||
|
it 'does not include the counts' do
|
||||||
|
expect(json.keys).not_to include(*%i(project_count subgroup_count))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'user is only a member of a project in the group' do
|
||||||
|
let(:project) { create(:project, namespace: object) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_guest(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not include the counts' do
|
||||||
|
expect(json.keys).not_to include(*%i(project_count subgroup_count))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'for a project with external authorization enabled' do
|
describe 'for a project with external authorization enabled' do
|
||||||
let(:object) do
|
let(:object) do
|
||||||
create(:project, :with_avatar,
|
create(:project, :with_avatar,
|
||||||
|
|
Loading…
Reference in New Issue