Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b487021bd3
commit
20dbd96076
|
|
@ -33,3 +33,4 @@ export const EXTENSION_MARKDOWN_PREVIEW_PANEL_CLASS = 'md';
|
|||
export const EXTENSION_MARKDOWN_PREVIEW_PANEL_PARENT_CLASS = 'source-editor-preview';
|
||||
export const EXTENSION_MARKDOWN_PREVIEW_ACTION_ID = 'markdown-preview';
|
||||
export const EXTENSION_MARKDOWN_PREVIEW_PANEL_WIDTH = 0.5; // 50% of the width
|
||||
export const EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY = 250; // ms
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
EXTENSION_MARKDOWN_PREVIEW_ACTION_ID,
|
||||
EXTENSION_MARKDOWN_PREVIEW_PANEL_WIDTH,
|
||||
EXTENSION_MARKDOWN_PREVIEW_PANEL_PARENT_CLASS,
|
||||
EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY,
|
||||
} from '../constants';
|
||||
import { SourceEditorExtension } from './source_editor_extension_base';
|
||||
|
||||
|
|
@ -50,9 +51,29 @@ export class EditorMarkdownExtension extends SourceEditorExtension {
|
|||
el: undefined,
|
||||
action: undefined,
|
||||
shown: false,
|
||||
modelChangeListener: undefined,
|
||||
},
|
||||
});
|
||||
this.setupPreviewAction.call(instance);
|
||||
|
||||
instance.getModel().onDidChangeLanguage(({ newLanguage, oldLanguage } = {}) => {
|
||||
if (newLanguage === 'markdown' && oldLanguage !== newLanguage) {
|
||||
instance.setupPreviewAction();
|
||||
} else {
|
||||
instance.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
instance.onDidChangeModel(() => {
|
||||
const model = instance.getModel();
|
||||
if (model) {
|
||||
const { language } = model.getLanguageIdentifier();
|
||||
instance.cleanup();
|
||||
if (language === 'markdown') {
|
||||
instance.setupPreviewAction();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static togglePreviewLayout() {
|
||||
|
|
@ -78,6 +99,9 @@ export class EditorMarkdownExtension extends SourceEditorExtension {
|
|||
}
|
||||
|
||||
cleanup() {
|
||||
if (this.preview.modelChangeListener) {
|
||||
this.preview.modelChangeListener.dispose();
|
||||
}
|
||||
this.preview.action.dispose();
|
||||
if (this.preview.shown) {
|
||||
EditorMarkdownExtension.togglePreviewPanel.call(this);
|
||||
|
|
@ -126,22 +150,14 @@ export class EditorMarkdownExtension extends SourceEditorExtension {
|
|||
EditorMarkdownExtension.togglePreviewPanel.call(this);
|
||||
|
||||
if (!this.preview?.shown) {
|
||||
this.modelChangeListener = this.onDidChangeModelContent(
|
||||
debounce(this.fetchPreview.bind(this), 250),
|
||||
this.preview.modelChangeListener = this.onDidChangeModelContent(
|
||||
debounce(this.fetchPreview.bind(this), EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY),
|
||||
);
|
||||
} else {
|
||||
this.modelChangeListener.dispose();
|
||||
this.preview.modelChangeListener.dispose();
|
||||
}
|
||||
|
||||
this.preview.shown = !this.preview?.shown;
|
||||
|
||||
this.getModel().onDidChangeLanguage(({ newLanguage, oldLanguage } = {}) => {
|
||||
if (newLanguage === 'markdown' && oldLanguage !== newLanguage) {
|
||||
this.setupPreviewAction();
|
||||
} else {
|
||||
this.cleanup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getSelectedText(selection = this.getSelection()) {
|
||||
|
|
|
|||
|
|
@ -16,12 +16,18 @@ export const setupEditorTheme = () => {
|
|||
monacoEditor.setTheme(theme ? themeName : DEFAULT_THEME);
|
||||
};
|
||||
|
||||
export const getBlobLanguage = (path) => {
|
||||
const ext = `.${path.split('.').pop()}`;
|
||||
export const getBlobLanguage = (blobPath) => {
|
||||
const defaultLanguage = 'plaintext';
|
||||
|
||||
if (!blobPath) {
|
||||
return defaultLanguage;
|
||||
}
|
||||
|
||||
const ext = `.${blobPath.split('.').pop()}`;
|
||||
const language = monacoLanguages
|
||||
.getLanguages()
|
||||
.find((lang) => lang.extensions.indexOf(ext) !== -1);
|
||||
return language ? language.id : 'plaintext';
|
||||
return language ? language.id : defaultLanguage;
|
||||
};
|
||||
|
||||
export const setupCodeSnippet = (el) => {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ import { getPathParent, readFileAsDataURL, registerSchema, isTextFile } from '..
|
|||
import FileAlert from './file_alert.vue';
|
||||
import FileTemplatesBar from './file_templates/bar.vue';
|
||||
|
||||
const MARKDOWN_FILE_TYPE = 'markdown';
|
||||
|
||||
export default {
|
||||
name: 'RepoEditor',
|
||||
components: {
|
||||
|
|
@ -201,7 +203,7 @@ export default {
|
|||
showContentViewer(val) {
|
||||
if (!val) return;
|
||||
|
||||
if (this.fileType === 'markdown') {
|
||||
if (this.fileType === MARKDOWN_FILE_TYPE) {
|
||||
const { content, images } = extractMarkdownImagesFromEntries(this.file, this.entries);
|
||||
this.content = content;
|
||||
this.images = images;
|
||||
|
|
@ -309,6 +311,23 @@ export default {
|
|||
}),
|
||||
);
|
||||
|
||||
if (this.fileType === MARKDOWN_FILE_TYPE) {
|
||||
import('~/editor/extensions/source_editor_markdown_ext')
|
||||
.then(({ EditorMarkdownExtension: MarkdownExtension } = {}) => {
|
||||
this.editor.use(
|
||||
new MarkdownExtension({
|
||||
instance: this.editor,
|
||||
projectPath: this.currentProjectId,
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catch((e) =>
|
||||
createFlash({
|
||||
message: e,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.setupEditor();
|
||||
});
|
||||
|
|
@ -406,7 +425,11 @@ export default {
|
|||
const reImage = /^image\/(png|jpg|jpeg|gif)$/;
|
||||
const file = event.clipboardData.files[0];
|
||||
|
||||
if (editor.hasTextFocus() && this.fileType === 'markdown' && reImage.test(file?.type)) {
|
||||
if (
|
||||
editor.hasTextFocus() &&
|
||||
this.fileType === MARKDOWN_FILE_TYPE &&
|
||||
reImage.test(file?.type)
|
||||
) {
|
||||
// don't let the event be passed on to Monaco.
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
|
|
|||
|
|
@ -151,11 +151,24 @@ export const isMetaKey = (e) => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey
|
|||
// 3) Middle-click or Mouse Wheel Click (e.which is 2)
|
||||
export const isMetaClick = (e) => e.metaKey || e.ctrlKey || e.which === 2;
|
||||
|
||||
/**
|
||||
* Get the current computed outer height for given selector.
|
||||
*/
|
||||
export const getOuterHeight = (selector) => {
|
||||
const element = document.querySelector(selector);
|
||||
|
||||
if (!element) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return element.offsetHeight;
|
||||
};
|
||||
|
||||
export const contentTop = () => {
|
||||
const isDesktop = breakpointInstance.isDesktop();
|
||||
const heightCalculators = [
|
||||
() => $('#js-peek').outerHeight(),
|
||||
() => $('.navbar-gitlab').outerHeight(),
|
||||
() => getOuterHeight('#js-peek'),
|
||||
() => getOuterHeight('.navbar-gitlab'),
|
||||
({ desktop }) => {
|
||||
const container = document.querySelector('.line-resolve-all-container');
|
||||
let size = 0;
|
||||
|
|
@ -166,14 +179,14 @@ export const contentTop = () => {
|
|||
|
||||
return size;
|
||||
},
|
||||
() => $('.merge-request-tabs').outerHeight(),
|
||||
() => $('.js-diff-files-changed').outerHeight(),
|
||||
() => getOuterHeight('.merge-request-tabs'),
|
||||
() => getOuterHeight('.js-diff-files-changed'),
|
||||
({ desktop }) => {
|
||||
const diffsTabIsActive = window.mrTabs?.currentAction === 'diffs';
|
||||
let size;
|
||||
|
||||
if (desktop && diffsTabIsActive) {
|
||||
size = $('.diff-file .file-title-flex-parent:visible').outerHeight();
|
||||
size = getOuterHeight('.diff-file .file-title-flex-parent:not([style="display:none"])');
|
||||
}
|
||||
|
||||
return size;
|
||||
|
|
@ -182,7 +195,7 @@ export const contentTop = () => {
|
|||
let size;
|
||||
|
||||
if (desktop) {
|
||||
size = $('.mr-version-controls').outerHeight();
|
||||
size = getOuterHeight('.mr-version-controls');
|
||||
}
|
||||
|
||||
return size;
|
||||
|
|
|
|||
|
|
@ -87,21 +87,22 @@ export default {
|
|||
@click="handleRetryClick"
|
||||
/>
|
||||
|
||||
<gl-button
|
||||
v-if="pipeline.flags.cancelable"
|
||||
v-gl-tooltip.hover
|
||||
v-gl-modal-directive="'confirmation-modal'"
|
||||
:aria-label="$options.i18n.cancelTitle"
|
||||
:title="$options.i18n.cancelTitle"
|
||||
:loading="isCancelling"
|
||||
:disabled="isCancelling"
|
||||
icon="cancel"
|
||||
variant="danger"
|
||||
category="primary"
|
||||
class="js-pipelines-cancel-button gl-ml-1"
|
||||
@click="handleCancelClick"
|
||||
/>
|
||||
|
||||
<pipeline-multi-actions :pipeline-id="pipeline.id" />
|
||||
</div>
|
||||
<gl-button
|
||||
v-if="pipeline.flags.cancelable"
|
||||
v-gl-tooltip.hover
|
||||
v-gl-modal-directive="'confirmation-modal'"
|
||||
:aria-label="$options.i18n.cancelTitle"
|
||||
:title="$options.i18n.cancelTitle"
|
||||
:loading="isCancelling"
|
||||
:disabled="isCancelling"
|
||||
icon="cancel"
|
||||
variant="danger"
|
||||
category="primary"
|
||||
class="js-pipelines-cancel-button gl-ml-1"
|
||||
@click="handleCancelClick"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@
|
|||
@include gl-py-4;
|
||||
@include gl-w-full;
|
||||
}
|
||||
|
||||
.gl-source-editor {
|
||||
@include gl-order-n1;
|
||||
}
|
||||
}
|
||||
|
||||
.monaco-editor.gl-source-editor {
|
||||
|
|
|
|||
|
|
@ -1,27 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module GroupsHelper
|
||||
def group_settings_nav_link_paths
|
||||
%w[
|
||||
groups#projects
|
||||
groups#edit
|
||||
badges#index
|
||||
repository#show
|
||||
ci_cd#show
|
||||
integrations#index
|
||||
integrations#edit
|
||||
ldap_group_links#index
|
||||
hooks#index
|
||||
pipeline_quota#index
|
||||
applications#index
|
||||
applications#show
|
||||
applications#edit
|
||||
packages_and_registries#show
|
||||
groups/runners#show
|
||||
groups/runners#edit
|
||||
]
|
||||
end
|
||||
|
||||
def group_sidebar_links
|
||||
@group_sidebar_links ||= get_group_sidebar_links
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2039,6 +2039,7 @@ class Project < ApplicationRecord
|
|||
.append(key: 'CI_PROJECT_URL', value: web_url)
|
||||
.append(key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level))
|
||||
.append(key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: repository_languages.map(&:name).join(',').downcase)
|
||||
.append(key: 'CI_PROJECT_CLASSIFICATION_LABEL', value: external_authorization_classification_label)
|
||||
.append(key: 'CI_DEFAULT_BRANCH', value: default_branch)
|
||||
.append(key: 'CI_CONFIG_PATH', value: ci_config_path_or_default)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
- if @group.packages_feature_enabled?
|
||||
= nav_link(controller: :packages_and_registries) do
|
||||
= link_to group_settings_packages_and_registries_path(@group), title: _('Packages & Registries'), data: { qa_selector: 'group_package_settings_link' } do
|
||||
%span
|
||||
= _('Packages & Registries')
|
||||
|
|
@ -1,50 +1,3 @@
|
|||
- if group_sidebar_link?(:settings)
|
||||
= nav_link(path: group_settings_nav_link_paths) do
|
||||
= link_to edit_group_path(@group), class: 'has-sub-items' do
|
||||
.nav-icon-container
|
||||
= sprite_icon('settings')
|
||||
%span.nav-item-name{ data: { qa_selector: 'group_settings' } }
|
||||
= _('Settings')
|
||||
%ul.sidebar-sub-level-items{ data: { testid: 'group-settings-menu', qa_selector: 'group_sidebar_submenu' } }
|
||||
= nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show groups/applications#index], html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to edit_group_path(@group) do
|
||||
%strong.fly-out-top-item-name
|
||||
= _('Settings')
|
||||
%li.divider.fly-out-top-item
|
||||
= nav_link(path: 'groups#edit') do
|
||||
= link_to edit_group_path(@group), title: _('General'), data: { qa_selector: 'general_settings_link' } do
|
||||
%span
|
||||
= _('General')
|
||||
|
||||
= nav_link(controller: :integrations) do
|
||||
= link_to group_settings_integrations_path(@group), title: _('Integrations') do
|
||||
%span
|
||||
= _('Integrations')
|
||||
|
||||
= nav_link(path: 'groups#projects') do
|
||||
= link_to projects_group_path(@group), title: _('Projects') do
|
||||
%span
|
||||
= _('Projects')
|
||||
|
||||
= nav_link(controller: :repository) do
|
||||
= link_to group_settings_repository_path(@group), title: _('Repository') do
|
||||
%span
|
||||
= _('Repository')
|
||||
|
||||
= nav_link(path: ['groups/runners#show', 'groups/runners#edit'], controller: [:ci_cd]) do
|
||||
= link_to group_settings_ci_cd_path(@group), title: _('CI/CD') do
|
||||
%span
|
||||
= _('CI/CD')
|
||||
|
||||
= nav_link(controller: :applications) do
|
||||
= link_to group_settings_applications_path(@group), title: _('Applications') do
|
||||
%span
|
||||
= _('Applications')
|
||||
|
||||
= render 'groups/sidebar/packages_settings'
|
||||
|
||||
= render_if_exists "groups/ee/settings_nav"
|
||||
|
||||
= render_if_exists "groups/ee/administration_nav"
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ Rails.autoloaders.each do |autoloader|
|
|||
# We need to ignore these since these are non-Ruby files
|
||||
# that do not define Ruby classes / modules
|
||||
autoloader.ignore(Rails.root.join('lib/support'))
|
||||
autoloader.ignore(Rails.root.join('ee/lib/ee/gitlab/ci/parsers/security/validators/schemas')) if Gitlab.ee?
|
||||
|
||||
# Mailer previews are loaded manually by Rails
|
||||
# https://github.com/rails/rails/blob/v6.1.3.2/actionmailer/lib/action_mailer/preview.rb#L121-L125
|
||||
|
|
|
|||
|
|
@ -186,13 +186,13 @@ keys must be manually replicated to the **secondary** site.
|
|||
1. Edit `/etc/gitlab/gitlab.rb` and add a **unique** name for your site. You need this in the next steps:
|
||||
|
||||
```ruby
|
||||
# The unique identifier for the Geo site.
|
||||
##
|
||||
## The unique identifier for the Geo site. See
|
||||
## https://docs.gitlab.com/ee/user/admin_area/geo_nodes.html#common-settings
|
||||
##
|
||||
gitlab_rails['geo_node_name'] = '<site_name_here>'
|
||||
```
|
||||
|
||||
We recommend mentioning specific names for the `geo_node_name` such as `gitlab-usa` instead of generic names, such as `geo`.
|
||||
This makes the failover process easier because the physical location does not change, but the Geo site role can.
|
||||
|
||||
1. Reconfigure **each Rails and Sidekiq node on your secondary** site for the change to take effect:
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ as well.
|
|||
|
||||
NOTE:
|
||||
You can also use a load balancer to distribute web UI or API traffic to
|
||||
[multiple Geo **secondary** sites](../../../user/admin_area/geo_nodes.md#multiple-secondary-nodes-behind-a-load-balancer).
|
||||
[multiple Geo **secondary** sites](../../../user/admin_area/geo_nodes.md#multiple-secondary-sites-behind-a-load-balancer).
|
||||
Importantly, the **primary** site cannot yet be included. See the feature request
|
||||
[Support putting the **primary** behind a Geo node load balancer](https://gitlab.com/gitlab-org/gitlab/-/issues/10888).
|
||||
|
||||
|
|
|
|||
|
|
@ -66,12 +66,10 @@ The following steps enable a GitLab site to serve as the Geo **primary** site.
|
|||
|
||||
```ruby
|
||||
##
|
||||
## The unique identifier for the Geo site. It's recommended to use a
|
||||
## physical location as a name, for example "us-east", instead of
|
||||
## "secondary" or "geo". It's case-sensitive, and most characters are
|
||||
## allowed. It should be the same for all nodes in a Geo site.
|
||||
## The unique identifier for the Geo site. See
|
||||
## https://docs.gitlab.com/ee/user/admin_area/geo_nodes.html#common-settings
|
||||
##
|
||||
gitlab_rails['geo_node_name'] = '<node_name_here>'
|
||||
gitlab_rails['geo_node_name'] = '<site_name_here>'
|
||||
|
||||
##
|
||||
## Disable automatic migrations
|
||||
|
|
@ -210,12 +208,10 @@ then make the following modifications:
|
|||
geo_logcursor['enable'] = true
|
||||
|
||||
##
|
||||
## The unique identifier for the Geo site. It's recommended to use a
|
||||
## physical location as a name, for example "eu-west", instead of
|
||||
## "secondary" or "geo". It's case-sensitive, and most characters are
|
||||
## allowed. It should be the same for all nodes in a Geo site.
|
||||
## The unique identifier for the Geo site. See
|
||||
## https://docs.gitlab.com/ee/user/admin_area/geo_nodes.html#common-settings
|
||||
##
|
||||
gitlab_rails['geo_node_name'] = '<node_name_here>'
|
||||
gitlab_rails['geo_node_name'] = '<site_name_here>'
|
||||
|
||||
##
|
||||
## Disable automatic migrations
|
||||
|
|
@ -313,12 +309,10 @@ application nodes above, with some changes to run only the `sidekiq` service:
|
|||
gitlab_rails['enable'] = true
|
||||
|
||||
##
|
||||
## The unique identifier for the Geo site. It's recommended to use a
|
||||
## physical location as a name, for example "eu-west", instead of
|
||||
## "secondary" or "geo". It's case-sensitive, and most characters are
|
||||
## allowed. It should be the same for all nodes in a Geo site.
|
||||
## The unique identifier for the Geo site. See
|
||||
## https://docs.gitlab.com/ee/user/admin_area/geo_nodes.html#common-settings
|
||||
##
|
||||
gitlab_rails['geo_node_name'] = '<node_name_here>'
|
||||
gitlab_rails['geo_node_name'] = '<site_name_here>'
|
||||
|
||||
##
|
||||
## Disable automatic migrations
|
||||
|
|
|
|||
|
|
@ -69,11 +69,14 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
|
|||
sudo -i
|
||||
```
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` and add a **unique** name for your node:
|
||||
1. Edit `/etc/gitlab/gitlab.rb` and add a **unique** name for your site:
|
||||
|
||||
```ruby
|
||||
# The unique identifier for the Geo node.
|
||||
gitlab_rails['geo_node_name'] = '<node_name_here>'
|
||||
##
|
||||
## The unique identifier for the Geo site. See
|
||||
## https://docs.gitlab.com/ee/user/admin_area/geo_nodes.html#common-settings
|
||||
##
|
||||
gitlab_rails['geo_node_name'] = '<site_name_here>'
|
||||
```
|
||||
|
||||
1. Reconfigure the **primary** node for the change to take effect:
|
||||
|
|
|
|||
|
|
@ -34,9 +34,10 @@ developed and tested. We aim to be compatible with most external
|
|||
roles ['geo_primary_role']
|
||||
|
||||
##
|
||||
## The unique identifier for the Geo site.
|
||||
## The unique identifier for the Geo site. See
|
||||
## https://docs.gitlab.com/ee/user/admin_area/geo_nodes.html#common-settings
|
||||
##
|
||||
gitlab_rails['geo_node_name'] = '<geo_site_name_here>'
|
||||
gitlab_rails['geo_node_name'] = '<site_name_here>'
|
||||
```
|
||||
|
||||
1. Reconfigure the **primary** node for the change to take effect:
|
||||
|
|
|
|||
|
|
@ -954,7 +954,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/approve
|
|||
| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
|
||||
| `merge_request_iid` | integer | yes | The IID of the merge request |
|
||||
| `sha` | string | no | The `HEAD` of the merge request |
|
||||
| `approval_password` **(PREMIUM)** | string | no | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/approvals/settings.md#require-authentication-for-approvals) is enabled in the project settings. |
|
||||
| `approval_password` **(PREMIUM)** | string | no | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/approvals/settings.md#require-user-password-to-approve) is enabled in the project settings. |
|
||||
|
||||
The `sha` parameter works in the same way as
|
||||
when [accepting a merge request](merge_requests.md#accept-mr): if it is passed, then it must
|
||||
|
|
|
|||
|
|
@ -792,6 +792,8 @@ if [[ -d "/builds/gitlab-examples/ci-debug-trace/.git" ]]; then
|
|||
++ CI_PROJECT_VISIBILITY=public
|
||||
++ export CI_PROJECT_REPOSITORY_LANGUAGES=
|
||||
++ CI_PROJECT_REPOSITORY_LANGUAGES=
|
||||
++ export CI_PROJECT_CLASSIFICATION_LABEL=
|
||||
++ CI_PROJECT_CLASSIFICATION_LABEL=
|
||||
++ export CI_DEFAULT_BRANCH=main
|
||||
++ CI_DEFAULT_BRANCH=main
|
||||
++ export CI_REGISTRY=registry.gitlab.com
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ There are also [Kubernetes-specific deployment variables](../../user/project/clu
|
|||
| `CI_PROJECT_TITLE` | 12.4 | all | The human-readable project name as displayed in the GitLab web interface. |
|
||||
| `CI_PROJECT_URL` | 8.10 | 0.5 | The HTTP(S) address of the project. |
|
||||
| `CI_PROJECT_VISIBILITY` | 10.3 | all | The project visibility. Can be `internal`, `private`, or `public`. |
|
||||
| `CI_PROJECT_CLASSIFICATION_LABEL` | 14.2 | all | The project [external authorization classification label](../../user/admin_area/settings/external_authorization.md). |
|
||||
| `CI_REGISTRY_IMAGE` | 8.10 | 0.5 | The address of the project's Container Registry. Only available if the Container Registry is enabled for the project. |
|
||||
| `CI_REGISTRY_PASSWORD` | 9.0 | all | The password to push containers to the project's GitLab Container Registry. Only available if the Container Registry is enabled for the project. This password value is the same as the `CI_JOB_TOKEN` and is valid only as long as the job is running. Use the `CI_DEPLOY_PASSWORD` for long-lived access to the registry |
|
||||
| `CI_REGISTRY_USER` | 9.0 | all | The username to push containers to the project's GitLab Container Registry. Only available if the Container Registry is enabled for the project. |
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ When you are optimizing your SQL queries, there are two dimensions to pay attent
|
|||
| Queries in a migration | `100ms` | This is different than the total [migration time](migration_style_guide.md#how-long-a-migration-should-take). |
|
||||
| Concurrent operations in a migration | `5min` | Concurrent operations do not block the database, but they block the GitLab update. This includes operations such as `add_concurrent_index` and `add_concurrent_foreign_key`. |
|
||||
| Background migrations | `1s` | |
|
||||
| Service Ping | `1s` | See the [Service Ping docs](service_ping/index.md#develop-and-test-service-ping) for more details. |
|
||||
| Service Ping | `1s` | See the [Service Ping docs](service_ping/implement.md) for more details. |
|
||||
|
||||
- When analyzing your query's performance, pay attention to if the time you are seeing is on a [cold or warm cache](#cold-and-warm-cache). These guidelines apply for both cache types.
|
||||
- When working with batched queries, change the range and batch size to see how it effects the query timing and caching.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
stage: Growth
|
||||
group: Product Intelligence
|
||||
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
|
||||
---
|
||||
|
||||
# Develop and test Service Ping
|
||||
|
||||
To add a new metric and test Service Ping:
|
||||
|
||||
1. [Name and place the metric](#name-and-place-the-metric)
|
||||
1. [Test counters manually using your Rails console](#test-counters-manually-using-your-rails-console)
|
||||
1. [Generate the SQL query](#generate-the-sql-query)
|
||||
1. [Optimize queries with `#database-lab`](#optimize-queries-with-database-lab)
|
||||
1. [Add the metric definition](#add-the-metric-definition)
|
||||
1. [Add the metric to the Versions Application](#add-the-metric-to-the-versions-application)
|
||||
1. [Create a merge request](#create-a-merge-request)
|
||||
1. [Verify your metric](#verify-your-metric)
|
||||
1. [Set up and test Service Ping locally](#set-up-and-test-service-ping-locally)
|
||||
|
||||
## Name and place the metric
|
||||
|
||||
Add the metric in one of the top-level keys:
|
||||
|
||||
- `settings`: for settings related metrics.
|
||||
- `counts_weekly`: for counters that have data for the most recent 7 days.
|
||||
- `counts_monthly`: for counters that have data for the most recent 28 days.
|
||||
- `counts`: for counters that have data for all time.
|
||||
|
||||
### How to get a metric name suggestion
|
||||
|
||||
The metric YAML generator can suggest a metric name for you.
|
||||
To generate a metric name suggestion, first instrument the metric at the provided `key_path`.
|
||||
Then, generate the metric's YAML definition and
|
||||
return to the instrumentation and update it.
|
||||
|
||||
1. Add the metric instrumentation to `lib/gitlab/usage_data.rb` inside one
|
||||
of the [top-level keys](#name-and-place-the-metric), using any name you choose.
|
||||
1. Run the [metrics YAML generator](metrics_dictionary.md#metrics-definition-and-validation).
|
||||
1. Use the metric name suggestion to select a suitable metric name.
|
||||
1. Update the instrumentation you created in the first step and change the metric name to the suggested name.
|
||||
1. Update the metric's YAML definition with the correct `key_path`.
|
||||
|
||||
## Test counters manually using your Rails console
|
||||
|
||||
```ruby
|
||||
# count
|
||||
Gitlab::UsageData.count(User.active)
|
||||
Gitlab::UsageData.count(::Clusters::Cluster.aws_installed.enabled, :cluster_id)
|
||||
|
||||
# count distinct
|
||||
Gitlab::UsageData.distinct_count(::Project, :creator_id)
|
||||
Gitlab::UsageData.distinct_count(::Note.with_suggestions.where(time_period), :author_id, start: ::User.minimum(:id), finish: ::User.maximum(:id))
|
||||
```
|
||||
|
||||
## Generate the SQL query
|
||||
|
||||
Your Rails console returns the generated SQL queries. For example:
|
||||
|
||||
```ruby
|
||||
pry(main)> Gitlab::UsageData.count(User.active)
|
||||
(2.6ms) SELECT "features"."key" FROM "features"
|
||||
(15.3ms) SELECT MIN("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4))
|
||||
(2.4ms) SELECT MAX("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4))
|
||||
(1.9ms) SELECT COUNT("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4)) AND "users"."id" BETWEEN 1 AND 100000
|
||||
```
|
||||
|
||||
## Optimize queries with `#database-lab`
|
||||
|
||||
`#database-lab` is a Slack channel that uses a production-sized environment to test your queries.
|
||||
Paste the SQL query into `#database-lab` to see how the query performs at scale.
|
||||
|
||||
- GitLab.com's production database has a 15 second timeout.
|
||||
- Any single query must stay below the [1 second execution time](../query_performance.md#timing-guidelines-for-queries) with cold caches.
|
||||
- Add a specialized index on columns involved to reduce the execution time.
|
||||
|
||||
To understand the query's execution, we add the following information
|
||||
to a merge request description:
|
||||
|
||||
- For counters that have a `time_period` test, we add information for both:
|
||||
- `time_period = {}` for all time periods.
|
||||
- `time_period = { created_at: 28.days.ago..Time.current }` for the last 28 days.
|
||||
- Execution plan and query time before and after optimization.
|
||||
- Query generated for the index and time.
|
||||
- Migration output for up and down execution.
|
||||
|
||||
We also use `#database-lab` and [explain.depesz.com](https://explain.depesz.com/). For more details, see the [database review guide](../database_review.md#preparation-when-adding-or-modifying-queries).
|
||||
|
||||
### Optimization recommendations and examples
|
||||
|
||||
- Use specialized indexes. For examples, see these merge requests:
|
||||
- [Example 1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26871)
|
||||
- [Example 2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26445)
|
||||
- Use defined `start` and `finish`, and simple queries.
|
||||
These values can be memoized and reused, as in this [example merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37155).
|
||||
- Avoid joins and write the queries as simply as possible,
|
||||
as in this [example merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36316).
|
||||
- Set a custom `batch_size` for `distinct_count`, as in this [example merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38000).
|
||||
|
||||
## Add the metric definition
|
||||
|
||||
See the [Metrics Dictionary guide](metrics_dictionary.md) for more information.
|
||||
|
||||
## Add the metric to the Versions Application
|
||||
|
||||
Check if the new metric must be added to the Versions Application. See the `usage_data` [schema](https://gitlab.com/gitlab-services/version-gitlab-com/-/blob/master/db/schema.rb#L147) and Service Data [parameters accepted](https://gitlab.com/gitlab-services/version-gitlab-com/-/blob/master/app/services/usage_ping.rb). Any metrics added under the `counts` key are saved in the `stats` column.
|
||||
|
||||
## Create a merge request
|
||||
|
||||
Create a merge request for the new Service Ping metric, and do the following:
|
||||
|
||||
- Add the `feature` label to the merge request. A metric is a user-facing change and is part of expanding the Service Ping feature.
|
||||
- Add a changelog entry that complies with the [changelog entries guide](../changelog.md).
|
||||
- Ask for a Product Intelligence review.
|
||||
On GitLab.com, we have DangerBot set up to monitor Product Intelligence related files and recommend a [Product Intelligence review](review_guidelines.md).
|
||||
|
||||
## Verify your metric
|
||||
|
||||
On GitLab.com, the Product Intelligence team regularly [monitors Service Ping](https://gitlab.com/groups/gitlab-org/-/epics/6000).
|
||||
They may alert you that your metrics need further optimization to run quicker and with greater success.
|
||||
|
||||
The Service Ping JSON payload for GitLab.com is shared in the
|
||||
[#g_product_intelligence](https://gitlab.slack.com/archives/CL3A7GFPF) Slack channel every week.
|
||||
|
||||
You may also use the [Service Ping QA dashboard](https://app.periscopedata.com/app/gitlab/632033/Usage-Ping-QA) to check how well your metric performs.
|
||||
The dashboard allows filtering by GitLab version, by "Self-managed" and "SaaS", and shows you how many failures have occurred for each metric. Whenever you notice a high failure rate, you can re-optimize your metric.
|
||||
|
||||
## Set up and test Service Ping locally
|
||||
|
||||
To set up Service Ping locally, you must:
|
||||
|
||||
1. [Set up local repositories](#set-up-local-repositories).
|
||||
1. [Test local setup](#test-local-setup).
|
||||
1. (Optional) [Test Prometheus-based Service Ping](#test-prometheus-based-service-ping).
|
||||
|
||||
### Set up local repositories
|
||||
|
||||
1. Clone and start [GitLab](https://gitlab.com/gitlab-org/gitlab-development-kit).
|
||||
1. Clone and start [Versions Application](https://gitlab.com/gitlab-services/version-gitlab-com).
|
||||
Make sure you run `docker-compose up` to start a PostgreSQL and Redis instance.
|
||||
1. Point GitLab to the Versions Application endpoint instead of the default endpoint:
|
||||
1. Open [service_ping/submit_service.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/service_ping/submit_service.rb#L5) in your local and modified `PRODUCTION_URL`.
|
||||
1. Set it to the local Versions Application URL: `http://localhost:3000/usage_data`.
|
||||
|
||||
### Test local setup
|
||||
|
||||
1. Using the `gitlab` Rails console, manually trigger Service Ping:
|
||||
|
||||
```ruby
|
||||
ServicePing::SubmitService.new.execute
|
||||
```
|
||||
|
||||
1. Use the `versions` Rails console to check the Service Ping was successfully received,
|
||||
parsed, and stored in the Versions database:
|
||||
|
||||
```ruby
|
||||
UsageData.last
|
||||
```
|
||||
|
||||
## Test Prometheus-based Service Ping
|
||||
|
||||
If the data submitted includes metrics [queried from Prometheus](index.md#prometheus-queries)
|
||||
you want to inspect and verify, you must:
|
||||
|
||||
- Ensure that a Prometheus server is running locally.
|
||||
- Ensure the respective GitLab components are exporting metrics to the Prometheus server.
|
||||
|
||||
If you do not need to test data coming from Prometheus, no further action
|
||||
is necessary. Service Ping should degrade gracefully in the absence of a running Prometheus server.
|
||||
|
||||
Three kinds of components may export data to Prometheus, and are included in Service Ping:
|
||||
|
||||
- [`node_exporter`](https://github.com/prometheus/node_exporter): Exports node metrics
|
||||
from the host machine.
|
||||
- [`gitlab-exporter`](https://gitlab.com/gitlab-org/gitlab-exporter): Exports process metrics
|
||||
from various GitLab components.
|
||||
- Other various GitLab services, such as Sidekiq and the Rails server, which export their own metrics.
|
||||
|
||||
### Test with an Omnibus container
|
||||
|
||||
This is the recommended approach to test Prometheus-based Service Ping.
|
||||
|
||||
To verify your change, build a new Omnibus image from your code branch using CI/CD, download the image,
|
||||
and run a local container instance:
|
||||
|
||||
1. From your merge request, select the `qa` stage, then trigger the `package-and-qa` job. This job triggers an Omnibus
|
||||
build in a [downstream pipeline of the `omnibus-gitlab-mirror` project](https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/-/pipelines).
|
||||
1. In the downstream pipeline, wait for the `gitlab-docker` job to finish.
|
||||
1. Open the job logs and locate the full container name including the version. It takes the following form: `registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`.
|
||||
1. On your local machine, make sure you are signed in to the GitLab Docker registry. You can find the instructions for this in
|
||||
[Authenticate to the GitLab Container Registry](../../user/packages/container_registry/index.md#authenticate-with-the-container-registry).
|
||||
1. Once signed in, download the new image by using `docker pull registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`
|
||||
1. For more information about working with and running Omnibus GitLab containers in Docker, refer to [GitLab Docker images](https://docs.gitlab.com/omnibus/docker/README.html) in the Omnibus documentation.
|
||||
|
||||
### Test with GitLab development toolkits
|
||||
|
||||
This is the less recommended approach, because it comes with a number of difficulties when emulating a real GitLab deployment.
|
||||
|
||||
The [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) is not set up to run a Prometheus server or `node_exporter` alongside other GitLab components. If you would
|
||||
like to do so, [Monitoring the GDK with Prometheus](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/prometheus/index.md#monitoring-the-gdk-with-prometheus) is a good start.
|
||||
|
||||
The [GCK](https://gitlab.com/gitlab-org/gitlab-compose-kit) has limited support for testing Prometheus based Service Ping.
|
||||
By default, it comes with a fully configured Prometheus service that is set up to scrape a number of components.
|
||||
However, it has the following limitations:
|
||||
|
||||
- It does not run a `gitlab-exporter` instance, so several `process_*` metrics from services such as Gitaly may be missing.
|
||||
- While it runs a `node_exporter`, `docker-compose` services emulate hosts, meaning that it normally reports itself as not associated
|
||||
with any of the other running services. That is not how node metrics are reported in a production setup, where `node_exporter`
|
||||
always runs as a process alongside other GitLab components on any given node. For Service Ping, none of the node data would therefore
|
||||
appear to be associated to any of the services running, because they all appear to be running on different hosts. To alleviate this problem, the `node_exporter` in GCK was arbitrarily "assigned" to the `web` service, meaning only for this service `node_*` metrics appears in Service Ping.
|
||||
|
|
@ -867,197 +867,6 @@ We return fallback values in these cases:
|
|||
| Timeouts, general failures | -1 |
|
||||
| Standard errors in counters | -2 |
|
||||
|
||||
## Develop and test Service Ping
|
||||
|
||||
### 1. Name and place the metric
|
||||
|
||||
Add the metric in one of the top level keys:
|
||||
|
||||
- `settings`: for settings related metrics.
|
||||
- `counts_weekly`: for counters that have data for the most recent 7 days.
|
||||
- `counts_monthly`: for counters that have data for the most recent 28 days.
|
||||
- `counts`: for counters that have data for all time.
|
||||
|
||||
#### How to get a metric name suggestion
|
||||
|
||||
The metric YAML generator can suggest a metric name for you. To generate a metric name suggestion,
|
||||
first instrument the metric at the provided `key_path`, generate the metrics YAML definition, then
|
||||
return to the instrumentation and update it.
|
||||
|
||||
1. Add the metric instrumentation to `lib/gitlab/usage_data.rb` inside one
|
||||
of the [top level keys](index.md#1-name-and-place-the-metric), using any name you choose.
|
||||
1. Run the [metrics YAML generator](metrics_dictionary.md#metrics-definition-and-validation).
|
||||
1. Use the metric name suggestion to select a suitable metric name.
|
||||
1. Update the instrumentation you created in the first step and change the metric name to the suggested name.
|
||||
1. Update the metric's YAML definition with the correct `key_path`.
|
||||
|
||||
### 2. Use your Rails console to manually test counters
|
||||
|
||||
```ruby
|
||||
# count
|
||||
Gitlab::UsageData.count(User.active)
|
||||
Gitlab::UsageData.count(::Clusters::Cluster.aws_installed.enabled, :cluster_id)
|
||||
|
||||
# count distinct
|
||||
Gitlab::UsageData.distinct_count(::Project, :creator_id)
|
||||
Gitlab::UsageData.distinct_count(::Note.with_suggestions.where(time_period), :author_id, start: ::User.minimum(:id), finish: ::User.maximum(:id))
|
||||
```
|
||||
|
||||
### 3. Generate the SQL query
|
||||
|
||||
Your Rails console returns the generated SQL queries.
|
||||
|
||||
Example:
|
||||
|
||||
```ruby
|
||||
pry(main)> Gitlab::UsageData.count(User.active)
|
||||
(2.6ms) SELECT "features"."key" FROM "features"
|
||||
(15.3ms) SELECT MIN("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4))
|
||||
(2.4ms) SELECT MAX("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4))
|
||||
(1.9ms) SELECT COUNT("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4)) AND "users"."id" BETWEEN 1 AND 100000
|
||||
```
|
||||
|
||||
### 4. Optimize queries with #database-lab
|
||||
|
||||
Paste the SQL query into `#database-lab` to see how the query performs at scale.
|
||||
|
||||
- `#database-lab` is a Slack channel which uses a production-sized environment to test your queries.
|
||||
- GitLab.com's production database has a 15 second timeout.
|
||||
- Any single query must stay below [1 second execution time](../query_performance.md#timing-guidelines-for-queries) with cold caches.
|
||||
- Add a specialized index on columns involved to reduce the execution time.
|
||||
|
||||
To have an understanding of the query's execution we add in the MR description the following information:
|
||||
|
||||
- For counters that have a `time_period` test we add information for both cases:
|
||||
- `time_period = {}` for all time periods
|
||||
- `time_period = { created_at: 28.days.ago..Time.current }` for last 28 days period
|
||||
- Execution plan and query time before and after optimization
|
||||
- Query generated for the index and time
|
||||
- Migration output for up and down execution
|
||||
|
||||
We also use `#database-lab` and [explain.depesz.com](https://explain.depesz.com/). For more details, see the [database review guide](../database_review.md#preparation-when-adding-or-modifying-queries).
|
||||
|
||||
#### Optimization recommendations and examples
|
||||
|
||||
- Use specialized indexes [example 1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26871), [example 2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26445).
|
||||
- Use defined `start` and `finish`, and simple queries. These values can be memoized and reused, [example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37155).
|
||||
- Avoid joins and write the queries as simply as possible, [example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36316).
|
||||
- Set a custom `batch_size` for `distinct_count`, [example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38000).
|
||||
|
||||
### 5. Add the metric definition
|
||||
|
||||
[Check Metrics Dictionary Guide](metrics_dictionary.md)
|
||||
|
||||
### 6. Add new metric to Versions Application
|
||||
|
||||
Check if the new metric must be added to the Versions Application. See `usage_data` [schema](https://gitlab.com/gitlab-services/version-gitlab-com/-/blob/master/db/schema.rb#L147) and Service Data [parameters accepted](https://gitlab.com/gitlab-services/version-gitlab-com/-/blob/master/app/services/usage_ping.rb). Any metrics added under the `counts` key are saved in the `stats` column.
|
||||
|
||||
### 7. Add the feature label
|
||||
|
||||
Add the `feature` label to the Merge Request for new Service Ping metrics. These are user-facing changes and are part of expanding the Service Ping feature.
|
||||
|
||||
### 8. Add a changelog entry
|
||||
|
||||
Ensure you comply with the [Changelog entries guide](../changelog.md).
|
||||
|
||||
### 9. Ask for a Product Intelligence review
|
||||
|
||||
On GitLab.com, we have DangerBot set up to monitor Product Intelligence related files and recommend a [Product Intelligence review](review_guidelines.md).
|
||||
|
||||
### 10. Verify your metric
|
||||
|
||||
On GitLab.com, the Product Intelligence team regularly [monitors Service Ping](https://gitlab.com/groups/gitlab-org/-/epics/6000).
|
||||
They may alert you that your metrics need further optimization to run quicker and with greater success.
|
||||
|
||||
The Service Ping JSON payload for GitLab.com is shared in the
|
||||
[#g_product_intelligence](https://gitlab.slack.com/archives/CL3A7GFPF) Slack channel every week.
|
||||
|
||||
You may also use the [Service Ping QA dashboard](https://app.periscopedata.com/app/gitlab/632033/Usage-Ping-QA) to check how well your metric performs. The dashboard allows filtering by GitLab version, by "Self-managed" & "SaaS" and shows you how many failures have occurred for each metric. Whenever you notice a high failure rate, you may re-optimize your metric.
|
||||
|
||||
### Service Ping local setup
|
||||
|
||||
To set up Service Ping locally, you must:
|
||||
|
||||
1. [Set up local repositories](#set-up-local-repositories).
|
||||
1. [Test local setup](#test-local-setup).
|
||||
1. (Optional) [Test Prometheus-based Service Ping](#test-prometheus-based-service-ping).
|
||||
|
||||
#### Set up local repositories
|
||||
|
||||
1. Clone and start [GitLab](https://gitlab.com/gitlab-org/gitlab-development-kit).
|
||||
1. Clone and start [Versions Application](https://gitlab.com/gitlab-services/version-gitlab-com).
|
||||
Make sure to run `docker-compose up` to start a PostgreSQL and Redis instance.
|
||||
1. Point GitLab to the Versions Application endpoint instead of the default endpoint:
|
||||
1. Open [service_ping/submit_service.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/service_ping/submit_service.rb#L5) in your local and modified `PRODUCTION_URL`.
|
||||
1. Set it to the local Versions Application URL `http://localhost:3000/usage_data`.
|
||||
|
||||
#### Test local setup
|
||||
|
||||
1. Using the `gitlab` Rails console, manually trigger Service Ping:
|
||||
|
||||
```ruby
|
||||
ServicePing::SubmitService.new.execute
|
||||
```
|
||||
|
||||
1. Use the `versions` Rails console to check the Service Ping was successfully received,
|
||||
parsed, and stored in the Versions database:
|
||||
|
||||
```ruby
|
||||
UsageData.last
|
||||
```
|
||||
|
||||
### Test Prometheus-based Service Ping
|
||||
|
||||
If the data submitted includes metrics [queried from Prometheus](#prometheus-queries)
|
||||
you want to inspect and verify, you must:
|
||||
|
||||
- Ensure that a Prometheus server is running locally.
|
||||
- Ensure the respective GitLab components are exporting metrics to the Prometheus server.
|
||||
|
||||
If you do not need to test data coming from Prometheus, no further action
|
||||
is necessary. Service Ping should degrade gracefully in the absence of a running Prometheus server.
|
||||
|
||||
Three kinds of components may export data to Prometheus, and are included in Service Ping:
|
||||
|
||||
- [`node_exporter`](https://github.com/prometheus/node_exporter): Exports node metrics
|
||||
from the host machine.
|
||||
- [`gitlab-exporter`](https://gitlab.com/gitlab-org/gitlab-exporter): Exports process metrics
|
||||
from various GitLab components.
|
||||
- Other various GitLab services, such as Sidekiq and the Rails server, which export their own metrics.
|
||||
|
||||
#### Test with an Omnibus container
|
||||
|
||||
This is the recommended approach to test Prometheus based Service Ping.
|
||||
|
||||
The easiest way to verify your changes is to build a new Omnibus image from your code branch using CI/CD, download the image,
|
||||
and run a local container instance:
|
||||
|
||||
1. From your merge request, select the `qa` stage, then trigger the `package-and-qa` job. This job triggers an Omnibus
|
||||
build in a [downstream pipeline of the `omnibus-gitlab-mirror` project](https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/-/pipelines).
|
||||
1. In the downstream pipeline, wait for the `gitlab-docker` job to finish.
|
||||
1. Open the job logs and locate the full container name including the version. It takes the following form: `registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`.
|
||||
1. On your local machine, make sure you are signed in to the GitLab Docker registry. You can find the instructions for this in
|
||||
[Authenticate to the GitLab Container Registry](../../user/packages/container_registry/index.md#authenticate-with-the-container-registry).
|
||||
1. Once signed in, download the new image by using `docker pull registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`
|
||||
1. For more information about working with and running Omnibus GitLab containers in Docker, please refer to [GitLab Docker images](https://docs.gitlab.com/omnibus/docker/README.html) in the Omnibus documentation.
|
||||
|
||||
#### Test with GitLab development toolkits
|
||||
|
||||
This is the less recommended approach, because it comes with a number of difficulties when emulating a real GitLab deployment.
|
||||
|
||||
The [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) is not set up to run a Prometheus server or `node_exporter` alongside other GitLab components. If you would
|
||||
like to do so, [Monitoring the GDK with Prometheus](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/prometheus/index.md#monitoring-the-gdk-with-prometheus) is a good start.
|
||||
|
||||
The [GCK](https://gitlab.com/gitlab-org/gitlab-compose-kit) has limited support for testing Prometheus based Service Ping.
|
||||
By default, it already comes with a fully configured Prometheus service that is set up to scrape a number of components,
|
||||
but with the following limitations:
|
||||
|
||||
- It does not run a `gitlab-exporter` instance, so several `process_*` metrics from services such as Gitaly may be missing.
|
||||
- While it runs a `node_exporter`, `docker-compose` services emulate hosts, meaning that it normally reports itself as not associated
|
||||
with any of the other running services. That is not how node metrics are reported in a production setup, where `node_exporter`
|
||||
always runs as a process alongside other GitLab components on any given node. For Service Ping, none of the node data would therefore
|
||||
appear to be associated to any of the services running, because they all appear to be running on different hosts. To alleviate this problem, the `node_exporter` in GCK was arbitrarily "assigned" to the `web` service, meaning only for this service `node_*` metrics appears in Service Ping.
|
||||
|
||||
## Aggregated metrics
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45979) in GitLab 13.6.
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ are regular backend changes.
|
|||
#### The Product Intelligence **reviewer** should
|
||||
|
||||
- Perform a first-pass review on the merge request and suggest improvements to the author.
|
||||
- Check the [metrics location](index.md#1-name-and-place-the-metric) in
|
||||
- Check the [metrics location](implement.md#name-and-place-the-metric) in
|
||||
the Service Ping JSON payload.
|
||||
- Suggest that the author checks the [naming suggestion](index.md#how-to-get-a-metric-name-suggestion) while
|
||||
- Suggest that the author checks the [naming suggestion](implement.md#how-to-get-a-metric-name-suggestion) while
|
||||
generating the metric's YAML definition.
|
||||
- Add the `~database` label and ask for a [database review](../database_review.md) for
|
||||
metrics that are based on Database.
|
||||
|
|
@ -68,8 +68,7 @@ are regular backend changes.
|
|||
- Check the file location. Consider the time frame, and if the file should be under `ee`.
|
||||
- Check the tiers.
|
||||
- Metrics instrumentations
|
||||
- Recommend to use metrics instrumentation for new metrics addded to service with
|
||||
[limitations](metrics_instrumentation.md#support-for-instrumentation-classes)
|
||||
- Recommend using metrics instrumentation for new metrics, [if possible](metrics_instrumentation.md#support-for-instrumentation-classes).
|
||||
- Approve the MR, and relabel the MR with `~"product intelligence::approved"`.
|
||||
|
||||
## Review workload distribution
|
||||
|
|
|
|||
|
|
@ -5,61 +5,60 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
type: howto
|
||||
---
|
||||
|
||||
# Geo nodes Admin Area **(PREMIUM SELF)**
|
||||
# Geo sites Admin Area **(PREMIUM SELF)**
|
||||
|
||||
You can configure various settings for GitLab Geo nodes. For more information, see
|
||||
You can configure various settings for GitLab Geo sites. For more information, see
|
||||
[Geo documentation](../../administration/geo/index.md).
|
||||
|
||||
On either the primary or secondary node:
|
||||
On either the primary or secondary site:
|
||||
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. On the left sidebar, select **Geo > Nodes**.
|
||||
|
||||
## Common settings
|
||||
|
||||
All Geo nodes have the following settings:
|
||||
All Geo sites have the following settings:
|
||||
|
||||
| Setting | Description |
|
||||
| --------| ----------- |
|
||||
| Primary | This marks a Geo Node as **primary** node. There can be only one **primary** node; make sure that you first add the **primary** node and then all the others. |
|
||||
| Name | The unique identifier for the Geo node. Must match the setting `gitlab_rails['geo_node_name']` in `/etc/gitlab/gitlab.rb`. The setting defaults to `external_url` with a trailing slash. |
|
||||
| Primary | This marks a Geo site as **primary** site. There can be only one **primary** site. |
|
||||
| Name | The unique identifier for the Geo site. It's highly recommended to use a physical location as a name. Good examples are "London Office" or "us-east-1". Avoid words like "primary", "secondary", "Geo", or "DR". This makes the failover process easier because the physical location does not change, but the Geo site role can. All nodes in a single Geo site use the same site name. Nodes use the `gitlab_rails['geo_node_name']` setting in `/etc/gitlab/gitlab.rb` to lookup their Geo site record in the PostgreSQL database. If `gitlab_rails['geo_node_name']` is not set, then the node's `external_url` with trailing slash is used as fallback. The value of `Name` is case-sensitive, and most characters are allowed. |
|
||||
| URL | The instance's user-facing URL. |
|
||||
|
||||
The node you're reading from is indicated with a green `Current node` label, and
|
||||
the **primary** node is given a blue `Primary` label. Remember that you can only make
|
||||
changes on the **primary** node!
|
||||
The site you're currently browsing is indicated with a blue `Current` label, and
|
||||
the **primary** node is listed first as `Primary site`.
|
||||
|
||||
## **Secondary** node settings
|
||||
## **Secondary** site settings
|
||||
|
||||
**Secondary** nodes have a number of additional settings available:
|
||||
**Secondary** sites have a number of additional settings available:
|
||||
|
||||
| Setting | Description |
|
||||
|---------------------------|-------------|
|
||||
| Selective synchronization | Enable Geo [selective sync](../../administration/geo/replication/configuration.md#selective-synchronization) for this **secondary** node. |
|
||||
| Repository sync capacity | Number of concurrent requests this **secondary** node will make to the **primary** node when backfilling repositories. |
|
||||
| File sync capacity | Number of concurrent requests this **secondary** node will make to the **primary** node when backfilling files. |
|
||||
| Selective synchronization | Enable Geo [selective sync](../../administration/geo/replication/configuration.md#selective-synchronization) for this **secondary** site. |
|
||||
| Repository sync capacity | Number of concurrent requests this **secondary** site will make to the **primary** site when backfilling repositories. |
|
||||
| File sync capacity | Number of concurrent requests this **secondary** site will make to the **primary** site when backfilling files. |
|
||||
|
||||
## Geo backfill
|
||||
|
||||
**Secondary** nodes are notified of changes to repositories and files by the **primary** node,
|
||||
**Secondary** sites are notified of changes to repositories and files by the **primary** site,
|
||||
and will always attempt to synchronize those changes as quickly as possible.
|
||||
|
||||
Backfill is the act of populating the **secondary** node with repositories and files that
|
||||
existed *before* the **secondary** node was added to the database. Since there may be
|
||||
Backfill is the act of populating the **secondary** site with repositories and files that
|
||||
existed *before* the **secondary** site was added to the database. Since there may be
|
||||
extremely large numbers of repositories and files, it's infeasible to attempt to
|
||||
download them all at once, so GitLab places an upper limit on the concurrency of
|
||||
these operations.
|
||||
|
||||
How long the backfill takes is a function of the maximum concurrency, but higher
|
||||
values place more strain on the **primary** node. From [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3107),
|
||||
the limits are configurable. If your **primary** node has lots of surplus capacity,
|
||||
values place more strain on the **primary** site. From [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3107),
|
||||
the limits are configurable. If your **primary** site has lots of surplus capacity,
|
||||
you can increase the values to complete backfill in a shorter time. If it's
|
||||
under heavy load and backfill is reducing its availability for normal requests,
|
||||
you can decrease them.
|
||||
|
||||
## Using a different URL for synchronization
|
||||
|
||||
The **primary** node's Internal URL is used by **secondary** nodes to contact it
|
||||
The **primary** site's Internal URL is used by **secondary** sites to contact it
|
||||
(to sync repositories, for example). The name Internal URL distinguishes it from
|
||||
[External URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab)
|
||||
which is used by users. Internal URL does not need to be a private address.
|
||||
|
|
@ -68,13 +67,13 @@ Internal URL defaults to external URL, but you can also customize it:
|
|||
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. On the left sidebar, select **Geo > Nodes**.
|
||||
1. Select **Edit** on the node you want to customize.
|
||||
1. Select **Edit** on the site you want to customize.
|
||||
1. Edit the internal URL.
|
||||
1. Select **Save changes**.
|
||||
|
||||
WARNING:
|
||||
We recommend using an HTTPS connection while configuring the Geo nodes. To avoid
|
||||
breaking communication between **primary** and **secondary** nodes when using
|
||||
We recommend using an HTTPS connection while configuring the Geo sites. To avoid
|
||||
breaking communication between **primary** and **secondary** sites when using
|
||||
HTTPS, customize your Internal URL to point to a load balancer with TLS
|
||||
terminated at the load balancer.
|
||||
|
||||
|
|
@ -84,14 +83,14 @@ using an internal URL that is not accessible to the users will result in the
|
|||
OAuth authorization flow not working properly, as the users will get redirected
|
||||
to the internal URL instead of the external one.
|
||||
|
||||
## Multiple secondary nodes behind a load balancer
|
||||
## Multiple secondary sites behind a load balancer
|
||||
|
||||
In GitLab 11.11, **secondary** nodes can use identical external URLs as long as
|
||||
a unique `name` is set for each Geo node. The `gitlab.rb` setting
|
||||
In GitLab 11.11, **secondary** sites can use identical external URLs as long as
|
||||
a unique `name` is set for each Geo site. The `gitlab.rb` setting
|
||||
`gitlab_rails['geo_node_name']` must:
|
||||
|
||||
- Be set for each GitLab instance that runs `puma`, `sidekiq`, or `geo_logcursor`.
|
||||
- Match a Geo node name.
|
||||
- Match a Geo site name.
|
||||
|
||||
The load balancer must use sticky sessions in order to avoid authentication
|
||||
failures and cross site request errors.
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ To enable merge request approval rules for an instance:
|
|||
|
||||
Merge request approval rules that can be set at an instance level are:
|
||||
|
||||
- **Prevent approval of merge requests by merge request author**. Prevents project
|
||||
- **Prevent approval by author**. Prevents project
|
||||
maintainers from allowing request authors to merge their own merge requests.
|
||||
- **Prevent approval of merge requests by merge request committers**. Prevents project
|
||||
- **Prevent approvals by users who add commits**. Prevents project
|
||||
maintainers from allowing users to approve merge requests if they have submitted
|
||||
any commits to the source branch.
|
||||
- **Prevent users from modifying merge request approvers list**. Prevents users from
|
||||
- **Prevent editing approval rules in projects and merge requests**. Prevents users from
|
||||
modifying the approvers list in project settings or in individual merge requests.
|
||||
|
||||
Also read the [project level merge request approval rules](../project/merge_requests/approvals/index.md), which are affected by instance level rules.
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ We support a separation of duties policy between users who create and approve me
|
|||
The approval status column can help you identify violations of this policy.
|
||||
Our criteria for the separation of duties is as follows:
|
||||
|
||||
- [A merge request author is **not** allowed to approve their merge request](../../project/merge_requests/approvals/settings.md#prevent-authors-from-approving-their-own-work)
|
||||
- [A merge request committer is **not** allowed to approve a merge request they have added commits to](../../project/merge_requests/approvals/settings.md#prevent-committers-from-approving-their-own-work)
|
||||
- [A merge request author is **not** allowed to approve their merge request](../../project/merge_requests/approvals/settings.md#prevent-approval-by-author)
|
||||
- [A merge request committer is **not** allowed to approve a merge request they have added commits to](../../project/merge_requests/approvals/settings.md#prevent-approvals-by-users-who-add-commits)
|
||||
- [The minimum number of approvals required to merge a merge request is **at least** two](../../project/merge_requests/approvals/rules.md)
|
||||
|
||||
The **Approval status** column shows you at a glance whether a merge request is complying with the above.
|
||||
|
|
|
|||
|
|
@ -42,17 +42,12 @@ rules to define what types of users can approve work. Some examples of rules you
|
|||
- Users with specific permissions can be allowed or denied the ability
|
||||
to [override approval rules on a specific merge request](rules.md#edit-or-override-merge-request-approval-rules).
|
||||
|
||||
You can also configure additional [settings for merge request approvals](settings.md)
|
||||
for more control of the level of oversight and security your project needs, including:
|
||||
You can also configure:
|
||||
|
||||
- [Prevent users from overriding a merge request approval rule.](settings.md#prevent-overrides-of-default-approvals)
|
||||
- [Reset approvals when new code is pushed.](settings.md#reset-approvals-on-push)
|
||||
- Allow (or disallow) [authors and committers](settings.md) to approve their own merge requests.
|
||||
- [Require password authentication when approving.](settings.md#require-authentication-for-approvals)
|
||||
- [Require security team approval.](settings.md#security-approvals-in-merge-requests)
|
||||
|
||||
You can configure your merge request approval rules and settings through the GitLab
|
||||
user interface or with the [Merge request approvals API](../../../../api/merge_request_approvals.md).
|
||||
- Additional [settings for merge request approvals](settings.md) for more control of the
|
||||
level of oversight and security your project needs.
|
||||
- Merge request approval rules and settings through the GitLab UI or with the
|
||||
[Merge request approvals API](../../../../api/merge_request_approvals.md).
|
||||
|
||||
## Approve a merge request
|
||||
|
||||
|
|
@ -74,10 +69,10 @@ such as merge conflicts, [pending discussions](../../../discussions/index.md#pre
|
|||
or a [failed CI/CD pipeline](../merge_when_pipeline_succeeds.md).
|
||||
|
||||
To prevent merge request authors from approving their own merge requests,
|
||||
enable [**Prevent author approval**](settings.md#prevent-authors-from-approving-their-own-work)
|
||||
enable [**Prevent author approval**](settings.md#prevent-approval-by-author)
|
||||
in your project's settings.
|
||||
|
||||
If you enable [approval rule overrides](settings.md#prevent-overrides-of-default-approvals),
|
||||
If you enable [approval rule overrides](settings.md#prevent-editing-approval-rules-in-merge-requests),
|
||||
merge requests created before a change to default approval rules are not affected.
|
||||
The only exceptions are changes to the [target branch](rules.md#approvals-for-protected-branches)
|
||||
of the rule.
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ Users of GitLab Premium and higher tiers can create [additional approval rules](
|
|||
Your configuration for approval rule overrides determines if the new rule is applied
|
||||
to existing merge requests:
|
||||
|
||||
- If [approval rule overrides](settings.md#prevent-overrides-of-default-approvals) are allowed,
|
||||
- If [approval rule overrides](settings.md#prevent-editing-approval-rules-in-merge-requests) are allowed,
|
||||
changes to these default rules are not applied to existing merge requests, except for
|
||||
changes to the [target branch](#approvals-for-protected-branches) of the rule.
|
||||
- If approval rule overrides are not allowed, all changes to default rules
|
||||
|
|
@ -138,10 +138,10 @@ approve in these ways:
|
|||
counts as one approver, and not two.
|
||||
- Merge request authors do not count as eligible approvers on their own merge requests by default.
|
||||
To change this behavior, disable the
|
||||
[**Prevent author approval**](settings.md#prevent-authors-from-approving-their-own-work)
|
||||
[**Prevent author approval**](settings.md#prevent-approval-by-author)
|
||||
project setting.
|
||||
- Committers to merge requests can approve a merge request. To change this behavior, enable the
|
||||
[**Prevent committers approval**](settings.md#prevent-committers-from-approving-their-own-work)
|
||||
[**Prevent committers approval**](settings.md#prevent-approvals-by-users-who-add-commits)
|
||||
project setting.
|
||||
|
||||
### Code owners as eligible approvers **(PREMIUM)**
|
||||
|
|
@ -201,7 +201,7 @@ on a merge request, you can either add or remove approvers:
|
|||
1. Add or remove your desired approval rules.
|
||||
1. Select **Save changes**.
|
||||
|
||||
Administrators can change the [merge request approvals settings](settings.md#prevent-overrides-of-default-approvals)
|
||||
Administrators can change the [merge request approvals settings](settings.md#prevent-editing-approval-rules-in-merge-requests)
|
||||
to prevent users from overriding approval rules for merge requests.
|
||||
|
||||
## Configure optional approval rules **(PREMIUM)**
|
||||
|
|
|
|||
|
|
@ -20,37 +20,17 @@ To view or edit merge request approval settings:
|
|||
1. Go to your project and select **Settings > General**.
|
||||
1. Expand **Merge request (MR) approvals**.
|
||||
|
||||
In this section of general settings, you can configure the settings described
|
||||
on this page.
|
||||
In this section of general settings, you can configure the following settings:
|
||||
|
||||
## Prevent overrides of default approvals
|
||||
| Setting | Description |
|
||||
| ------ | ------ |
|
||||
| [Prevent approval by author](#prevent-approval-by-author) | When enabled, the author of a merge request cannot approve it. |
|
||||
| [Prevent approvals by users who add commits](#prevent-approvals-by-users-who-add-commits) | When enabled, users who have committed to a merge request cannot approve it. |
|
||||
| [Prevent editing approval rules in merge requests](#prevent-editing-approval-rules-in-merge-requests) | When enabled, users can't override the project's approval rules on merge requests. |
|
||||
| [Require user password to approve](#require-user-password-to-approve) | Force potential approvers to first authenticate with a password. |
|
||||
| [Remove all approvals when commits are added to the source branch](#remove-all-approvals-when-commits-are-added-to-the-source-branch) | When enabled, remove all existing approvals on a merge request when more changes are added to it. |
|
||||
|
||||
By default, users can override the approval rules you [create for a project](rules.md)
|
||||
on a per-merge request basis. If you don't want users to change approval rules
|
||||
on merge requests, you can disable this setting:
|
||||
|
||||
1. Go to your project and select **Settings > General**.
|
||||
1. Expand **Merge request (MR) approvals**.
|
||||
1. Select the **Prevent users from modifying MR approval rules in merge requests** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
This change affects all open merge requests.
|
||||
|
||||
## Reset approvals on push
|
||||
|
||||
By default, an approval on a merge request remains in place, even if you add more changes
|
||||
after the approval. If you want to remove all existing approvals on a merge request
|
||||
when more changes are added to it:
|
||||
|
||||
1. Go to your project and select **Settings > General**.
|
||||
1. Expand **Merge request (MR) approvals**.
|
||||
1. Select the **Require new approvals when new commits are added to an MR** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
Approvals aren't reset when a merge request is [rebased from the UI](../fast_forward_merge.md)
|
||||
However, approvals are reset if the target branch is changed.
|
||||
|
||||
## Prevent authors from approving their own work **(PREMIUM)**
|
||||
## Prevent approval by author **(PREMIUM)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3349) in GitLab 11.3.
|
||||
> - Moved to GitLab Premium in 13.9.
|
||||
|
|
@ -65,14 +45,14 @@ By default, the author of a merge request cannot approve it. To change this sett
|
|||
Authors can edit the approval rule in an individual merge request and override
|
||||
this setting, unless you configure one of these options:
|
||||
|
||||
- [Prevent overrides of default approvals](#prevent-overrides-of-default-approvals) at
|
||||
- [Prevent overrides of default approvals](#prevent-editing-approval-rules-in-merge-requests) at
|
||||
the project level.
|
||||
- *(Self-managed instances only)* Prevent overrides of default approvals
|
||||
[at the instance level](../../../admin_area/merge_requests_approvals.md). When configured
|
||||
at the instance level, you can't edit this setting at the project or individual
|
||||
merge request levels.
|
||||
|
||||
## Prevent committers from approving their own work **(PREMIUM)**
|
||||
## Prevent approvals by users who add commits **(PREMIUM)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10441) in GitLab 11.10.
|
||||
> - Moved to GitLab Premium in 13.9.
|
||||
|
|
@ -96,7 +76,20 @@ to a merge request can approve merge requests that affect files they own.
|
|||
To learn more about the [differences between authors and committers](https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History),
|
||||
read the official Git documentation for an explanation.
|
||||
|
||||
## Require authentication for approvals
|
||||
## Prevent editing approval rules in merge requests
|
||||
|
||||
By default, users can override the approval rules you [create for a project](rules.md)
|
||||
on a per-merge request basis. If you don't want users to change approval rules
|
||||
on merge requests, you can disable this setting:
|
||||
|
||||
1. Go to your project and select **Settings > General**.
|
||||
1. Expand **Merge request (MR) approvals**.
|
||||
1. Select the **Prevent users from modifying MR approval rules in merge requests** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
This change affects all open merge requests.
|
||||
|
||||
## Require user password to approve
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5981) in GitLab 12.0.
|
||||
> - Moved to GitLab Premium in 13.9.
|
||||
|
|
@ -112,6 +105,20 @@ permission enables an electronic signature for approvals, such as the one define
|
|||
1. Select the **Require user password for approvals** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Remove all approvals when commits are added to the source branch
|
||||
|
||||
By default, an approval on a merge request remains in place, even if you add more changes
|
||||
after the approval. If you want to remove all existing approvals on a merge request
|
||||
when more changes are added to it:
|
||||
|
||||
1. Go to your project and select **Settings > General**.
|
||||
1. Expand **Merge request (MR) approvals**.
|
||||
1. Select the **Require new approvals when new commits are added to an MR** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
Approvals aren't reset when a merge request is [rebased from the UI](../fast_forward_merge.md)
|
||||
However, approvals are reset if the target branch is changed.
|
||||
|
||||
## Security approvals in merge requests **(ULTIMATE)**
|
||||
|
||||
You can require that a member of your security team approves a merge request if a
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ type: concepts, howto
|
|||
|
||||
# Signing commits and tags with X.509 **(FREE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17773) in GitLab 12.8.
|
||||
|
||||
[X.509](https://en.wikipedia.org/wiki/X.509) is a standard format for public key
|
||||
certificates issued by a public or private Public Key Infrastructure (PKI).
|
||||
Personal X.509 certificates are used for authentication or signing purposes
|
||||
|
|
@ -37,6 +39,20 @@ Self signed certificates without `authorityKeyIdentifier`,
|
|||
recommend using certificates from a PKI that are in line with
|
||||
[RFC 5280](https://tools.ietf.org/html/rfc5280).
|
||||
|
||||
## Limitations
|
||||
|
||||
- If you have more than one email in the Subject Alternative Name list in
|
||||
your signing certificate,
|
||||
[only the first one is used to verify commits](https://gitlab.com/gitlab-org/gitlab/-/issues/336677).
|
||||
- The `X509v3 Subject Key Identifier` (SKI) in the issuer certificate and the
|
||||
signing certificate
|
||||
[must be 40 characters long](https://gitlab.com/gitlab-org/gitlab/-/issues/332503).
|
||||
If your SKI is shorter, commits will not show as verified in GitLab, and
|
||||
short subject key identifiers may also
|
||||
[cause errors when accessing the project](https://gitlab.com/gitlab-org/gitlab/-/issues/332464),
|
||||
such as 'An error occurred while loading commit signatures' and
|
||||
`HTTP 422 Unprocessable Entity` errors.
|
||||
|
||||
## Obtaining an X.509 key pair
|
||||
|
||||
If your organization has Public Key Infrastructure (PKI), that PKI provides
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Sidebars
|
||||
module Groups
|
||||
module Menus
|
||||
class SettingsMenu < ::Sidebars::Menu
|
||||
override :configure_menu_items
|
||||
def configure_menu_items
|
||||
return false unless can?(context.current_user, :admin_group, context.group)
|
||||
|
||||
add_item(general_menu_item)
|
||||
add_item(integrations_menu_item)
|
||||
add_item(group_projects_menu_item)
|
||||
add_item(repository_menu_item)
|
||||
add_item(ci_cd_menu_item)
|
||||
add_item(applications_menu_item)
|
||||
add_item(packages_and_registries_menu_item)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
override :link
|
||||
def link
|
||||
edit_group_path(context.group)
|
||||
end
|
||||
|
||||
override :title
|
||||
def title
|
||||
_('Settings')
|
||||
end
|
||||
|
||||
override :sprite_icon
|
||||
def sprite_icon
|
||||
'settings'
|
||||
end
|
||||
|
||||
override :extra_nav_link_html_options
|
||||
def extra_nav_link_html_options
|
||||
{
|
||||
class: 'shortcuts-settings'
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def general_menu_item
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('General'),
|
||||
link: edit_group_path(context.group),
|
||||
active_routes: { path: 'groups#edit' },
|
||||
item_id: :general
|
||||
)
|
||||
end
|
||||
|
||||
def integrations_menu_item
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('Integrations'),
|
||||
link: group_settings_integrations_path(context.group),
|
||||
active_routes: { controller: :integrations },
|
||||
item_id: :integrations
|
||||
)
|
||||
end
|
||||
|
||||
def group_projects_menu_item
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('Projects'),
|
||||
link: projects_group_path(context.group),
|
||||
active_routes: { path: 'groups#projects' },
|
||||
item_id: :group_projects
|
||||
)
|
||||
end
|
||||
|
||||
def repository_menu_item
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('Repository'),
|
||||
link: group_settings_repository_path(context.group),
|
||||
active_routes: { controller: :repository },
|
||||
item_id: :repository
|
||||
)
|
||||
end
|
||||
|
||||
def ci_cd_menu_item
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('CI/CD'),
|
||||
link: group_settings_ci_cd_path(context.group),
|
||||
active_routes: { path: %w[ci_cd#show groups/runners#show groups/runners#edit] },
|
||||
item_id: :ci_cd
|
||||
)
|
||||
end
|
||||
|
||||
def applications_menu_item
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('Applications'),
|
||||
link: group_settings_applications_path(context.group),
|
||||
active_routes: { controller: :applications },
|
||||
item_id: :applications
|
||||
)
|
||||
end
|
||||
|
||||
def packages_and_registries_menu_item
|
||||
unless context.group.packages_feature_enabled?
|
||||
return ::Sidebars::NilMenuItem.new(item_id: :packages_and_registries)
|
||||
end
|
||||
|
||||
::Sidebars::MenuItem.new(
|
||||
title: _('Packages & Registries'),
|
||||
link: group_settings_packages_and_registries_path(context.group),
|
||||
active_routes: { controller: :packages_and_registries },
|
||||
item_id: :packages_and_registries
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Sidebars::Groups::Menus::SettingsMenu.prepend_mod_with('Sidebars::Groups::Menus::SettingsMenu')
|
||||
|
|
@ -13,6 +13,7 @@ module Sidebars
|
|||
add_menu(Sidebars::Groups::Menus::CiCdMenu.new(context))
|
||||
add_menu(Sidebars::Groups::Menus::KubernetesMenu.new(context))
|
||||
add_menu(Sidebars::Groups::Menus::PackagesRegistriesMenu.new(context))
|
||||
add_menu(Sidebars::Groups::Menus::SettingsMenu.new(context))
|
||||
end
|
||||
|
||||
override :render_raw_menus_partial
|
||||
|
|
|
|||
|
|
@ -4247,19 +4247,25 @@ msgstr ""
|
|||
msgid "ApprovalSettings|Merge request approval settings have been updated."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|Prevent MR approvals by the author."
|
||||
msgid "ApprovalSettings|Prevent approval by author."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|Prevent approval of merge requests by merge request committers."
|
||||
msgid "ApprovalSettings|Prevent approvals by users who add commits."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|Prevent users from modifying MR approval rules."
|
||||
msgid "ApprovalSettings|Prevent editing approval rules in merge requests."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|Remove all approvals in a merge request when new commits are pushed to its source branch."
|
||||
msgid "ApprovalSettings|Prevent editing approval rules in projects and merge requests."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|Require user password for approvals."
|
||||
msgid "ApprovalSettings|Prevent editing approval rules in projects and merge requests. "
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|Remove all approvals when commits are added to the source branch."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|Require user password to approve."
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalSettings|There was an error loading merge request approval settings."
|
||||
|
|
@ -8356,9 +8362,6 @@ msgstr ""
|
|||
msgid "Compliance framework"
|
||||
msgstr ""
|
||||
|
||||
msgid "Compliance framework (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ComplianceDashboard|created by:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -25233,15 +25236,12 @@ msgstr ""
|
|||
msgid "Prev"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent MR approvals by author."
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent MR approvals from users who make commits to the MR."
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent adding new members to project membership within this group"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent editing approval rules in projects and merge requests."
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent environment from auto-stopping"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -25251,9 +25251,6 @@ msgstr ""
|
|||
msgid "Prevent users from changing their profile name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent users from modifying MR approval rules in projects and merge requests."
|
||||
msgstr ""
|
||||
|
||||
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,6 @@ module QA
|
|||
class Menu < Page::Base
|
||||
include SubMenus::Common
|
||||
|
||||
view 'app/views/layouts/nav/sidebar/_group_menus.html.haml' do
|
||||
element :general_settings_link
|
||||
element :group_settings
|
||||
end
|
||||
|
||||
view 'app/views/groups/sidebar/_packages_settings.html.haml' do
|
||||
element :group_package_settings_link
|
||||
end
|
||||
|
||||
def click_group_members_item
|
||||
hover_group_information do
|
||||
within_submenu do
|
||||
|
|
@ -33,14 +24,14 @@ module QA
|
|||
|
||||
def click_settings
|
||||
within_sidebar do
|
||||
click_element(:group_settings)
|
||||
click_element(:sidebar_menu_link, menu_item: 'Settings')
|
||||
end
|
||||
end
|
||||
|
||||
def click_group_general_settings_item
|
||||
hover_element(:group_settings) do
|
||||
within_submenu(:group_sidebar_submenu) do
|
||||
click_element(:general_settings_link)
|
||||
hover_group_settings do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'General')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -54,10 +45,9 @@ module QA
|
|||
end
|
||||
|
||||
def go_to_package_settings
|
||||
scroll_to_element(:group_settings)
|
||||
hover_element(:group_settings) do
|
||||
within_submenu(:group_sidebar_submenu) do
|
||||
click_element(:group_package_settings_link)
|
||||
hover_group_settings do
|
||||
within_submenu do
|
||||
click_element(:sidebar_menu_item_link, menu_item: 'Packages & Registries')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -113,6 +103,15 @@ module QA
|
|||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def hover_group_settings
|
||||
within_sidebar do
|
||||
scroll_to_element(:sidebar_menu_link, menu_item: 'Settings')
|
||||
find_element(:sidebar_menu_link, menu_item: 'Settings').hover
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ RSpec.describe 'Group Packages & Registries settings' do
|
|||
end
|
||||
|
||||
def find_settings_menu
|
||||
find('ul[data-testid="group-settings-menu"]')
|
||||
find('.shortcuts-settings ul')
|
||||
end
|
||||
|
||||
def visit_settings_page
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { Range, Position } from 'monaco-editor';
|
||||
import { Range, Position, editor as monacoEditor } from 'monaco-editor';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import {
|
||||
EXTENSION_MARKDOWN_PREVIEW_PANEL_CLASS,
|
||||
EXTENSION_MARKDOWN_PREVIEW_ACTION_ID,
|
||||
EXTENSION_MARKDOWN_PREVIEW_PANEL_WIDTH,
|
||||
EXTENSION_MARKDOWN_PREVIEW_PANEL_PARENT_CLASS,
|
||||
EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY,
|
||||
} from '~/editor/constants';
|
||||
import { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext';
|
||||
import SourceEditor from '~/editor/source_editor';
|
||||
|
|
@ -27,7 +28,8 @@ describe('Markdown Extension for Source Editor', () => {
|
|||
const secondLine = 'multiline';
|
||||
const thirdLine = 'string with some **markup**';
|
||||
const text = `${firstLine}\n${secondLine}\n${thirdLine}`;
|
||||
const filePath = 'foo.md';
|
||||
const plaintextPath = 'foo.txt';
|
||||
const markdownPath = 'foo.md';
|
||||
const responseData = '<div>FooBar</div>';
|
||||
|
||||
const setSelection = (startLineNumber = 1, startColumn = 1, endLineNumber = 1, endColumn = 1) => {
|
||||
|
|
@ -52,7 +54,7 @@ describe('Markdown Extension for Source Editor', () => {
|
|||
editor = new SourceEditor();
|
||||
instance = editor.createInstance({
|
||||
el: editorEl,
|
||||
blobPath: filePath,
|
||||
blobPath: markdownPath,
|
||||
blobContent: text,
|
||||
});
|
||||
editor.use(new EditorMarkdownExtension({ instance, projectPath }));
|
||||
|
|
@ -70,16 +72,107 @@ describe('Markdown Extension for Source Editor', () => {
|
|||
el: undefined,
|
||||
action: expect.any(Object),
|
||||
shown: false,
|
||||
modelChangeListener: undefined,
|
||||
});
|
||||
expect(instance.projectPath).toBe(projectPath);
|
||||
});
|
||||
|
||||
describe('model language changes listener', () => {
|
||||
let cleanupSpy;
|
||||
let actionSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
cleanupSpy = jest.spyOn(instance, 'cleanup');
|
||||
actionSpy = jest.spyOn(instance, 'setupPreviewAction');
|
||||
await togglePreview();
|
||||
});
|
||||
|
||||
it('cleans up when switching away from markdown', () => {
|
||||
expect(instance.cleanup).not.toHaveBeenCalled();
|
||||
expect(instance.setupPreviewAction).not.toHaveBeenCalled();
|
||||
|
||||
instance.updateModelLanguage(plaintextPath);
|
||||
|
||||
expect(cleanupSpy).toHaveBeenCalled();
|
||||
expect(actionSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each`
|
||||
oldLanguage | newLanguage | setupCalledTimes
|
||||
${'plaintext'} | ${'markdown'} | ${1}
|
||||
${'markdown'} | ${'markdown'} | ${0}
|
||||
${'markdown'} | ${'plaintext'} | ${0}
|
||||
${'markdown'} | ${undefined} | ${0}
|
||||
${undefined} | ${'markdown'} | ${1}
|
||||
`(
|
||||
'correctly handles re-enabling of the action when switching from $oldLanguage to $newLanguage',
|
||||
({ oldLanguage, newLanguage, setupCalledTimes } = {}) => {
|
||||
expect(actionSpy).not.toHaveBeenCalled();
|
||||
instance.updateModelLanguage(oldLanguage);
|
||||
instance.updateModelLanguage(newLanguage);
|
||||
expect(actionSpy).toHaveBeenCalledTimes(setupCalledTimes);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('model change listener', () => {
|
||||
let cleanupSpy;
|
||||
let actionSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
cleanupSpy = jest.spyOn(instance, 'cleanup');
|
||||
actionSpy = jest.spyOn(instance, 'setupPreviewAction');
|
||||
instance.togglePreview();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('does not do anything if there is no model', () => {
|
||||
instance.setModel(null);
|
||||
|
||||
expect(cleanupSpy).not.toHaveBeenCalled();
|
||||
expect(actionSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('cleans up the preview when the model changes', () => {
|
||||
instance.setModel(monacoEditor.createModel('foo'));
|
||||
expect(cleanupSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each`
|
||||
language | setupCalledTimes
|
||||
${'markdown'} | ${1}
|
||||
${'plaintext'} | ${0}
|
||||
${undefined} | ${0}
|
||||
`(
|
||||
'correctly handles actions when the new model is $language',
|
||||
({ language, setupCalledTimes } = {}) => {
|
||||
instance.setModel(monacoEditor.createModel('foo', language));
|
||||
|
||||
expect(actionSpy).toHaveBeenCalledTimes(setupCalledTimes);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('cleanup', () => {
|
||||
beforeEach(async () => {
|
||||
mockAxios.onPost().reply(200, { body: responseData });
|
||||
await togglePreview();
|
||||
});
|
||||
|
||||
it('disposes the modelChange listener and does not fetch preview on content changes', () => {
|
||||
expect(instance.preview.modelChangeListener).toBeDefined();
|
||||
jest.spyOn(instance, 'fetchPreview');
|
||||
|
||||
instance.cleanup();
|
||||
instance.setValue('Foo Bar');
|
||||
jest.advanceTimersByTime(EXTENSION_MARKDOWN_PREVIEW_UPDATE_DELAY);
|
||||
|
||||
expect(instance.fetchPreview).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('removes the contextual menu action', () => {
|
||||
expect(instance.getAction(EXTENSION_MARKDOWN_PREVIEW_ACTION_ID)).toBeDefined();
|
||||
|
||||
|
|
@ -219,47 +312,6 @@ describe('Markdown Extension for Source Editor', () => {
|
|||
expect(instance.preview.shown).toBe(false);
|
||||
});
|
||||
|
||||
describe('model language changes', () => {
|
||||
const plaintextPath = 'foo.txt';
|
||||
const markdownPath = 'foo.md';
|
||||
let cleanupSpy;
|
||||
let actionSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
cleanupSpy = jest.spyOn(instance, 'cleanup');
|
||||
actionSpy = jest.spyOn(instance, 'setupPreviewAction');
|
||||
instance.togglePreview();
|
||||
});
|
||||
|
||||
it('cleans up when switching away from markdown', async () => {
|
||||
expect(instance.cleanup).not.toHaveBeenCalled();
|
||||
expect(instance.setupPreviewAction).not.toHaveBeenCalled();
|
||||
|
||||
instance.updateModelLanguage(plaintextPath);
|
||||
|
||||
expect(cleanupSpy).toHaveBeenCalled();
|
||||
expect(actionSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('re-enables the action when switching back to markdown', () => {
|
||||
instance.updateModelLanguage(plaintextPath);
|
||||
|
||||
jest.clearAllMocks();
|
||||
|
||||
instance.updateModelLanguage(markdownPath);
|
||||
|
||||
expect(cleanupSpy).not.toHaveBeenCalled();
|
||||
expect(actionSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not re-enable the action if we do not change the language', () => {
|
||||
instance.updateModelLanguage(markdownPath);
|
||||
|
||||
expect(cleanupSpy).not.toHaveBeenCalled();
|
||||
expect(actionSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('panel DOM element set up', () => {
|
||||
it('sets up an element to contain the preview and stores it on instance', () => {
|
||||
expect(instance.preview.el).toBeUndefined();
|
||||
|
|
@ -335,9 +387,9 @@ describe('Markdown Extension for Source Editor', () => {
|
|||
});
|
||||
|
||||
it('stores disposable listener for model changes', async () => {
|
||||
expect(instance.modelChangeListener).toBeUndefined();
|
||||
expect(instance.preview.modelChangeListener).toBeUndefined();
|
||||
await togglePreview();
|
||||
expect(instance.modelChangeListener).toBeDefined();
|
||||
expect(instance.preview.modelChangeListener).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -354,7 +406,7 @@ describe('Markdown Extension for Source Editor', () => {
|
|||
|
||||
it('disposes the model change event listener', () => {
|
||||
const disposeSpy = jest.fn();
|
||||
instance.modelChangeListener = {
|
||||
instance.preview.modelChangeListener = {
|
||||
dispose: disposeSpy,
|
||||
};
|
||||
instance.togglePreview();
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ describe('Source Editor utils', () => {
|
|||
${'foo.js'} | ${'javascript'}
|
||||
${'foo.js.rb'} | ${'ruby'}
|
||||
${'foo.bar'} | ${'plaintext'}
|
||||
${undefined} | ${'plaintext'}
|
||||
`(
|
||||
'sets the $expectedThemeName theme when $themeName is set in the user preference',
|
||||
({ path, expectedLanguage }) => {
|
||||
|
|
|
|||
|
|
@ -166,6 +166,11 @@ describe('RepoEditor', () => {
|
|||
expect(tabs).toHaveLength(1);
|
||||
expect(tabs.at(0).text()).toBe('Edit');
|
||||
});
|
||||
|
||||
it('does not get markdown extension by default', async () => {
|
||||
await createComponent();
|
||||
expect(vm.editor.projectPath).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when file is markdown', () => {
|
||||
|
|
@ -213,6 +218,11 @@ describe('RepoEditor', () => {
|
|||
});
|
||||
expect(findTabs()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('uses the markdown extension and sets it up correctly', async () => {
|
||||
await createComponent({ activeFile });
|
||||
expect(vm.editor.projectPath).toBe(vm.currentProjectId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when file is binary and not raw', () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Sidebars::Groups::Menus::SettingsMenu do
|
||||
let_it_be(:owner) { create(:user) }
|
||||
|
||||
let_it_be_with_refind(:group) do
|
||||
build(:group, :private).tap do |g|
|
||||
g.add_owner(owner)
|
||||
end
|
||||
end
|
||||
|
||||
let(:user) { owner }
|
||||
let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) }
|
||||
let(:menu) { described_class.new(context) }
|
||||
|
||||
describe '#render?' do
|
||||
context 'when user cannot admin group' do
|
||||
let(:user) { nil }
|
||||
|
||||
it 'returns false' do
|
||||
expect(menu.render?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Menu items' do
|
||||
subject { menu.renderable_items.find { |e| e.item_id == item_id } }
|
||||
|
||||
shared_examples 'access rights checks' do
|
||||
specify { is_expected.not_to be_nil }
|
||||
|
||||
context 'when the user does not have access' do
|
||||
let(:user) { nil }
|
||||
|
||||
specify { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'General menu' do
|
||||
let(:item_id) { :general }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
|
||||
describe 'Integrations menu' do
|
||||
let(:item_id) { :integrations }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
|
||||
describe 'Projects menu' do
|
||||
let(:item_id) { :group_projects }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
|
||||
describe 'Repository menu' do
|
||||
let(:item_id) { :repository }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
|
||||
describe 'CI/CD menu' do
|
||||
let(:item_id) { :ci_cd }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
|
||||
describe 'Applications menu' do
|
||||
let(:item_id) { :applications }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
|
||||
describe 'Packages & Registries' do
|
||||
let(:item_id) { :packages_and_registries }
|
||||
|
||||
before do
|
||||
allow(group).to receive(:packages_feature_enabled?).and_return(packages_enabled)
|
||||
end
|
||||
|
||||
describe 'when packages feature is disabled' do
|
||||
let(:packages_enabled) { false }
|
||||
|
||||
specify { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
describe 'when packages feature is enabled' do
|
||||
let(:packages_enabled) { true }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2621,6 +2621,7 @@ RSpec.describe Ci::Build do
|
|||
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false },
|
||||
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false },
|
||||
{ key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false },
|
||||
{ key: 'CI_PROJECT_CLASSIFICATION_LABEL', value: project.external_authorization_classification_label, public: true, masked: false },
|
||||
{ key: 'CI_DEFAULT_BRANCH', value: project.default_branch, public: true, masked: false },
|
||||
{ key: 'CI_CONFIG_PATH', value: project.ci_config_path_or_default, public: true, masked: false },
|
||||
{ key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true, masked: false },
|
||||
|
|
|
|||
|
|
@ -134,4 +134,54 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
|
|||
expect(rendered).to have_link('Dependency Proxy', href: group_dependency_proxy_path(group))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Settings' do
|
||||
it 'default link points to edit group page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Settings', href: edit_group_path(group))
|
||||
end
|
||||
|
||||
it 'has a link to the General settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('General', href: edit_group_path(group))
|
||||
end
|
||||
|
||||
it 'has a link to the Integrations settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Integrations', href: group_settings_integrations_path(group))
|
||||
end
|
||||
|
||||
it 'has a link to the group Projects settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Projects', href: projects_group_path(group))
|
||||
end
|
||||
|
||||
it 'has a link to the Repository settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Repository', href: group_settings_repository_path(group))
|
||||
end
|
||||
|
||||
it 'has a link to the CI/CD settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('CI/CD', href: group_settings_ci_cd_path(group))
|
||||
end
|
||||
|
||||
it 'has a link to the Applications settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Applications', href: group_settings_applications_path(group))
|
||||
end
|
||||
|
||||
it 'has a link to the Package & Registries settings page' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_link('Packages & Registries', href: group_settings_packages_and_registries_path(group))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue