Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-08-17 15:10:19 +00:00
parent b487021bd3
commit 20dbd96076
42 changed files with 847 additions and 493 deletions

View File

@ -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

View File

@ -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()) {

View File

@ -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) => {

View File

@ -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();

View File

@ -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;

View File

@ -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>

View File

@ -34,6 +34,10 @@
@include gl-py-4;
@include gl-w-full;
}
.gl-source-editor {
@include gl-order-n1;
}
}
.monaco-editor.gl-source-editor {

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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).

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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. |

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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)**

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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 }) => {

View File

@ -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', () => {

View File

@ -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

View File

@ -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 },

View File

@ -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