Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
2516f0d87b
commit
37a1347df2
85 changed files with 1396 additions and 127 deletions
|
@ -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;
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
27
app/assets/javascripts/notifications/constants.js
Normal file
27
app/assets/javascripts/notifications/constants.js
Normal 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.',
|
||||
),
|
||||
};
|
38
app/assets/javascripts/notifications/index.js
Normal file
38
app/assets/javascripts/notifications/index.js
Normal 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);
|
||||
},
|
||||
});
|
||||
};
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Log user approval/rejection in application logs
|
||||
merge_request: 51768
|
||||
author:
|
||||
type: added
|
5
changelogs/unreleased/sfang-project-access-token-api.yml
Normal file
5
changelogs/unreleased/sfang-project-access-token-api.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Project access token management via API
|
||||
merge_request: 52139
|
||||
author:
|
||||
type: added
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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 you’re managing HA systems like GitLab, you have a load balancer
|
||||
We hope that if you’re 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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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/),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) |
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
107
doc/api/resource_access_tokens.md
Normal file
107
doc/api/resource_access_tokens.md
Normal 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.
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -303,7 +303,7 @@ test:
|
|||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
junit:
|
||||
junit:
|
||||
- report.xml
|
||||
```
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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`).
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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). |
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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/)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
94
lib/api/resource_access_tokens.rb
Normal file
94
lib/api/resource_access_tokens.rb
Normal 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
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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' },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 }
|
||||
|
|
293
spec/requests/api/resource_access_tokens_spec.rb
Normal file
293
spec/requests/api/resource_access_tokens_spec.rb
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue