Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-01-29 21:09:34 +00:00
parent 2516f0d87b
commit 37a1347df2
85 changed files with 1396 additions and 127 deletions

View file

@ -83,6 +83,9 @@ const Api = {
featureFlagUserList: '/api/:version/projects/:id/feature_flags_user_lists/:list_iid',
billableGroupMembersPath: '/api/:version/groups/:id/billable_members',
containerRegistryDetailsPath: '/api/:version/registry/repositories/:id/',
projectNotificationSettingsPath: '/api/:version/projects/:id/notification_settings',
groupNotificationSettingsPath: '/api/:version/groups/:id/notification_settings',
notificationSettingsPath: '/api/:version/notification_settings',
group(groupId, callback = () => {}) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
@ -906,6 +909,34 @@ const Api = {
return { data, headers };
});
},
async updateNotificationSettings(projectId, groupId, data = {}) {
let url = Api.buildUrl(this.notificationSettingsPath);
if (projectId) {
url = Api.buildUrl(this.projectNotificationSettingsPath).replace(':id', projectId);
} else if (groupId) {
url = Api.buildUrl(this.groupNotificationSettingsPath).replace(':id', groupId);
}
const result = await axios.put(url, data);
return result;
},
async getNotificationSettings(projectId, groupId) {
let url = Api.buildUrl(this.notificationSettingsPath);
if (projectId) {
url = Api.buildUrl(this.projectNotificationSettingsPath).replace(':id', projectId);
} else if (groupId) {
url = Api.buildUrl(this.groupNotificationSettingsPath).replace(':id', groupId);
}
const result = await axios.get(url);
return result;
},
};
export default Api;

View file

@ -0,0 +1,169 @@
<script>
import {
GlButtonGroup,
GlButton,
GlDropdown,
GlDropdownDivider,
GlTooltipDirective,
} from '@gitlab/ui';
import { sprintf } from '~/locale';
import Api from '~/api';
import NotificationsDropdownItem from './notifications_dropdown_item.vue';
import { CUSTOM_LEVEL, i18n } from '../constants';
export default {
name: 'NotificationsDropdown',
components: {
GlButtonGroup,
GlButton,
GlDropdown,
GlDropdownDivider,
NotificationsDropdownItem,
},
directives: {
GlTooltip: GlTooltipDirective,
},
inject: {
containerClass: {
default: '',
},
disabled: {
default: false,
},
dropdownItems: {
default: [],
},
buttonSize: {
default: 'medium',
},
initialNotificationLevel: {
default: '',
},
projectId: {
default: null,
},
groupId: {
default: null,
},
},
data() {
return {
selectedNotificationLevel: this.initialNotificationLevel,
isLoading: false,
};
},
computed: {
notificationLevels() {
return this.dropdownItems.map((level) => ({
level,
title: this.$options.i18n.notificationTitles[level] || '',
description: this.$options.i18n.notificationDescriptions[level] || '',
}));
},
isCustomNotification() {
return this.selectedNotificationLevel === CUSTOM_LEVEL;
},
buttonIcon() {
if (this.isLoading) {
return null;
}
return this.selectedNotificationLevel === 'disabled' ? 'notifications-off' : 'notifications';
},
buttonTooltip() {
const notificationTitle =
this.$options.i18n.notificationTitles[this.selectedNotificationLevel] ||
this.selectedNotificationLevel;
return this.disabled
? this.$options.i18n.notificationDescriptions.owner_disabled
: sprintf(this.$options.i18n.notificationTooltipTitle, {
notification_title: notificationTitle,
});
},
},
methods: {
selectItem(level) {
if (level !== this.selectedNotificationLevel) {
this.updateNotificationLevel(level);
}
},
async updateNotificationLevel(level) {
this.isLoading = true;
try {
await Api.updateNotificationSettings(this.projectId, this.groupId, { level });
this.selectedNotificationLevel = level;
} catch (error) {
this.$toast.show(this.$options.i18n.updateNotificationLevelErrorMessage, { type: 'error' });
} finally {
this.isLoading = false;
}
},
},
customLevel: CUSTOM_LEVEL,
i18n,
};
</script>
<template>
<div :class="containerClass">
<gl-button-group
v-if="isCustomNotification"
v-gl-tooltip="{ title: buttonTooltip }"
data-testid="notificationButton"
:size="buttonSize"
>
<gl-button :size="buttonSize" :icon="buttonIcon" :loading="isLoading" :disabled="disabled" />
<gl-dropdown :size="buttonSize" :disabled="disabled">
<notifications-dropdown-item
v-for="item in notificationLevels"
:key="item.level"
:level="item.level"
:title="item.title"
:description="item.description"
:notification-level="selectedNotificationLevel"
@item-selected="selectItem"
/>
<gl-dropdown-divider />
<notifications-dropdown-item
:key="$options.customLevel"
:level="$options.customLevel"
:title="$options.i18n.notificationTitles.custom"
:description="$options.i18n.notificationDescriptions.custom"
:notification-level="selectedNotificationLevel"
@item-selected="selectItem"
/>
</gl-dropdown>
</gl-button-group>
<gl-dropdown
v-else
v-gl-tooltip="{ title: buttonTooltip }"
data-testid="notificationButton"
:icon="buttonIcon"
:loading="isLoading"
:size="buttonSize"
:disabled="disabled"
>
<notifications-dropdown-item
v-for="item in notificationLevels"
:key="item.level"
:level="item.level"
:title="item.title"
:description="item.description"
:notification-level="selectedNotificationLevel"
@item-selected="selectItem"
/>
<gl-dropdown-divider />
<notifications-dropdown-item
:key="$options.customLevel"
:level="$options.customLevel"
:title="$options.i18n.notificationTitles.custom"
:description="$options.i18n.notificationDescriptions.custom"
:notification-level="selectedNotificationLevel"
@item-selected="selectItem"
/>
</gl-dropdown>
</div>
</template>

View file

@ -0,0 +1,42 @@
<script>
import { GlDropdownItem } from '@gitlab/ui';
export default {
name: 'NotificationsDropdownItem',
components: {
GlDropdownItem,
},
props: {
level: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
notificationLevel: {
type: String,
required: true,
},
},
computed: {
isActive() {
return this.notificationLevel === this.level;
},
},
};
</script>
<template>
<gl-dropdown-item is-check-item :is-checked="isActive" @click="$emit('item-selected', level)">
<div class="gl-display-flex gl-flex-direction-column">
<span class="gl-font-weight-bold">{{ title }}</span>
<span class="gl-text-gray-500">{{ description }}</span>
</div>
</gl-dropdown-item>
</template>

View file

@ -0,0 +1,27 @@
import { __, s__ } from '~/locale';
export const CUSTOM_LEVEL = 'custom';
export const i18n = {
notificationTitles: {
participating: s__('NotificationLevel|Participate'),
mention: s__('NotificationLevel|On mention'),
watch: s__('NotificationLevel|Watch'),
global: s__('NotificationLevel|Global'),
disabled: s__('NotificationLevel|Disabled'),
custom: s__('NotificationLevel|Custom'),
},
notificationTooltipTitle: __('Notification setting - %{notification_title}'),
notificationDescriptions: {
participating: __('You will only receive notifications for threads you have participated in'),
mention: __('You will receive notifications only for comments in which you were @mentioned'),
watch: __('You will receive notifications for any activity'),
disabled: __('You will not get any notifications via email'),
global: __('Use your global notification setting'),
custom: __('You will only receive notifications for the events you choose'),
owner_disabled: __('Notifications have been disabled by the project or group owner'),
},
updateNotificationLevelErrorMessage: __(
'An error occured while updating the notification settings. Please try again.',
),
};

View file

@ -0,0 +1,38 @@
import Vue from 'vue';
import { GlToast } from '@gitlab/ui';
import { parseBoolean } from '~/lib/utils/common_utils';
import NotificationsDropdown from './components/notifications_dropdown.vue';
Vue.use(GlToast);
export default () => {
const el = document.querySelector('.js-vue-notification-dropdown');
if (!el) return false;
const {
containerClass,
buttonSize,
disabled,
dropdownItems,
notificationLevel,
projectId,
groupId,
} = el.dataset;
return new Vue({
el,
provide: {
containerClass,
buttonSize,
disabled: parseBoolean(disabled),
dropdownItems: JSON.parse(dropdownItems),
initialNotificationLevel: notificationLevel,
projectId,
groupId,
},
render(h) {
return h(NotificationsDropdown);
},
});
};

View file

@ -12,6 +12,7 @@ import notificationsDropdown from '../../../notifications_dropdown';
import { showLearnGitLabProjectPopover } from '~/onboarding_issues';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initVueNotificationsDropdown from '~/notifications';
initReadMore();
new Star(); // eslint-disable-line no-new
@ -42,7 +43,14 @@ leaveByUrl('project');
showLearnGitLabProjectPopover();
notificationsDropdown();
if (gon.features?.vueNotificationDropdown) {
initVueNotificationsDropdown();
} else {
notificationsDropdown();
}
initVueNotificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new
initInviteMembersTrigger();

View file

@ -31,6 +31,10 @@ class ProjectsController < Projects::ApplicationController
# Project Export Rate Limit
before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export]
before_action do
push_frontend_feature_flag(:vue_notification_dropdown, @project, default_enabled: :yaml)
end
before_action only: [:edit] do
push_frontend_feature_flag(:approval_suggestions, @project, default_enabled: true)
push_frontend_feature_flag(:allow_editing_commit_messages, @project)

View file

@ -125,4 +125,13 @@ module NotificationsHelper
def can_read_project?(project)
can?(current_user, :read_project, project)
end
def notification_dropdown_items(notification_setting)
NotificationSetting.levels.each_key.map do |level|
next if level == "custom"
next if level == "global" && notification_setting.source.nil?
level
end.compact
end
end

View file

@ -30,6 +30,9 @@ class ProjectPolicy < BasePolicy
desc "User has maintainer access"
condition(:maintainer) { team_access_level >= Gitlab::Access::MAINTAINER }
desc "User is a project bot"
condition(:project_bot) { user.project_bot? && team_member? }
desc "Project is public"
condition(:public_project, scope: :subject, score: 0) { project.public? }
@ -616,10 +619,14 @@ class ProjectPolicy < BasePolicy
prevent :read_project
end
rule { project_bot }.enable :project_bot_access
rule { resource_access_token_available & can?(:admin_project) }.policy do
enable :admin_resource_access_tokens
end
rule { can?(:project_bot_access) }.prevent :admin_resource_access_tokens
rule { user_defined_variables_allowed | can?(:maintainer_access) }.policy do
enable :set_pipeline_variables
end

View file

@ -17,6 +17,7 @@ module Users
user.accept_pending_invitations! if user.active_for_authentication?
DeviseMailer.user_admin_approval(user).deliver_later
log_event(user)
after_approve_hook(user)
success(message: 'Success', http_status: :created)
else
@ -39,6 +40,10 @@ module Users
def approval_required?(user)
user.blocked_pending_approval?
end
def log_event(user)
Gitlab::AppLogger.info(message: "User instance access request approved", user: "#{user.username}", email: "#{user.email}", approved_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
end
end

View file

@ -16,6 +16,8 @@ module Users
NotificationService.new.user_admin_rejection(user.name, user.email)
log_event(user)
success
end
@ -30,6 +32,10 @@ module Users
def after_reject_hook(user)
# overridden by EE module
end
def log_event(user)
Gitlab::AppLogger.info(message: "User instance access request rejected", user: "#{user.username}", email: "#{user.email}", rejected_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
end
end

View file

@ -46,7 +46,11 @@
.project-repo-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
- if current_user
.d-inline-flex
= render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn-xs', dropdown_container_class: 'gl-mr-3', emails_disabled: emails_disabled
- if Feature.enabled?(:vue_notification_dropdown, @project, default_enabled: :yaml)
- if @notification_setting
.js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, container_class: 'gl-mr-3 gl-mt-5 gl-vertical-align-top' } }
- else
= render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn-xs', dropdown_container_class: 'gl-mr-3', emails_disabled: emails_disabled
.count-buttons.d-inline-flex
= render 'projects/buttons/star'

View file

@ -0,0 +1,5 @@
---
title: Log user approval/rejection in application logs
merge_request: 51768
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Project access token management via API
merge_request: 52139
author:
type: added

View file

@ -0,0 +1,8 @@
---
name: vue_notification_dropdown
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52068
rollout_issue_url:
milestone: '13.8'
type: development
group: group::optimize
default_enabled: false

View file

@ -12,6 +12,7 @@ Ansible
Anthos
approvers
architected
architecting
Arel
Artifactory
Asana
@ -60,6 +61,10 @@ boolean
booleans
Bootsnap
browsable
bugfix
bugfixed
bugfixes
bugfixing
Bugzilla
Buildkite
buildpack
@ -353,6 +358,7 @@ onboarding
OpenID
OpenShift
Opsgenie
Overcommit
Packagist
parallelization
parallelizations
@ -404,6 +410,7 @@ pseudocode
pseudonymized
pseudonymizer
Puma
pytest
Python
Qualys
queryable
@ -434,6 +441,8 @@ referer
referers
reflog
reflogs
refspec
refspecs
reindex
reindexed
reindexes
@ -499,6 +508,7 @@ Silverlight
Sisense
Sitespeed
Slack
Slackbot
Slony
smartcard
smartcards
@ -541,11 +551,15 @@ subquery
subquerying
substring
substrings
subtask
subtasks
subtree
subtrees
sudo
supercookie
supercookies
supertype
supertypes
swappiness
swimlane
swimlanes
@ -668,6 +682,7 @@ unverify
unverifying
uploader
uploaders
upstreams
upvoted
upvotes
URIs

View file

@ -15,9 +15,9 @@ relevant compliance standards.
|Feature |GitLab tier |GitLab.com | Product level |
| ---------| :--------: | :-------: | :-----------: |
|**[Restrict SSH Keys](../security/ssh_keys_restrictions.md)**<br>Control the technology and key length of SSH keys used to access GitLab|Core+||Instance|
|**[Granular user roles and flexible permissions](../user/permissions.md)**<br>Manage access and permissions with five different user roles and settings for external users. Set permissions according to people's role, rather than either read or write access to a repository. Don't share the source code with people that only need access to the issue tracker.|Core+|✓|Instance, Group, Project|
|**[Enforce TOS acceptance](../user/admin_area/settings/terms.md)**<br>Enforce your users accepting new terms of service by blocking GitLab traffic.|Core+||Instance|
|**[Restrict SSH Keys](../security/ssh_keys_restrictions.md)**<br>Control the technology and key length of SSH keys used to access GitLab|Free+||Instance|
|**[Granular user roles and flexible permissions](../user/permissions.md)**<br>Manage access and permissions with five different user roles and settings for external users. Set permissions according to people's role, rather than either read or write access to a repository. Don't share the source code with people that only need access to the issue tracker.|Free+|✓|Instance, Group, Project|
|**[Enforce TOS acceptance](../user/admin_area/settings/terms.md)**<br>Enforce your users accepting new terms of service by blocking GitLab traffic.|Free+||Instance|
|**[Email all users of a project, group, or entire server](../tools/email.md)**<br>An admin can email groups of users based on project or group membership, or email everyone using the GitLab instance. This is great for scheduled maintenance or upgrades.|Starter+|||Instance
|**[Omnibus package supports log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-forwarding)**<br>Forward your logs to a central system.|Starter+||Instance|
|**[Lock project membership to group](../user/group/index.md#member-lock)**<br>Group owners can prevent new members from being added to projects within a group.|Starter+|✓|Group|

View file

@ -10,7 +10,7 @@ type: reference
[Gitaly](index.md), the service that provides storage for Git repositories, can
be run in a clustered configuration to increase fault tolerance. In this
configuration, every Git repository is stored on every Gitaly node in the
cluster. Multiple clusters (or shards) can be configured.
cluster. Multiple clusters (or storage shards) can be configured.
NOTE:
Technical support for Gitaly clusters is limited to GitLab Premium and Ultimate
@ -21,7 +21,7 @@ component for running a Gitaly Cluster.
![Architecture diagram](img/praefect_architecture_v12_10.png)
Using a Gitaly Cluster increase fault tolerance by:
Using a Gitaly Cluster increases fault tolerance by:
- Replicating write operations to warm standby Gitaly nodes.
- Detecting Gitaly node failures.
@ -53,7 +53,7 @@ Gitaly Cluster supports:
- Reporting of possible data loss if replication queue is non-empty.
- Marking repositories as [read only](#read-only-mode) if data loss is detected to prevent data inconsistencies.
Follow the [HA Gitaly epic](https://gitlab.com/groups/gitlab-org/-/epics/1489)
Follow the [Gitaly Cluster epic](https://gitlab.com/groups/gitlab-org/-/epics/1489)
for improvements including
[horizontally distributing reads](https://gitlab.com/groups/gitlab-org/-/epics/2013).
@ -80,23 +80,65 @@ For more information, see:
- [Gitaly architecture](index.md#architecture).
- Geo [use cases](../geo/index.md#use-cases) and [architecture](../geo/index.md#architecture).
## Cluster or shard
## Where Gitaly Cluster fits
GitLab accesses [repositories](../../user/project/repository/index.md) through the configured
[repository storages](../repository_storage_paths.md). Each new repository is stored on one of the
repository storages based on their configured weights. Each repository storage is either:
- A Gitaly storage served directly by Gitaly. These map to a directory on the file system of a
Gitaly node.
- A [virtual storage](#virtual-storage-or-direct-gitaly-storage) served by Praefect. A virtual
storage is a cluster of Gitaly storages that appear as a single repository storage.
Virtual storages are a feature of Gitaly Cluster. They support replicating the repositories to
multiple storages for fault tolerance. Virtual storages can improve performance by distributing
requests across Gitaly nodes. Their distributed nature makes it viable to have a single repository
storage in GitLab to simplify repository management.
## Components of Gitaly Cluster
Gitaly Cluster consists of multiple components:
- [Load balancer](#load-balancer) for distributing requests and providing fault-tolerant access to
Praefect nodes.
- [Praefect](#praefect) nodes for managing the cluster and routing requests to Gitaly nodes.
- [PostgreSQL database](#postgresql) for persisting cluster metadata and [PgBouncer](#pgbouncer),
recommended for pooling Praefect's database connections.
- [Gitaly](index.md) nodes to provide repository storage and Git access.
![Cluster example](img/cluster_example_v13_3.png)
In this example:
- Repositories are stored on a virtual storage called `storage-1`.
- Three Gitaly nodes provide `storage-1` access: `gitaly-1`, `gitaly-2`, and `gitaly-3`.
- The three Gitaly nodes store data on their filesystems.
### Virtual storage or direct Gitaly storage
Gitaly supports multiple models of scaling:
- Clustering using Gitaly Cluster, where each repository is stored on multiple Gitaly nodes in the
cluster. Read requests are distributed between repository replicas and write requests are
broadcast to repository replicas.
- Sharding using [repository storage paths](../repository_storage_paths.md), where each repository
is stored on the assigned Gitaly node. All requests are routed to this node.
broadcast to repository replicas. GitLab accesses virtual storage.
- Direct access to Gitaly storage using [repository storage paths](../repository_storage_paths.md),
where each repository is stored on the assigned Gitaly node. All requests are routed to this node.
| Cluster | Shard |
|:--------------------------------------------------|:----------------------------------------------|
| ![Cluster example](img/cluster_example_v13_3.png) | ![Shard example](img/shard_example_v13_3.png) |
The following is Gitaly set up to use direct access to Gitaly instead of Gitaly Cluster:
Generally, Gitaly Cluster can replace sharded configurations, at the expense of additional storage
needed to store each repository on multiple Gitaly nodes. The benefit of using Gitaly Cluster over
sharding is:
![Shard example](img/shard_example_v13_3.png)
In this example:
- Each repository is stored on one of three Gitaly storages: `storage-1`, `storage-2`,
or `storage-3`.
- Each storage is serviced by a Gitaly node.
- The three Gitaly nodes store data in three separate hashed storage locations.
Generally, virtual storage with Gitaly Cluster can replace direct Gitaly storage configurations, at
the expense of additional storage needed to store each repository on multiple Gitaly nodes. The
benefit of using Gitaly Cluster over direct Gitaly storage is:
- Improved fault tolerance, because each Gitaly node has a copy of every repository.
- Improved resource utilization, reducing the need for over-provisioning for shard-specific peak
@ -773,7 +815,7 @@ configuration.
### Load Balancer
In a highly available Gitaly configuration, a load balancer is needed to route
In a fault-tolerant Gitaly configuration, a load balancer is needed to route
internal traffic from the GitLab application to the Praefect nodes. The
specifics on which load balancer to use or the exact configuration is beyond the
scope of the GitLab documentation.
@ -785,7 +827,7 @@ addition to the GitLab nodes. Some requests handled by
process. `gitaly-ruby` uses the Gitaly address set in the GitLab server's
`git_data_dirs` setting to make this connection.
We hope that if youre managing HA systems like GitLab, you have a load balancer
We hope that if youre managing fault-tolerant systems like GitLab, you have a load balancer
of choice already. Some examples include [HAProxy](https://www.haproxy.org/)
(open-source), [Google Internal Load Balancer](https://cloud.google.com/load-balancing/docs/internal/),
[AWS Elastic Load Balancer](https://aws.amazon.com/elasticloadbalancing/), F5
@ -974,7 +1016,7 @@ To get started quickly:
1. Go to **Explore** and query `gitlab_build_info` to verify that you are
getting metrics from all your machines.
Congratulations! You've configured an observable highly available Praefect
Congratulations! You've configured an observable fault-tolerant Praefect
cluster.
## Distributed reads

View file

@ -17,7 +17,7 @@ GitLab has two product distributions available through [different subscriptions]
You can [install either GitLab CE or GitLab EE](https://about.gitlab.com/install/ce-or-ee/).
However, the features you have access to depend on your chosen [subscription](https://about.gitlab.com/pricing/).
GitLab Community Edition installations have access only to Core features.
GitLab Community Edition installations have access only to Free features.
Non-administrator users can't access GitLab administration tools and settings.

View file

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Instance Review
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6995) in [GitLab Core](https://about.gitlab.com/pricing/) 11.3.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6995) in [GitLab Free](https://about.gitlab.com/pricing/) 11.3.
If you run a medium-sized self-managed instance (50+ users) of a free version of GitLab,
[either Community Edition or unlicensed Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/),

View file

@ -1,3 +1,9 @@
---
stage: Create
group: Ecosystem
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
---
# Kroki diagrams **(FREE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241744) in GitLab 13.7.

View file

@ -91,7 +91,7 @@ _The artifacts are stored by default in
> [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
> - Since version 9.5, artifacts are [browsable](../ci/pipelines/job_artifacts.md#browsing-artifacts),
> when object storage is enabled. 9.4 lacks this feature.
> - Since version 10.6, available in [GitLab Core](https://about.gitlab.com/pricing/)
> - Since version 10.6, available in [GitLab Free](https://about.gitlab.com/pricing/).
> - Since version 11.0, we support `direct_upload` to S3.
If you don't want to use the local disk where GitLab is installed to store the

View file

@ -27,7 +27,7 @@ can be started.
## Start multiple processes
> - [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4006) in GitLab 12.10, starting multiple processes with Sidekiq cluster.
> - [Sidekiq cluster moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab [Core](https://about.gitlab.com/pricing/#self-managed) in GitLab 12.10.
> - [Sidekiq cluster moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab [Free](https://about.gitlab.com/pricing/) in GitLab 12.10.
> - [Sidekiq cluster became default](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4140) in GitLab 13.0.
To start multiple processes:
@ -113,7 +113,7 @@ you list:
## Queue selector
> - [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/45) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.8.
> - [Sidekiq cluster including queue selector moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab [Core](https://about.gitlab.com/pricing/#self-managed) in GitLab 12.10.
> - [Sidekiq cluster including queue selector moved](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/181) to GitLab [Free](https://about.gitlab.com/pricing/) in GitLab 12.10.
> - [Renamed from `experimental_queue_selector` to `queue_selector`](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/147) in GitLab 13.6.
In addition to selecting queues by name, as above, the `queue_selector`

View file

@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab Dependency Proxy administration
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Free](https://about.gitlab.com/pricing/) in GitLab 13.6.
GitLab can be used as a dependency proxy for a variety of common package managers.

View file

@ -161,7 +161,8 @@ When avatar is replaced, `Upload` model is destroyed and a new one takes place w
#### CI artifacts
CI Artifacts are S3 compatible since **9.4** (GitLab Premium), and available in GitLab Core since **10.6**.
CI Artifacts are S3 compatible since **9.4** (GitLab Premium), and available in GitLab Free since
**10.6**.
#### LFS objects

View file

@ -70,7 +70,7 @@ and they will assist you with any issues you are having.
kubectl logs <pod-name> --previous
```
No logs are kept in the containers/pods themselves. Everything is written to stdout.
No logs are kept in the containers/pods themselves. Everything is written to `stdout`.
This is the principle of Kubernetes, read [Twelve-factor app](https://12factor.net/)
for details.

View file

@ -54,7 +54,7 @@ _The uploads are stored by default in
> **Notes:**
>
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3867) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.5.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17358) in [GitLab Core](https://about.gitlab.com/pricing/) 10.7.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17358) in [GitLab Free](https://about.gitlab.com/pricing/) 10.7.
> - Since version 11.1, we support direct_upload to S3.
If you don't want to use the local disk where GitLab is installed to store the

View file

@ -124,7 +124,7 @@ There are several methods you can use to authenticate with the GitLab API:
- [GitLab CI/CD job token](#gitlab-ci-job-token) **(Specific endpoints only)**
NOTE:
Project access tokens are supported for self-managed instances on Core and
Project access tokens are supported for self-managed instances on Free and
higher. They're also supported on GitLab.com Bronze and higher.
For administrators who want to authenticate with the API as a specific user, or who want

View file

@ -25,6 +25,7 @@ The following API resources are available in the project context:
| Resource | Available endpoints |
|:--------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Access requests](access_requests.md) | `/projects/:id/access_requests` (also available for groups) |
| [Access tokens](resource_access_tokens.md) | `/projects/:id/access_tokens` |
| [Award emoji](award_emoji.md) | `/projects/:id/issues/.../award_emoji`, `/projects/:id/merge_requests/.../award_emoji`, `/projects/:id/snippets/.../award_emoji` |
| [Branches](branches.md) | `/projects/:id/repository/branches/`, `/projects/:id/repository/merged_branches` |
| [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` |
@ -76,7 +77,7 @@ The following API resources are available in the project context:
| [Remote mirrors](remote_mirrors.md) | `/projects/:id/remote_mirrors` |
| [Repositories](repositories.md) | `/projects/:id/repository` |
| [Repository files](repository_files.md) | `/projects/:id/repository/files` |
| [Repository submodules](repository_submodules.md) | `/projects/:id/repository/submodules` |
| [Repository submodules](repository_submodules.md) | `/projects/:id/repository/submodules` |
| [Resource label events](resource_label_events.md) | `/projects/:id/issues/.../resource_label_events`, `/projects/:id/merge_requests/.../resource_label_events` (also available for groups) |
| [Runners](runners.md) | `/projects/:id/runners` (also available standalone) |
| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |

View file

@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Purge the dependency proxy for a group
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Free](https://about.gitlab.com/pricing/) in GitLab 13.6.
Deletes the cached manifests and blobs for a group. This endpoint requires group admin access.

View file

@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Feature flag user lists API **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/205409) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.10.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to GitLab Core in 13.5.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to GitLab Free in 13.5.
API for accessing GitLab Feature Flag User Lists.

View file

@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9566) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.5.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Starter](https://about.gitlab.com/pricing/) in 13.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.5.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.5.
API for accessing resources of [GitLab Feature Flags](../operations/feature_flags.md).

View file

@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9566) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.5.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Starter](https://about.gitlab.com/pricing/) in 13.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.5.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.5.
WARNING:
This API is deprecated and [scheduled for removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213369). Use [this API](feature_flags.md) instead.

View file

@ -1,3 +1,9 @@
---
stage: Plan
group: Product Planning
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
---
# GraphQL API removed items
GraphQL is a versionless API, unlike the REST API.

View file

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Issue links API **(FREE)**
> The simple "relates to" relationship [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212329) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.4.
> The simple "relates to" relationship [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212329) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.4.
## List issue relations

View file

@ -11,7 +11,7 @@ You can read more about [personal access tokens](../user/profile/personal_access
## List personal access tokens
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227264) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.6.
Get a list of personal access tokens.
@ -71,7 +71,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
## Revoke a personal access token
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216004) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) to [GitLab Free](https://about.gitlab.com/pricing/) in 13.6.
Revoke a personal access token.

View file

@ -0,0 +1,107 @@
---
stage: Manage
group: Access
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
---
# Project access tokens API
You can read more about [project access tokens](../user/project/settings/project_access_tokens.md).
## List project access tokens
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238991) in GitLab 13.9.
Get a list of project access tokens.
```plaintext
GET /:id/access_tokens
```
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer/string | yes | The ID of the project |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<project_id>/access_tokens"
```
```json
[
{
"user_id" : 141,
"scopes" : [
"api"
],
"name" : "token",
"expires_at" : "2021-01-31",
"id" : 42,
"active" : true,
"created_at" : "2021-01-20T22:11:48.151Z",
"revoked" : false
}
]
```
## Create a project access token
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238991) in GitLab 13.9.
Create a project access token.
```plaintext
POST /:id/access_tokens
```
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
| `name` | String | yes | The name of the project access token |
| `scopes` | Array[String] | yes | [List of scopes](../user/project/settings/project_access_tokens.md#limiting-scopes-of-a-project-access-token) |
| `expires_at` | Date | no | The token expires at midnight UTC on that date |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
--header "Content-Type:application/json" \
--data '{ "name":"test_token", "scopes":["api", "read_repository"], "expires_at":"2021-01-31" }' \
"https://gitlab.example.com/api/v4/projects/<project_id>/access_tokens"
```
```json
{
"scopes" : [
"api",
"read_repository"
],
"active" : true,
"name" : "test",
"revoked" : false,
"created_at" : "2021-01-21T19:35:37.921Z",
"user_id" : 166,
"id" : 58,
"expires_at" : "2021-01-31"
}
```
## Revoke a project access token
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238991) in GitLab 13.9.
Revoke a project access token.
```plaintext
DELETE /:id/access_tokens/:token_id
```
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer/string | yes | The ID of the project |
| `token_id` | integer/string | yes | The ID of the project access token |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<project_id>/access_tokens/<token_id>"
```
### Responses
- `204: No Content` if successfully revoked.
- `400 Bad Request` or `404 Not Found` if not revoked successfully.

View file

@ -71,7 +71,7 @@ available through the UI. You can use them by creating a new file,
choosing a template that suits your application, and adjusting it
to your needs:
![Use a `.gitlab-ci.yml` template](img/add_file_template_11_10.png)
![Use a YAML template](img/add_file_template_11_10.png)
While building your `.gitlab-ci.yml`, you can use the [CI/CD configuration visualization](pipeline_editor/index.md#visualize-ci-configuration) to facilitate your writing experience.

View file

@ -5,10 +5,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: index, concepts, howto
---
# GitLab ChatOps **(CORE)**
# GitLab ChatOps **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24780) to [GitLab Free](https://about.gitlab.com/pricing/) in 11.9.
GitLab ChatOps provides a method to interact with CI/CD jobs through chat services
like Slack. Many organizations' discussion, collaboration, and troubleshooting takes

View file

@ -14,8 +14,9 @@ GitLab CI/CD can be used with Bitbucket Cloud by:
To use GitLab CI/CD with a Bitbucket Cloud repository:
1. In GitLab create a **CI/CD for external repository**, select **Repo by URL** and
create the project.
1. <!-- vale gitlab.Spelling = NO --> In GitLab create a **CI/CD for external repository**, select
**Repo by URL** and create the project.
<!-- vale gitlab.Spelling = YES -->
![Create project](img/external_repository.png)

View file

@ -25,11 +25,15 @@ snippets disabled. These features
To connect to an external repository:
<!-- vale gitlab.Spelling = NO -->
1. From your GitLab dashboard, click **New project**.
1. Switch to the **CI/CD for external repository** tab.
1. Choose **GitHub** or **Repo by URL**.
1. The next steps are similar to the [import flow](../../user/project/import/index.md).
<!-- vale gitlab.Spelling = YES -->
![CI/CD for external repository project creation](img/ci_cd_for_external_repo.png)
## Pipelines for external pull requests

View file

@ -126,7 +126,7 @@ not without its own challenges:
instance of Docker engine so they don't conflict with each other. But this
also means that jobs can be slower because there's no caching of layers.
- By default, Docker 17.09 and higher uses `--storage-driver overlay2` which is
the recommended storage driver. See [Using the overlayfs driver](#use-the-overlayfs-driver)
the recommended storage driver. See [Using the OverlayFS driver](#use-the-overlayfs-driver)
for details.
- Since the `docker:19.03.12-dind` container and the runner container don't share their
root file system, the job's working directory can be used as a mount point for

View file

@ -36,7 +36,7 @@ environments are not displayed.
To add a project to the dashboard:
1. Click the **Add projects** button in the homescreen of the dashboard.
1. Click the **Add projects** button in the home screen of the dashboard.
1. Search and add one or more projects using the **Search your projects** field.
1. Click the **Add projects** button.

View file

@ -1009,7 +1009,7 @@ fetch = +refs/environments/*:refs/remotes/origin/environments/*
### Scoping environments with specs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2112) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
> - [Scoping for environment variables was moved to Core](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30779) in GitLab 12.2.
> - [Scoping for environment variables was moved to Free](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30779) in GitLab 12.2.
You can limit the environment scope of a variable by
defining which environments it can be available for.

View file

@ -4,8 +4,10 @@ group: unassigned
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
---
<!-- markdownlint-disable MD044 -->
<!-- vale gitlab.Spelling = NO -->
# Validate .gitlab-ci.yml syntax with the CI Lint tool
<!-- markdownlint-enable MD044 -->
<!-- vale gitlab.Spelling = YES -->
If you want to test the validity of your GitLab CI/CD configuration before committing
the changes, you can use the CI Lint tool. This tool checks for syntax and logical

View file

@ -261,7 +261,7 @@ test:
### Mirroring status from triggered pipeline
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11238) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.3.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Core in 12.8.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
You can mirror the pipeline status from the triggered pipeline to the source
bridge job by using `strategy: depend`. For example:

View file

@ -156,7 +156,11 @@ build a matrix of targets and architectures.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Create child pipelines using dynamically generated configurations](https://youtu.be/nMdfus2JWHM).
We also have an [example project using Dynamic Child Pipelines with Jsonnet](https://gitlab.com/gitlab-org/project-templates/jsonnet) which shows how to use a data templating language to generate your `.gitlab-ci.yml` at runtime. You could use a similar process for other templating languages like [Dhall](https://dhall-lang.org/) or [`ytt`](https://get-ytt.io/).
<!-- vale gitlab.Spelling = NO -->
We also have an example project using
[Dynamic Child Pipelines with Jsonnet](https://gitlab.com/gitlab-org/project-templates/jsonnet)
which shows how to use a data templating language to generate your `.gitlab-ci.yml` at runtime. You could use a similar process for other templating languages like [Dhall](https://dhall-lang.org/) or [`ytt`](https://get-ytt.io/).
<!-- vale gitlab.Spelling = NO -->
The artifact path is parsed by GitLab, not the runner, so the path must match the
syntax for the OS running GitLab. If GitLab is running on Linux but using a Windows

View file

@ -131,6 +131,8 @@ averaged.
![Build status coverage](img/pipelines_test_coverage_build.png)
<!-- vale gitlab.Spelling = NO -->
| Coverage Tool | Sample regular expression |
|------------------------------------------------|---------------------------|
| Simplecov (Ruby) | `\(\d+.\d+\%\) covered` |
@ -145,6 +147,8 @@ averaged.
| JaCoCo (Java/Kotlin) | `Total.*?([0-9]{1,3})%` |
| `go test -cover` (Go) | `coverage: \d+.\d+% of statements` |
<!-- vale gitlab.Spelling = YES -->
### Code Coverage history
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/209121) the ability to download a `.csv` in GitLab 12.10.
@ -154,7 +158,7 @@ To see the evolution of your project code coverage over time,
you can view a graph or download a CSV file with this data. From your project:
1. Go to **{chart}** **Project Analytics > Repository** to see the historic data for each job listed in the dropdown above the graph.
1. If you want a CSV file of that data, click **Download raw data (.csv)**
1. If you want a CSV file of that data, click **Download raw data (`.csv`)**
![Code coverage graph of a project over time](img/code_coverage_graph_v13_1.png)

View file

@ -303,7 +303,7 @@ test:
artifacts:
when: always
reports:
junit:
junit:
- report.xml
```

View file

@ -341,7 +341,7 @@ include:
> - Introduced in [GitLab Premium](https://about.gitlab.com/pricing/) 10.5.
> - Available for Starter, Premium, and Ultimate in GitLab 10.6 and later.
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/42861) to GitLab Core in 11.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/42861) to GitLab Free in 11.4.
Use the `include` keyword to include external YAML files in your CI/CD configuration.
You can break down one long `gitlab-ci.yml` file into multiple files to increase readability,
@ -3675,7 +3675,7 @@ deploystacks:
### `trigger`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8997) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Core in 12.8.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
Use `trigger` to define a downstream pipeline trigger. When GitLab starts a `trigger` job,
a downstream pipeline is created.

View file

@ -65,7 +65,7 @@ Notify the [Distribution team](https://about.gitlab.com/handbook/engineering/dev
New services to be bundled with GitLab need to be available in the following environments.
**Dev environment**
**Development environment**
The first step of bundling a new service is to provide it in the development environment to engage in collaboration and feedback.

View file

@ -1683,7 +1683,7 @@ full stack:
- An argument or scalar's [`prepare`](#validating-arguments) applies correctly.
- Logic in a resolver or mutation's [`#ready?` method](#correct-use-of-resolverready) applies correctly.
- An [argument's `default_value`](https://graphql-ruby.org/fields/arguments.html) applies correctly.
- Objects resolve performantly and there are no N+1 issues.
- Objects resolve successfully, and there are no N+1 issues.
When adding a query, you can use the `a working graphql query` shared example to test if the query
renders valid results.

View file

@ -934,7 +934,7 @@ ps aux | grep '^git'
```
GitLab has several components to operate. It requires a persistent database
(PostgreSQL) and Redis database, and uses Apache `httpd` or NGINX to proxypass
(PostgreSQL) and Redis database, and uses Apache `httpd` or NGINX to `proxypass`
Puma. All these components should run as different system users to GitLab
(for example, `postgres`, `redis`, and `www-data`, instead of `git`).

View file

@ -6,8 +6,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Generating chaos in a test GitLab instance
<!-- vale gitlab.Spelling = NO -->
As [Werner Vogels](https://twitter.com/Werner), the CTO at Amazon Web Services, famously put it, **Everything fails, all the time**.
<!-- vale gitlab.Spelling = NO -->
As a developer, it's as important to consider the failure modes in which your software may operate as much as normal operation. Doing so can mean the difference between a minor hiccup leading to a scattering of `500` errors experienced by a tiny fraction of users, and a full site outage that affects all users for an extended period.
To paraphrase [Tolstoy](https://en.wikipedia.org/wiki/Anna_Karenina_principle), _all happy servers are alike, but all failing servers are failing in their own way_. Luckily, there are ways we can attempt to simulate these failure modes, and the chaos endpoints are tools for assisting in this process.

View file

@ -45,7 +45,7 @@ sequenceDiagram
GitLab Rails to authorize the upload.
1. GitLab Rails validates whether the artifact can be uploaded and sends
`ProcessLsif: true` header if the lsif artifact can be processed.
`ProcessLsif: true` header if the LSIF artifact can be processed.
1. Workhorse reads the LSIF document line by line and generates code intelligence
data for each file in the project. The output is a zipped directory of JSON

View file

@ -24,7 +24,7 @@ uncovered edge cases.
The default approach is to choose a reviewer from your group or team for the first review.
This is only a recommendation and the reviewer may be from a different team.
However, it is recommended to pick someone who is a [domain expert](#domain-experts).
If your merge request touches more than one domain (for example, Dynamic Analysis and GraphQL), ask for reviews from an expert from each domain.
If your merge request touches more than one domain (for example, Dynamic Analysis and GraphQL), ask for reviews from an expert from each domain.
You can read more about the importance of involving reviewer(s) in the section on the responsibility of the author below.
@ -344,7 +344,7 @@ experience, refactors the existing code). Then:
convey your intent.
- For non-mandatory suggestions, decorate with (non-blocking) so the author knows they can
optionally resolve within the merge request or follow-up at a later stage.
- There's a [Chrome/Firefox addon](https://gitlab.com/conventionalcomments/conventional-comments-button) which you can use to apply [Conventional Comment](https://conventionalcomments.org/) prefixes.
- There's a [Chrome/Firefox add-on](https://gitlab.com/conventionalcomments/conventional-comments-button) which you can use to apply [Conventional Comment](https://conventionalcomments.org/) prefixes.
- After a round of line notes, it can be helpful to post a summary note such as
"Looks good to me", or "Just a couple things to address."
- Assign the merge request to the author if changes are required following your
@ -569,7 +569,7 @@ A good example of collaboration on an MR touching multiple parts of the codebase
### Credits
Largely based on the [thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review).
Largely based on the [`thoughtbot` code review guide](https://github.com/thoughtbot/guides/tree/master/code-review).
---

View file

@ -56,12 +56,12 @@ The current Lefthook configuration can be found in [`lefthook.yml`](https://gitl
Before you push your changes, Lefthook automatically runs the following checks:
- Danger: Runs a subset of checks that `danger-review` runs on your merge requests.
- ES lint: Run `yarn eslint` checks (with the [`.eslintrc.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.eslintrc.yml) config) on the modified `*.{js,vue}` files. Tags: `frontend`, `style`.
- HAML lint: Run `bundle exec haml-lint` checks (with the [`.haml-lint.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.haml-lint.yml) config) on the modified `*.html.haml` files. Tags: `view`, `haml`, `style`.
- ES lint: Run `yarn eslint` checks (with the [`.eslintrc.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.eslintrc.yml) configuration) on the modified `*.{js,vue}` files. Tags: `frontend`, `style`.
- HAML lint: Run `bundle exec haml-lint` checks (with the [`.haml-lint.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.haml-lint.yml) configuration) on the modified `*.html.haml` files. Tags: `view`, `haml`, `style`.
- Markdown lint: Run `yarn markdownlint` checks on the modified `*.md` files. Tags: `documentation`, `style`.
- SCSS lint: Run `bundle exec scss-lint` checks (with the [`.scss-lint.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.scss-lint.yml) config) on the modified `*.scss{,.css}` files. Tags: `stylesheet`, `css`, `style`.
- RuboCop: Run `bundle exec rubocop` checks (with the [`.rubocop.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.rubocop.yml) config) on the modified `*.rb` files. Tags: `backend`, `style`.
- Vale: Run `vale` checks (with the [`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.vale.ini) config) on the modified `*.md` files. Tags: `documentation`, `style`.
- SCSS lint: Run `bundle exec scss-lint` checks (with the [`.scss-lint.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.scss-lint.yml) configuration) on the modified `*.scss{,.css}` files. Tags: `stylesheet`, `css`, `style`.
- RuboCop: Run `bundle exec rubocop` checks (with the [`.rubocop.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.rubocop.yml) configuration) on the modified `*.rb` files. Tags: `backend`, `style`.
- Vale: Run `vale` checks (with the [`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.vale.ini) configuration) on the modified `*.md` files. Tags: `documentation`, `style`.
In addition to the default configuration, you can define a [local configuration](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#local-config).
@ -142,7 +142,7 @@ This ensures that our list isn't mistakenly removed by another auto generation o
the `.rubocop_todo.yml`. This also allows us greater visibility into the exceptions
which are currently being resolved.
One way to generate the initial list is to run the todo auto generation,
One way to generate the initial list is to run the `todo` auto generation,
with `exclude limit` set to a high number.
```shell
@ -180,8 +180,12 @@ See the dedicated [Shell scripting standards and style guidelines](../shell_scri
## Markdown
<!-- vale gitlab.Spelling = NO -->
We're following [Ciro Santilli's Markdown Style Guide](https://cirosantilli.com/markdown-style-guide/).
<!-- vale gitlab.Spelling = YES -->
## Documentation
See the dedicated [Documentation Style Guide](../documentation/styleguide/index.md).

View file

@ -50,17 +50,17 @@ European/US and APAC friendly hours. You can join the office hours call and brin
that require a more in-depth discussion between the database reviewers and maintainers:
- [Database Office Hours Agenda](https://docs.google.com/document/d/1wgfmVL30F8SdMg-9yY6Y8djPSxWNvKmhR5XmsvYX1EI/edit).
- [Youtube playlist with past recordings](https://www.youtube.com/playlist?list=PL05JrBw4t0Kp-kqXeiF7fF7cFYaKtdqXM).
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [YouTube playlist with past recordings](https://www.youtube.com/playlist?list=PL05JrBw4t0Kp-kqXeiF7fF7cFYaKtdqXM).
You should also join the [#database-labs](../understanding_explain_plans.md#database-lab)
Slack channel and get familiar with how to use Joe, the slackbot that provides developers
Slack channel and get familiar with how to use Joe, the Slackbot that provides developers
with their own clone of the production database.
Understanding and efficiently using `EXPLAIN` plans is at the core of the database review process.
The following guides provide a quick introduction and links to follow on more advanced topics:
- Guide on [understanding EXPLAIN plans](../understanding_explain_plans.md).
- [Explaining the unexplainable series in depesz](https://www.depesz.com/tag/unexplainable/).
- [Explaining the unexplainable series in `depesz`](https://www.depesz.com/tag/unexplainable/).
Finally, you can find various guides in the [Database guides](index.md) page that cover more specific
topics and use cases. The most frequently required during database reviewing are the following:

View file

@ -60,7 +60,7 @@ was the first table to be partitioned in the application database
(scheduled for deployment with the GitLab 13.5 release). This
table tracks audit entries of security events that happen in the
application. In almost all cases, users want to see audit activity that
occurs in a certain timeframe. As a result, date-range partitioning
occurs in a certain time frame. As a result, date-range partitioning
was a natural fit for how the data would be accessed.
To look at this in more detail, imagine a simplified `audit_events` schema:

View file

@ -69,16 +69,14 @@ bundle exec rails db -e development
Use these instructions for exploring the GitLab database while developing with the GDK:
1. Install or open [Visual Studio Code](https://code.visualstudio.com/download).
1. Install the [PostgreSQL VSCode Extension](https://marketplace.visualstudio.com/items?itemName=ckolkman.vscode-postgres) by Chris Kolkman.
1. Install the [PostgreSQL VSCode Extension](https://marketplace.visualstudio.com/items?itemName=ckolkman.vscode-postgres).
1. In Visual Studio Code click on the PostgreSQL Explorer button in the left toolbar.
1. In the top bar of the new window, click on the `+` to **Add Database Connection**, and follow the prompts to fill in the details:
1. **Hostname**: the path to the PostgreSQL folder in your GDK directory (for example `/dev/gitlab-development-kit/postgresql`).
1. **PostgreSQL user to authenticate as**: usually your local username, unless otherwise specified during PostgreSQL installation.
1. **Password of the PostgreSQL user**: the password you set when installing PostgreSQL.
1. **Port number to connect to**: `5432` (default).
1. <!-- vale gitlab.Spelling = NO -->
**Use an ssl connection?**
<!-- vale gitlab.Spelling = YES --> This depends on your installation. Options are:
1. **Use an SSL connection?** This depends on your installation. Options are:
- **Use Secure Connection**
- **Standard Connection** (default)
1. **(Optional) The database to connect to**: `gitlabhq_development`.

View file

@ -147,7 +147,7 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac
- Provide the link to the plan at: [explain.depesz.com](https://explain.depesz.com). Paste both the plan and the query used in the form.
- When providing query plans, make sure it hits enough data:
- You can use a GitLab production replica to test your queries on a large scale,
through the `#database-lab` Slack channel or through [chatops](understanding_explain_plans.md#chatops).
through the `#database-lab` Slack channel or through [ChatOps](understanding_explain_plans.md#chatops).
- Usually, the `gitlab-org` namespace (`namespace_id = 9970`) and the
`gitlab-org/gitlab-foss` (`project_id = 13083`) or the `gitlab-org/gitlab` (`project_id = 278964`)
projects provide enough data to serve as a good example.
@ -220,13 +220,13 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac
- Check for any obviously complex queries and queries the author specifically
points out for review (if any)
- If not present yet, ask the author to provide SQL queries and query plans
(for example, by using [chatops](understanding_explain_plans.md#chatops) or direct
(for example, by using [ChatOps](understanding_explain_plans.md#chatops) or direct
database access)
- For given queries, review parameters regarding data distribution
- [Check query plans](understanding_explain_plans.md) and suggest improvements
to queries (changing the query, schema or adding indexes and similar)
- General guideline is for queries to come in below [100ms execution time](query_performance.md#timing-guidelines-for-queries)
- Avoid N+1 problems and minimalize the [query count](merge_request_performance_guidelines.md#query-counts).
- Avoid N+1 problems and minimize the [query count](merge_request_performance_guidelines.md#query-counts).
### Timing guidelines for migrations

View file

@ -14,12 +14,20 @@ We rely on different sources to present diffs. These include:
## Deep Dive
<!-- vale gitlab.Spelling = NO -->
In January 2019, Oswaldo Ferreira hosted a Deep Dive (GitLab team members only:
`https://gitlab.com/gitlab-org/create-stage/issues/1`) on GitLab Diffs and Commenting on Diffs
functionality to share his domain specific knowledge with anyone who may work in this part of the
codebase in the future. You can find the <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [recording on YouTube](https://www.youtube.com/watch?v=K6G3gMcFyek),
and the slides on [Google Slides](https://docs.google.com/presentation/d/1bGutFH2AT3bxOPZuLMGl1ANWHqFnrxwQwjiwAZkF-TU/edit)
and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/b5ad2f336e0afcfe0f99db0af0ccc71a/).
functionality to share domain-specific knowledge with anyone who may work in this part of the
codebase in the future:
<!-- vale gitlab.Spelling = YES -->
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
[Recording on YouTube](https://www.youtube.com/watch?v=K6G3gMcFyek)
- Slides on [Google Slides](https://docs.google.com/presentation/d/1bGutFH2AT3bxOPZuLMGl1ANWHqFnrxwQwjiwAZkF-TU/edit)
- [PDF slides](https://gitlab.com/gitlab-org/create-stage/uploads/b5ad2f336e0afcfe0f99db0af0ccc71a/)
Everything covered in this deep dive was accurate as of GitLab 11.7, and while specific details may
have changed since then, it should still serve as a good introduction.

View file

@ -514,7 +514,7 @@ To run the tool on an existing screenshot generator, take the following steps:
1. Set up the [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/gitlab_docs.md).
1. Navigate to the subdirectory with your cloned GitLab repository, typically `gdk/gitlab`.
1. Make sure that your GDK database is fully migrated: `bin/rake db:migrate RAILS_ENV=development`.
1. Install pngquant, see the tool website for more information: [`pngquant`](https://pngquant.org/)
1. Install `pngquant`, see the tool website for more information: [`pngquant`](https://pngquant.org/)
1. Run `scripts/docs_screenshots.rb spec/docs_screenshots/<name_of_screenshot_generator>.rb <milestone-version>`.
1. Identify the location of the screenshots, based on the `gitlab/doc` location defined by the `it` parameter in your script.
1. Commit the newly created screenshots.

View file

@ -165,7 +165,7 @@ curl --request POST \
### Post data using form-data
Instead of using JSON or urlencode you can use multipart/form-data which
Instead of using JSON or URL-encoding data, you can use `multipart/form-data` which
properly handles data encoding:
```shell

View file

@ -114,7 +114,7 @@ located in the [Dockerfiles directory](https://gitlab.com/gitlab-org/gitlab-docs
If you need to rebuild the Docker images immediately (must have maintainer level permissions):
WARNING:
If you change the dockerfile configuration and rebuild the images, you can break the master
If you change the Dockerfile configuration and rebuild the images, you can break the master
pipeline in the main `gitlab` repository as well as in `gitlab-docs`. Create an image with
a different name first and test it to ensure you do not break the pipelines.

View file

@ -246,7 +246,7 @@ when building the `image:docs-lint-markdown` Docker image containing these tools
| Tool | Version | Command | Additional information |
|--------------------|----------|-------------------------------------------|------------------------|
| `markdownlint-cli` | Latest | `yarn global add markdownlint-cli` | n/a |
| `markdownlint-cli` | Specfic | `yarn global add markdownlint-cli@0.23.2` | The `@` indicates a specific version, and this example updates the tool to version `0.23.2`. |
| `markdownlint-cli` | Specific | `yarn global add markdownlint-cli@0.23.2` | The `@` indicates a specific version, and this example updates the tool to version `0.23.2`. |
| Vale | Latest | `brew update && brew upgrade vale` | This command is for macOS only. |
| Vale | Specific | n/a | Not possible using `brew`, but can be [directly downloaded](https://github.com/errata-ai/vale/releases). |

View file

@ -1,3 +1,9 @@
---
stage: none
group: Development
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
---
# Set up local Codesandbox development environment
This guide walks through setting up a local [Codesandbox repository](https://github.com/codesandbox/codesandbox-client) and integrating it with a local GitLab instance. Codesandbox

View file

@ -153,7 +153,7 @@ cd $indexer_path && sudo make install
The `gitlab-elasticsearch-indexer` will be installed to `/usr/local/bin`.
You can change the installation path with the `PREFIX` env variable.
You can change the installation path with the `PREFIX` environment variable.
Please remember to pass the `-E` flag to `sudo` if you do so.
Example:
@ -218,7 +218,7 @@ The following Elasticsearch settings are available:
| Parameter | Description |
|-------------------------------------------------------|-------------|
| `Elasticsearch indexing` | Enables or disables Elasticsearch indexing and creates an empty index if one does not already exist. You may want to enable indexing but disable search in order to give the index time to be fully completed, for example. Also, keep in mind that this option doesn't have any impact on existing data, this only enables/disables the background indexer which tracks data changes and ensures new data is indexed. |
| `Pause Elasticsearch indexing` | Enables or disables temporary indexing pause. This is useful for cluster migration/reindexing. All changes are still tracked, but they are not committed to the Elasticsearch index until unpaused. |
| `Pause Elasticsearch indexing` | Enables or disables temporary indexing pause. This is useful for cluster migration/reindexing. All changes are still tracked, but they are not committed to the Elasticsearch index until resumed. |
| `Search with Elasticsearch enabled` | Enables or disables using Elasticsearch in search. |
| `URL` | The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., `http://host1, https://host2:9200`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (e.g., `http://<username>:<password>@<elastic_host>:9200/`). |
| `Number of Elasticsearch shards` | Elasticsearch indexes are split into multiple shards for performance reasons. In general, larger indexes need to have more shards. Changes to this value do not take effect until the index is recreated. You can read more about tradeoffs in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/scalability.html). |
@ -260,7 +260,7 @@ from the Elasticsearch index as expected.
## Enabling custom language analyzers
You can improve the language support for Chinese and Japanese languages by utilizing [smartcn](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html) and/or [kuromoji](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html) analysis plugins from Elastic.
You can improve the language support for Chinese and Japanese languages by utilizing [`smartcn`](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html) and/or [`kuromoji`](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html) analysis plugins from Elastic.
To enable language(s) support:
@ -276,10 +276,10 @@ For guidance on what to install, see the following Elasticsearch language plugin
| Parameter | Description |
|-------------------------------------------------------|-------------|
| `Enable Chinese (smartcn) custom analyzer: Indexing` | Enables or disables Chinese language support using [smartcn](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html) custom analyzer for newly created indices.|
| `Enable Chinese (smartcn) custom analyzer: Search` | Enables or disables using [smartcn](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html) fields for Advanced Search. Please only enable this after [installing the plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html), enabling custom analyzer indexing and recreating the index.|
| `Enable Japanese (kuromoji) custom analyzer: Indexing` | Enables or disables Japanese language support using [kuromoji](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html) custom analyzer for newly created indices.|
| `Enable Japanese (kuromoji) custom analyzer: Search` | Enables or disables using [kuromoji](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html) fields for Advanced Search. Please only enable this after [installing the plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html), enabling custom analyzer indexing and recreating the index.|
| `Enable Chinese (smartcn) custom analyzer: Indexing` | Enables or disables Chinese language support using [`smartcn`](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html) custom analyzer for newly created indices.|
| `Enable Chinese (smartcn) custom analyzer: Search` | Enables or disables using [`smartcn`](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html) fields for Advanced Search. Please only enable this after [installing the plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html), enabling custom analyzer indexing and recreating the index.|
| `Enable Japanese (kuromoji) custom analyzer: Indexing` | Enables or disables Japanese language support using [`kuromoji`](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html) custom analyzer for newly created indices.|
| `Enable Japanese (kuromoji) custom analyzer: Search` | Enables or disables using [`kuromoji`](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html) fields for Advanced Search. Please only enable this after [installing the plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html), enabling custom analyzer indexing and recreating the index.|
## Disabling Advanced Search
@ -317,9 +317,9 @@ used by the GitLab Advanced Search integration.
In the **Admin Area > Settings > Advanced Search** section, select the
**Pause Elasticsearch Indexing** setting, and then save your change.
With this, all updates that should happen on your Elasticsearch index will be
buffered and caught up once unpaused.
buffered and caught up after resuming.
The indexing will also be automatically paused when the [**Trigger cluster reindexing**](#trigger-the-reindex-via-the-advanced-search-administration) button is used, and unpaused when the reindexing completes or aborts.
The indexing will also be automatically paused when the [**Trigger cluster reindexing**](#trigger-the-reindex-via-the-advanced-search-administration) button is used, and resumes when the reindexing completes or aborts.
### Setup
@ -430,7 +430,7 @@ To trigger the re-index from `primary` index:
The reindexing is now completed. Your GitLab instance is now ready to use the [automated in-cluster reindexing](#trigger-the-reindex-via-the-advanced-search-administration) feature for future reindexing.
1. Unpause the indexing
1. Resume the indexing
Under **Admin Area > Settings > Advanced Search**, uncheck the **Pause Elasticsearch Indexing** setting and save.
@ -448,9 +448,9 @@ After the reindexing is completed, the original index will be scheduled to be de
While the reindexing is running, you will be able to follow its progress under that same section.
### Mark the most recent reindex job as failed and unpause the indexing
### Mark the most recent reindex job as failed and resume the indexing
Sometimes, you might want to abandon the unfinished reindex job and unpause the indexing. You can achieve this via the following steps:
Sometimes, you might want to abandon the unfinished reindex job and resume the indexing. You can achieve this via the following steps:
1. Mark the most recent reindex job as failed:
@ -518,8 +518,8 @@ In order to debug issues with the migrations you can check the [`elasticsearch.l
Some migrations are built with a retry limit. If the migration cannot finish within the retry limit,
it will be halted and a notification will be displayed in the Advanced Search integration settings.
It is recommended to check the [`elasticsearch.log` file](../administration/logs.md#elasticsearchlog) to
debug why the migration was halted and make any changes before retrying the migration. Once you believe you've
fixed the cause of the failure, click "Retry migration", and the migration will be scheduled to be retried
debug why the migration was halted and make any changes before retrying the migration. Once you believe you've
fixed the cause of the failure, click "Retry migration", and the migration will be scheduled to be retried
in the background.
## GitLab Advanced Search Rake tasks
@ -598,7 +598,7 @@ For basic guidance on choosing a cluster configuration you may refer to [Elastic
- Number of CPUs (CPU cores) per node usually corresponds to the `Number of Elasticsearch shards` setting described below.
- A good guideline is to ensure you keep the number of shards per node below 20 per GB heap it has configured. A node with a 30GB heap should therefore have a maximum of 600 shards, but the further below this limit you can keep it the better. This will generally help the cluster stay in good health.
- Small shards result in small segments, which increases overhead. Aim to keep the average shard size between at least a few GB and a few tens of GB. Another consideration is the number of documents, you should aim for this simple formula for the number of shards: `number of expected documents / 5M +1`.
- `refresh_interval` is a per index setting. You may want to adjust that from default `1s` to a bigger value if you don't need data in realtime. This will change how soon you will see fresh results. If that's important for you, you should leave it as close as possible to the default value.
- `refresh_interval` is a per index setting. You may want to adjust that from default `1s` to a bigger value if you don't need data in real-time. This will change how soon you will see fresh results. If that's important for you, you should leave it as close as possible to the default value.
- You might want to raise [`indices.memory.index_buffer_size`](https://www.elastic.co/guide/en/elasticsearch/reference/current/indexing-buffer.html) to 30% or 40% if you have a lot of heavy indexing operations.
### Advanced Search integration settings guidance
@ -993,7 +993,7 @@ Sometimes there may be issues with your Elasticsearch index data and as such
GitLab will allow you to revert to "basic search" when there are no search
results and assuming that basic search is supported in that scope. This "basic
search" will behave as though you don't have Advanced Search enabled at all for
your instance and search using other data sources (ie. PostgreSQL data and Git
your instance and search using other data sources (such as PostgreSQL data and Git
data).
### Data recovery: Elasticsearch is a secondary data store only

View file

@ -94,7 +94,7 @@ with the **Add Panel** page:
## Duplicate a GitLab-defined dashboard
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/37238) in GitLab 12.7.
> - From [GitLab 12.8 onwards](https://gitlab.com/gitlab-org/gitlab/-/issues/39505), custom metrics are also duplicated when you duplicate a dashboard.
> - [GitLab versions 12.8 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/39505), custom metrics are also duplicated when you duplicate a dashboard.
You can save a complete copy of a GitLab-defined dashboard along with all custom metrics added to it.
The resulting `.yml` file can be customized and adapted to your project.
@ -128,7 +128,7 @@ any chart on a dashboard:
The options are:
- **Expand panel** - Displays a larger version of a visualization. To return to
the dashboard, click the **Back** button in your browser, or press the <kbd>Esc</kbd> key.
the dashboard, click the **Back** button in your browser, or press the <kbd>Escape</kbd> key.
([Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3100) in GitLab 13.0.)
- **View logs** **(ULTIMATE)** - Displays [Logs](../../../user/project/clusters/kubernetes_pod_logs.md),
if they are enabled. If used in conjunction with the [timeline zoom](#timeline-zoom-and-url-sharing)
@ -147,7 +147,7 @@ The options are:
You can use the **Timeline zoom** function at the bottom of a chart to zoom in
on a date and time of your choice. When you click and drag the sliders to select
a different beginning or end date of data to display, GitLab adds your selected start
and end times to the URL, enabling you to share specific timeframes more easily.
and end times to the URL, enabling you to share specific time frames more easily.
## Dashboard Annotations

View file

@ -40,7 +40,7 @@ To unlock a locked user:
user.unlock_access!
```
1. Exit the console with <kbd>Ctrl</kbd>+<kbd>d</kbd>
1. Exit the console with <kbd>Control</kbd>+<kbd>d</kbd>
The user should now be able to log in.

View file

@ -28,9 +28,13 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
### 1.1. Version Control and Git
<!-- vale gitlab.Spelling = NO -->
1. [Version Control Systems](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.g72f2e4906_2_29)
1. [Katacoda: Learn Git Version Control using Interactive Browser-Based Scenarios](https://www.katacoda.com/courses/git)
<!-- vale gitlab.Spelling = YES -->
### 1.2. GitLab Basics
1. [An Overview of GitLab.com - Video](https://www.youtube.com/watch?v=WaiL5DGEMR4)
@ -55,11 +59,14 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
### 1.5. Migrating from other Source Control
<!-- vale gitlab.Spelling = NO -->
1. [Migrating from Bitbucket/Stash](../user/project/import/bitbucket.md)
1. [Migrating from GitHub](../user/project/import/github.md)
1. [Migrating from SVN](../user/project/import/svn.md)
1. [Migrating from Fogbugz](../user/project/import/fogbugz.md)
<!-- vale gitlab.Spelling = YES -->
### 1.6. The GitLab team
1. [About GitLab](https://about.gitlab.com/company/)
@ -185,6 +192,8 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
### 3.9. Integrations
<!-- vale gitlab.Spelling = NO -->
1. [How to Integrate Jira and Jenkins with GitLab - Video](https://gitlabmeetings.webex.com/gitlabmeetings/ldr.php?RCID=44b548147a67ab4d8a62274047146415)
1. [How to Integrate Jira with GitLab](../user/project/integrations/jira.md)
1. [How to Integrate Jenkins with GitLab](../integration/jenkins.md)
@ -193,9 +202,11 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
1. [How to Integrate Convox with GitLab](https://about.gitlab.com/blog/2016/06/09/continuous-delivery-with-gitlab-and-convox/)
1. [Getting Started with GitLab and Shippable CI](https://about.gitlab.com/blog/2016/05/05/getting-started-gitlab-and-shippable/)
<!-- vale gitlab.Spelling = YES -->
## 4. External Articles
1. [2011 WSJ article by Marc Andreessen - Software is Eating the World](https://www.wsj.com/articles/SB10001424053111903480904576512250915629460)
1. [2011 Wall Street Journal article - Software is Eating the World](https://www.wsj.com/articles/SB10001424053111903480904576512250915629460)
1. [2014 Blog post by Chris Dixon - Software eats software development](https://cdixon.org/2014/04/13/software-eats-software-development/)
1. [2015 Venture Beat article - Actually, Open Source is Eating the World](https://venturebeat.com/2015/12/06/its-actually-open-source-software-thats-eating-the-world/)

View file

@ -268,6 +268,7 @@ module API
mount ::API::Release::Links
mount ::API::RemoteMirrors
mount ::API::Repositories
mount ::API::ResourceAccessTokens
mount ::API::Search
mount ::API::Services
mount ::API::Settings

View file

@ -0,0 +1,94 @@
# frozen_string_literal: true
module API
class ResourceAccessTokens < ::API::Base
include PaginationParams
before { authenticate! }
feature_category :authentication_and_authorization
%w[project].each do |source_type|
resource source_type.pluralize, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get list of all access tokens for the specified resource' do
detail 'This feature was introduced in GitLab 13.9.'
end
params do
requires :id, type: String, desc: "The #{source_type} ID"
end
get ":id/access_tokens" do
resource = find_source(source_type, params[:id])
next unauthorized! unless has_permission_to_read?(resource)
tokens = PersonalAccessTokensFinder.new({ user: resource.bots, impersonation: false }).execute
present paginate(tokens), with: Entities::PersonalAccessToken
end
desc 'Revoke a resource access token' do
detail 'This feature was introduced in GitLab 13.9.'
end
params do
requires :id, type: String, desc: "The #{source_type} ID"
requires :token_id, type: String, desc: "The ID of the token"
end
delete ':id/access_tokens/:token_id' do
resource = find_source(source_type, params[:id])
token = find_token(resource, params[:token_id])
if token.nil?
next not_found!("Could not find #{source_type} access token with token_id: #{params[:token_id]}")
end
service = ::ResourceAccessTokens::RevokeService.new(
current_user,
resource,
token
).execute
service.success? ? no_content! : bad_request!(service.message)
end
desc 'Create a resource access token' do
detail 'This feature was introduced in GitLab 13.9.'
end
params do
requires :id, type: String, desc: "The #{source_type} ID"
requires :name, type: String, desc: "Resource access token name"
requires :scopes, type: Array[String], desc: "The permissions of the token"
optional :expires_at, type: Date, desc: "The expiration date of the token"
end
post ':id/access_tokens' do
resource = find_source(source_type, params[:id])
token_response = ::ResourceAccessTokens::CreateService.new(
current_user,
resource,
declared_params
).execute
if token_response.success?
present token_response.payload[:access_token], with: Entities::PersonalAccessToken
else
bad_request!(token_response.message)
end
end
end
end
helpers do
def find_source(source_type, id)
public_send("find_#{source_type}!", id) # rubocop:disable GitlabSecurity/PublicSend
end
def find_token(resource, token_id)
PersonalAccessTokensFinder.new({ user: resource.bots, impersonation: false }).find_by_id(token_id)
end
def has_permission_to_read?(resource)
can?(current_user, :project_bot_access, resource) || can?(current_user, :admin_resource_access_tokens, resource)
end
end
end
end

View file

@ -3062,6 +3062,9 @@ msgstr ""
msgid "An error occured while saving changes: %{error}"
msgstr ""
msgid "An error occured while updating the notification settings. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the thread."
msgstr ""

View file

@ -70,7 +70,7 @@
"codesandbox-api": "0.0.23",
"compression-webpack-plugin": "^5.0.2",
"copy-webpack-plugin": "^5.0.5",
"core-js": "^3.6.4",
"core-js": "^3.8.3",
"cron-validator": "^1.1.1",
"cropper": "^2.3.0",
"css-loader": "^2.1.1",

View file

@ -37,7 +37,10 @@ RSpec.describe 'Admin disables Git access protocol', :js do
it 'shows only the SSH clone information' do
resize_screen_xs
visit_project
find('.dropdown-toggle').click
within('.js-mobile-git-clone') do
find('.dropdown-toggle').click
end
expect(page).to have_content('Copy SSH clone URL')
expect(page).not_to have_content('Copy HTTP clone URL')
@ -66,7 +69,10 @@ RSpec.describe 'Admin disables Git access protocol', :js do
it 'shows only the HTTP clone information' do
resize_screen_xs
visit_project
find('.dropdown-toggle').click
within('.js-mobile-git-clone') do
find('.dropdown-toggle').click
end
expect(page).to have_content('Copy HTTP clone URL')
expect(page).not_to have_content('Copy SSH clone URL')
@ -97,7 +103,10 @@ RSpec.describe 'Admin disables Git access protocol', :js do
it 'shows both SSH and HTTP clone information' do
resize_screen_xs
visit_project
find('.dropdown-toggle').click
within('.js-mobile-git-clone') do
find('.dropdown-toggle').click
end
expect(page).to have_content('Copy HTTP clone URL')
expect(page).to have_content('Copy SSH clone URL')

View file

@ -6,6 +6,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
let(:project) { create(:project, :public, :repository) }
before do
stub_feature_flags(vue_notification_dropdown: false)
sign_in(project.owner)
end

View file

@ -0,0 +1,221 @@
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { shallowMount } from '@vue/test-utils';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import waitForPromises from 'helpers/wait_for_promises';
import { GlButtonGroup, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import httpStatus from '~/lib/utils/http_status';
import NotificationsDropdown from '~/notifications/components/notifications_dropdown.vue';
import NotificationsDropdownItem from '~/notifications/components/notifications_dropdown_item.vue';
const mockDropdownItems = ['global', 'watch', 'participating', 'mention', 'disabled'];
const mockToastShow = jest.fn();
describe('NotificationsDropdown', () => {
let wrapper;
let mockAxios;
function createComponent(injectedProperties = {}) {
return shallowMount(NotificationsDropdown, {
stubs: {
GlButtonGroup,
GlDropdown,
GlDropdownItem,
NotificationsDropdownItem,
},
directives: {
GlTooltip: createMockDirective(),
},
provide: {
...injectedProperties,
},
mocks: {
$toast: {
show: mockToastShow,
},
},
});
}
const findButtonGroup = () => wrapper.find(GlButtonGroup);
const findDropdown = () => wrapper.find(GlDropdown);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findAllNotificationsDropdownItems = () => wrapper.findAll(NotificationsDropdownItem);
const findDropdownItemAt = (index) =>
findAllNotificationsDropdownItems().at(index).find(GlDropdownItem);
const clickDropdownItemAt = async (index) => {
const dropdownItem = findDropdownItemAt(index);
dropdownItem.vm.$emit('click');
await waitForPromises();
};
beforeEach(() => {
gon.api_version = 'v4';
mockAxios = new MockAdapter(axios);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockAxios.restore();
});
describe('template', () => {
describe('when notification level is "custom"', () => {
beforeEach(() => {
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'custom',
});
});
it('renders a button group', () => {
expect(findButtonGroup().exists()).toBe(true);
});
});
describe('when notification level is not "custom"', () => {
beforeEach(() => {
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'global',
});
});
it('does not render a button group', () => {
expect(findButtonGroup().exists()).toBe(false);
});
});
describe('button tooltip', () => {
const tooltipTitlePrefix = 'Notification setting';
it.each`
level | title
${'global'} | ${'Global'}
${'watch'} | ${'Watch'}
${'participating'} | ${'Participate'}
${'mention'} | ${'On mention'}
${'disabled'} | ${'Disabled'}
${'custom'} | ${'Custom'}
`(`renders "${tooltipTitlePrefix} - $title" for "$level" level`, ({ level, title }) => {
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: level,
});
const tooltipElement = findByTestId('notificationButton');
const tooltip = getBinding(tooltipElement.element, 'gl-tooltip');
expect(tooltip.value.title).toBe(`${tooltipTitlePrefix} - ${title}`);
});
});
describe('button icon', () => {
beforeEach(() => {
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'disabled',
});
});
it('renders the "notifications-off" icon when notification level is "disabled"', () => {
expect(findDropdown().props('icon')).toBe('notifications-off');
});
it('renders the "notifications" icon when notification level is not "disabled"', () => {
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'global',
});
expect(findDropdown().props('icon')).toBe('notifications');
});
});
describe('dropdown items', () => {
it.each`
dropdownIndex | level | title | description
${0} | ${'global'} | ${'Global'} | ${'Use your global notification setting'}
${1} | ${'watch'} | ${'Watch'} | ${'You will receive notifications for any activity'}
${2} | ${'participating'} | ${'Participate'} | ${'You will only receive notifications for threads you have participated in'}
${3} | ${'mention'} | ${'On mention'} | ${'You will receive notifications only for comments in which you were @mentioned'}
${4} | ${'disabled'} | ${'Disabled'} | ${'You will not get any notifications via email'}
${5} | ${'custom'} | ${'Custom'} | ${'You will only receive notifications for the events you choose'}
`('displays "$title" and "$description"', ({ dropdownIndex, title, description }) => {
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'global',
});
expect(findAllNotificationsDropdownItems().at(dropdownIndex).props('title')).toBe(title);
expect(findAllNotificationsDropdownItems().at(dropdownIndex).props('description')).toBe(
description,
);
});
});
});
describe('when selecting an item', () => {
beforeEach(() => {
jest.spyOn(axios, 'put');
});
it.each`
projectId | groupId | endpointUrl | endpointType | condition
${1} | ${null} | ${'/api/v4/projects/1/notification_settings'} | ${'project notifications'} | ${'a projectId is given'}
${null} | ${1} | ${'/api/v4/groups/1/notification_settings'} | ${'group notifications'} | ${'a groupId is given'}
${null} | ${null} | ${'/api/v4/notification_settings'} | ${'global notifications'} | ${'when neither projectId nor groupId are given'}
`(
'calls the $endpointType endpoint when $condition',
async ({ projectId, groupId, endpointUrl }) => {
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'global',
projectId,
groupId,
});
await clickDropdownItemAt(1);
expect(axios.put).toHaveBeenCalledWith(endpointUrl, {
level: 'watch',
});
},
);
it('updates the selectedNotificationLevel and marks the item with a checkmark', async () => {
mockAxios.onPut('/api/v4/notification_settings').reply(httpStatus.OK, {});
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'global',
});
const dropdownItem = findDropdownItemAt(1);
await clickDropdownItemAt(1);
expect(wrapper.vm.selectedNotificationLevel).toBe('watch');
expect(dropdownItem.props('isChecked')).toBe(true);
});
it("won't update the selectedNotificationLevel and shows a toast message when the request fails and ", async () => {
mockAxios.onPut('/api/v4/notification_settings').reply(httpStatus.NOT_FOUND, {});
wrapper = createComponent({
dropdownItems: mockDropdownItems,
initialNotificationLevel: 'global',
});
await clickDropdownItemAt(1);
expect(wrapper.vm.selectedNotificationLevel).toBe('global');
expect(
mockToastShow,
).toHaveBeenCalledWith(
'An error occured while updating the notification settings. Please try again.',
{ type: 'error' },
);
});
});
});

View file

@ -1,25 +1,20 @@
import Vue from 'vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import component from '~/pipelines/components/pipelines_list/blank_state.vue';
import { mount } from '@vue/test-utils';
import { getByText } from '@testing-library/dom';
import BlankState from '~/pipelines/components/pipelines_list/blank_state.vue';
describe('Pipelines Blank State', () => {
let vm;
let Component;
beforeEach(() => {
Component = Vue.extend(component);
vm = mountComponent(Component, {
const wrapper = mount(BlankState, {
propsData: {
svgPath: 'foo',
message: 'Blank State',
});
},
});
it('should render svg', () => {
expect(vm.$el.querySelector('.svg-content img').getAttribute('src')).toEqual('foo');
expect(wrapper.find('.svg-content img').attributes('src')).toEqual('foo');
});
it('should render message', () => {
expect(vm.$el.querySelector('h4').textContent.trim()).toEqual('Blank State');
expect(getByText(wrapper.element, /Blank State/i)).toBeTruthy();
});
});

View file

@ -468,6 +468,49 @@ RSpec.describe ProjectPolicy do
end
end
context "project bots" do
let(:project_bot) { create(:user, :project_bot) }
let(:user) { create(:user) }
context "project_bot_access" do
context "when regular user and part of the project" do
let(:current_user) { user }
before do
project.add_developer(user)
end
it { is_expected.not_to be_allowed(:project_bot_access)}
end
context "when project bot and not part of the project" do
let(:current_user) { project_bot }
it { is_expected.not_to be_allowed(:project_bot_access)}
end
context "when project bot and part of the project" do
let(:current_user) { project_bot }
before do
project.add_developer(project_bot)
end
it { is_expected.to be_allowed(:project_bot_access)}
end
end
context 'with resource access tokens' do
let(:current_user) { project_bot }
before do
project.add_maintainer(project_bot)
end
it { is_expected.not_to be_allowed(:admin_resource_access_tokens)}
end
end
describe 'read_prometheus_alerts' do
context 'with admin' do
let(:current_user) { admin }

View file

@ -0,0 +1,293 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe API::ResourceAccessTokens do
context "when the resource is a project" do
let_it_be(:project) { create(:project) }
let_it_be(:other_project) { create(:project) }
let_it_be(:user) { create(:user) }
describe "GET projects/:id/access_tokens" do
subject(:get_tokens) { get api("/projects/#{project_id}/access_tokens", user) }
context "when the user has maintainer permissions" do
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:access_tokens) { create_list(:personal_access_token, 3, user: project_bot) }
let_it_be(:project_id) { project.id }
before do
project.add_maintainer(user)
project.add_maintainer(project_bot)
end
it "gets a list of access tokens for the specified project" do
get_tokens
token_ids = json_response.map { |token| token['id'] }
expect(response).to have_gitlab_http_status(:ok)
expect(token_ids).to match_array(access_tokens.pluck(:id))
end
context "when using a project access token to GET other project access tokens" do
let_it_be(:token) { access_tokens.first }
it "gets a list of access tokens for the specified project" do
get api("/projects/#{project_id}/access_tokens", personal_access_token: token)
token_ids = json_response.map { |token| token['id'] }
expect(response).to have_gitlab_http_status(:ok)
expect(token_ids).to match_array(access_tokens.pluck(:id))
end
end
context "when tokens belong to a different project" do
let_it_be(:bot) { create(:user, :project_bot) }
let_it_be(:token) { create(:personal_access_token, user: bot) }
before do
other_project.add_maintainer(bot)
other_project.add_maintainer(user)
end
it "does not return tokens from a different project" do
get_tokens
token_ids = json_response.map { |token| token['id'] }
expect(token_ids).not_to include(token.id)
end
end
context "when the project has no access tokens" do
let(:project_id) { other_project.id }
before do
other_project.add_maintainer(user)
end
it 'returns an empty array' do
get_tokens
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq([])
end
end
context "when trying to get the tokens of a different project" do
let_it_be(:project_id) { other_project.id }
it "returns 404" do
get_tokens
expect(response).to have_gitlab_http_status(:not_found)
end
end
context "when the project does not exist" do
let(:project_id) { non_existing_record_id }
it "returns 404" do
get_tokens
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context "when the user does not have valid permissions" do
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:access_tokens) { create_list(:personal_access_token, 3, user: project_bot) }
let_it_be(:project_id) { project.id }
before do
project.add_developer(user)
project.add_maintainer(project_bot)
end
it "returns 401" do
get_tokens
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
describe "DELETE projects/:id/access_tokens/:token_id", :sidekiq_inline do
subject(:delete_token) { delete api("/projects/#{project_id}/access_tokens/#{token_id}", user) }
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:token) { create(:personal_access_token, user: project_bot) }
let_it_be(:project_id) { project.id }
let_it_be(:token_id) { token.id }
before do
project.add_maintainer(project_bot)
end
context "when the user has maintainer permissions" do
before do
project.add_maintainer(user)
end
it "deletes the project access token from the project" do
delete_token
expect(response).to have_gitlab_http_status(:no_content)
expect(User.exists?(project_bot.id)).to be_falsy
end
context "when attempting to delete a non-existent project access token" do
let_it_be(:token_id) { non_existing_record_id }
it "does not delete the token, and returns 404" do
delete_token
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to include("Could not find project access token with token_id: #{token_id}")
end
end
context "when attempting to delete a token that does not belong to the specified project" do
let_it_be(:project_id) { other_project.id }
before do
other_project.add_maintainer(user)
end
it "does not delete the token, and returns 404" do
delete_token
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to include("Could not find project access token with token_id: #{token_id}")
end
end
end
context "when the user does not have valid permissions" do
before do
project.add_developer(user)
end
it "does not delete the token, and returns 400", :aggregate_failures do
delete_token
expect(response).to have_gitlab_http_status(:bad_request)
expect(User.exists?(project_bot.id)).to be_truthy
expect(response.body).to include("#{user.name} cannot delete #{token.user.name}")
end
end
end
describe "POST projects/:id/access_tokens" do
let_it_be(:params) { { name: "test", scopes: ["api"], expires_at: Date.today + 1.month } }
subject(:create_token) { post api("/projects/#{project_id}/access_tokens", user), params: params }
context "when the user has maintainer permissions" do
let_it_be(:project_id) { project.id }
let_it_be(:expires_at) { 1.month.from_now }
before do
project.add_maintainer(user)
end
context "with valid params" do
context "with full params" do
it "creates a project access token with the params", :aggregate_failures do
create_token
expect(response).to have_gitlab_http_status(:created)
expect(json_response["name"]).to eq("test")
expect(json_response["scopes"]).to eq(["api"])
expect(json_response["expires_at"]).to eq(expires_at.to_date.iso8601)
end
end
context "when 'expires_at' is not set" do
let_it_be(:params) { { name: "test", scopes: ["api"] } }
it "creates a project access token with the params", :aggregate_failures do
create_token
expect(response).to have_gitlab_http_status(:created)
expect(json_response["name"]).to eq("test")
expect(json_response["scopes"]).to eq(["api"])
expect(json_response["expires_at"]).to eq(nil)
end
end
end
context "with invalid params" do
context "when missing the 'name' param" do
let_it_be(:params) { { scopes: ["api"], expires_at: 5.days.from_now } }
it "does not create a project access token without 'name'" do
create_token
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include("name is missing")
end
end
context "when missing the 'scopes' param" do
let_it_be(:params) { { name: "test", expires_at: 5.days.from_now } }
it "does not create a project access token without 'scopes'" do
create_token
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include("scopes is missing")
end
end
end
context "when trying to create a token in a different project" do
let_it_be(:project_id) { other_project.id }
it "does not create the token, and returns the project not found error" do
create_token
expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to include("Project Not Found")
end
end
end
context "when the user does not have valid permissions" do
let_it_be(:project_id) { project.id }
context "when the user is a developer" do
before do
project.add_developer(user)
end
it "does not create the token, and returns the permission error" do
create_token
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include("User does not have permission to create project access token")
end
end
context "when a project access token tries to create another project access token" do
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:user) { project_bot }
before do
project.add_maintainer(user)
end
it "does not allow a project access token to create another project access token" do
create_token
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to include("User does not have permission to create project access token")
end
end
end
end
end
end

View file

@ -61,6 +61,14 @@ RSpec.describe Users::ApproveService do
expect(user.reload).to be_active
end
it 'logs approval in application logs' do
allow(Gitlab::AppLogger).to receive(:info)
subject
expect(Gitlab::AppLogger).to have_received(:info).with(message: "User instance access request approved", user: "#{user.username}", email: "#{user.email}", approved_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
it 'emails the user on approval' do
expect(DeviseMailer).to receive(:user_admin_approval).with(user).and_call_original
expect { subject }.to have_enqueued_mail(DeviseMailer, :user_admin_approval)

View file

@ -48,6 +48,14 @@ RSpec.describe Users::RejectService do
subject
end
it 'logs rejection in application logs' do
allow(Gitlab::AppLogger).to receive(:info)
subject
expect(Gitlab::AppLogger).to have_received(:info).with(message: "User instance access request rejected", user: "#{user.username}", email: "#{user.email}", rejected_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
end
end

View file

@ -9,6 +9,7 @@ RSpec.describe 'projects/_home_panel' do
let(:project) { create(:project) }
before do
stub_feature_flags(vue_notification_dropdown: false)
assign(:project, project)
allow(view).to receive(:current_user).and_return(user)

View file

@ -3334,10 +3334,10 @@ core-js-pure@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
core-js@^3.1.3, core-js@^3.6.4:
version "3.6.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647"
integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==
core-js@^3.1.3, core-js@^3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.3.tgz#c21906e1f14f3689f93abcc6e26883550dd92dd0"
integrity sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==
core-js@~2.3.0:
version "2.3.0"