...
 
Commits (87)
......@@ -8,12 +8,16 @@ rule "MD001"
rule "MD002"
rule "MD003", :style => :atx
rule "MD006"
rule "MD010"
rule "MD011"
rule "MD012"
rule "MD019"
rule "MD022"
rule "MD023"
rule "MD025"
rule "MD028"
rule "MD029", :style => :one
rule "MD030"
rule "MD032"
rule "MD034"
rule "MD037"
......
1.57.0
1.58.0
\ No newline at end of file
......@@ -137,7 +137,7 @@ gem 'asciidoctor-plantuml', '0.0.9'
gem 'rouge', '~> 3.7'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.3'
gem 'nokogiri', '~> 1.10.4'
gem 'escape_utils', '~> 1.1'
# Calendar rendering
......@@ -283,7 +283,7 @@ gem 'sentry-raven', '~> 2.9'
gem 'premailer-rails', '~> 1.9.7'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '~> 0.3.0'
gem 'gitlab-labkit', '~> 0.4.2'
# I18n
gem 'ruby_parser', '~> 3.8', require: false
......@@ -424,8 +424,8 @@ group :ed25519 do
gem 'bcrypt_pbkdf', '~> 1.0'
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 1.37.0', require: 'gitaly'
# Gitaly GRPC protocol definitions
gem 'gitaly', '~> 1.58.0'
gem 'grpc', '~> 1.19.0'
......
......@@ -309,13 +309,13 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (1.37.0)
gitaly (1.58.0)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-labkit (0.3.0)
gitlab-labkit (0.4.2)
actionpack (~> 5)
activesupport (~> 5)
grpc (~> 1.19.0)
grpc (~> 1.19)
jaeger-client (~> 0.10)
opentracing (~> 0.4)
gitlab-markup (1.7.0)
......@@ -541,7 +541,7 @@ GEM
net-ssh (5.2.0)
netrc (0.11.0)
nio4r (2.3.1)
nokogiri (1.10.3)
nokogiri (1.10.4)
mini_portile2 (~> 2.4.0)
nokogumbo (1.5.0)
nokogiri
......@@ -1096,9 +1096,9 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.37.0)
gitaly (~> 1.58.0)
github-markup (~> 1.7.0)
gitlab-labkit (~> 0.3.0)
gitlab-labkit (~> 0.4.2)
gitlab-markup (~> 1.7.0)
gitlab-sidekiq-fetcher (= 0.5.1)
gitlab-styles (~> 2.7)
......@@ -1148,7 +1148,7 @@ DEPENDENCIES
nakayoshi_fork (~> 0.0.4)
net-ldap
net-ssh (~> 5.2)
nokogiri (~> 1.10.3)
nokogiri (~> 1.10.4)
oauth2 (~> 1.4)
octokit (~> 4.9)
omniauth (~> 1.8)
......
......@@ -213,7 +213,7 @@ export default {
<template>
<td :colspan="colspan">
<div class="content">
<div class="content js-line-expansion-content">
<a
v-if="canExpandUp"
v-tooltip
......
......@@ -109,7 +109,7 @@ export const toggleLineDiscussions = ({ commit }, options) => {
export const renderFileForDiscussionId = ({ commit, rootState, state }, discussionId) => {
const discussion = rootState.notes.discussions.find(d => d.id === discussionId);
if (discussion) {
if (discussion && discussion.diff_file) {
const file = state.diffFiles.find(f => f.file_hash === discussion.diff_file.file_hash);
if (file) {
......
fragment PageInfo on PageInfo {
hasNextPage
endCursor
}
......@@ -58,26 +58,24 @@ export default {
<template>
<div>
<div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
<div class="position-relative">
<input
ref="searchInput"
v-model="search"
:placeholder="__('Search branches')"
type="search"
class="form-control dropdown-input-field"
@input="searchBranches"
/>
<icon :size="18" name="search" class="input-icon" />
</div>
</div>
<label class="dropdown-input pt-3 pb-3 mb-0 border-bottom block position-relative" @click.stop>
<input
ref="searchInput"
v-model="search"
:placeholder="__('Search branches')"
type="search"
class="form-control dropdown-input-field"
@input="searchBranches"
/>
<icon :size="18" name="search" class="ml-3 input-icon" />
</label>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
<gl-loading-icon
v-if="isLoading"
:size="2"
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
/>
<ul v-else class="mb-3 w-100">
<ul v-else class="mb-0 w-100">
<template v-if="hasBranches">
<li v-for="item in branches" :key="item.name">
<item :item="item" :project-id="currentProjectId" :is-active="isActiveBranch(item)" />
......
......@@ -76,19 +76,17 @@ export default {
<template>
<div>
<div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
<div class="position-relative">
<tokened-input
v-model="search"
:tokens="searchTokens"
:placeholder="__('Search merge requests')"
@focus="onSearchFocus"
@input="searchMergeRequests"
@removeToken="setSearchType(null)"
/>
<icon :size="18" name="search" class="input-icon" />
</div>
</div>
<label class="dropdown-input pt-3 pb-3 mb-0 border-bottom block" @click.stop>
<tokened-input
v-model="search"
:tokens="searchTokens"
:placeholder="__('Search merge requests')"
@focus="onSearchFocus"
@input="searchMergeRequests"
@removeToken="setSearchType(null)"
/>
<icon :size="18" name="search" class="ml-3 input-icon" />
</label>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
<gl-loading-icon
v-if="isLoading"
......@@ -96,7 +94,7 @@ export default {
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
/>
<template v-else>
<ul class="mb-3 w-100">
<ul class="mb-0 w-100">
<template v-if="showSearchTypes">
<li v-for="searchType in $options.searchTypes" :key="searchType.type">
<button
......@@ -107,7 +105,7 @@ export default {
<span class="d-flex append-right-default ide-search-list-current-icon">
<icon :size="18" name="search" />
</span>
<span> {{ searchType.label }} </span>
<span>{{ searchType.label }}</span>
</button>
</li>
</template>
......
......@@ -70,6 +70,9 @@ export default {
hasIssuableTemplates() {
return this.issuableTemplates.length;
},
showLockedWarning() {
return this.formState.lockedWarningVisible && !this.formState.updateLoading;
},
},
created() {
eventHub.$on('delete.issuable', this.resetAutosave);
......@@ -117,7 +120,7 @@ export default {
<template>
<form>
<locked-warning v-if="formState.lockedWarningVisible" />
<locked-warning v-if="showLockedWarning" />
<div class="row">
<div v-if="hasIssuableTemplates" class="col-sm-4 col-lg-3">
<description-template
......
......@@ -3,6 +3,7 @@ import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex';
import store from 'ee_else_ce/mr_notes/stores';
import notesApp from '../notes/components/notes_app.vue';
import discussionKeyboardNavigator from '../notes/components/discussion_keyboard_navigator.vue';
export default () => {
// eslint-disable-next-line no-new
......@@ -56,15 +57,19 @@ export default () => {
},
},
render(createElement) {
return createElement('notes-app', {
props: {
noteableData: this.noteableData,
notesData: this.notesData,
userData: this.currentUserData,
shouldShow: this.activeTab === 'show',
helpPagePath: this.helpPagePath,
},
});
const isDiffView = this.activeTab === 'diffs';
return createElement(discussionKeyboardNavigator, { props: { isDiffView } }, [
createElement('notes-app', {
props: {
noteableData: this.noteableData,
notesData: this.notesData,
userData: this.currentUserData,
shouldShow: this.activeTab === 'show',
helpPagePath: this.helpPagePath,
},
}),
]);
},
});
};
......@@ -9,7 +9,7 @@ Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
modules: {
page: mrPageModule,
page: mrPageModule(),
notes: notesModule(),
diffs: diffsModule(),
},
......
......@@ -2,11 +2,11 @@ import actions from '../actions';
import getters from '../getters';
import mutations from '../mutations';
export default {
export default () => ({
state: {
activeTab: null,
},
actions,
getters,
mutations,
};
});
<script>
/* global Mousetrap */
import 'mousetrap';
import { mapGetters, mapActions } from 'vuex';
import discussionNavigation from '~/notes/mixins/discussion_navigation';
export default {
mixins: [discussionNavigation],
props: {
isDiffView: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
currentDiscussionId: null,
};
},
computed: {
...mapGetters(['nextUnresolvedDiscussionId', 'previousUnresolvedDiscussionId']),
},
mounted() {
Mousetrap.bind('n', () => this.jumpToNextDiscussion());
Mousetrap.bind('p', () => this.jumpToPreviousDiscussion());
},
methods: {
...mapActions(['expandDiscussion']),
jumpToNextDiscussion() {
const nextId = this.nextUnresolvedDiscussionId(this.currentDiscussionId, this.isDiffView);
this.jumpToDiscussion(nextId);
this.currentDiscussionId = nextId;
},
jumpToPreviousDiscussion() {
const prevId = this.previousUnresolvedDiscussionId(this.currentDiscussionId, this.isDiffView);
this.jumpToDiscussion(prevId);
this.currentDiscussionId = prevId;
},
},
render() {
return this.$slots.default;
},
};
</script>
......@@ -183,6 +183,15 @@ export const nextUnresolvedDiscussionId = (state, getters) => (discussionId, dif
return slicedIds.length ? idsOrdered.slice(currentIndex + 1, currentIndex + 2)[0] : idsOrdered[0];
};
export const previousUnresolvedDiscussionId = (state, getters) => (discussionId, diffOrder) => {
const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder);
const currentIndex = idsOrdered.indexOf(discussionId);
const slicedIds = idsOrdered.slice(currentIndex - 1, currentIndex);
// Get the last ID if there is none after the currentIndex
return slicedIds.length ? slicedIds[0] : idsOrdered[idsOrdered.length - 1];
};
// @param {Boolean} diffOrder - is ordered by diff?
export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
if (diffOrder) {
......
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
fragment TreeEntry on Entry {
id
name
......@@ -5,11 +7,6 @@ fragment TreeEntry on Entry {
type
}
fragment PageInfo on PageInfo {
hasNextPage
endCursor
}
query getFiles(
$projectPath: ID!
$path: String
......
......@@ -1216,11 +1216,10 @@ $ide-commit-header-height: 48px;
}
.ide-search-list-empty {
height: 230px;
height: 69px;
}
.ide-merge-requests-dropdown-content {
min-height: 230px;
max-height: 470px;
}
......
......@@ -67,7 +67,7 @@
position: absolute;
right: 4px;
top: 0;
height: 35px;
height: $input-height;
padding-left: 10px;
padding-right: 10px;
color: $gray-darkest;
......
......@@ -5,6 +5,8 @@ class Groups::GroupMembersController < Groups::ApplicationController
include MembersPresentation
include SortingHelper
MEMBER_PER_PAGE_LIMIT = 50
def self.admin_not_required_endpoints
%i[index leave request_access]
end
......@@ -24,7 +26,14 @@ class Groups::GroupMembersController < Groups::ApplicationController
@project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = GroupMembersFinder.new(@group).execute
@members = @members.non_invite unless can_manage_members
if can_manage_members
@invited_members = @members.invite
@invited_members = @invited_members.search_invite_email(params[:search_invited]) if params[:search_invited].present?
@invited_members = present_members(@invited_members.page(params[:invited_members_page]).per(MEMBER_PER_PAGE_LIMIT))
end
@members = @members.non_invite
@members = @members.search(params[:search]) if params[:search].present?
@members = @members.sort_by_attribute(@sort)
......@@ -32,7 +41,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
@members = @members.filter_by_2fa(params[:two_factor])
end
@members = @members.page(params[:page]).per(50)
@members = @members.page(params[:page]).per(MEMBER_PER_PAGE_LIMIT)
@members = present_members(@members)
@requesters = present_members(
......
# frozen_string_literal: true
class Import::BitbucketServerController < Import::BaseController
include ActionView::Helpers::SanitizeHelper
before_action :verify_bitbucket_server_import_enabled
before_action :bitbucket_auth, except: [:new, :configure]
before_action :validate_import_params, only: [:create]
......@@ -57,7 +59,7 @@ class Import::BitbucketServerController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
@collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page)
@collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param)
@repos, @incompatible_repos = @collection.partition { |repo| repo.valid? }
# Use the import URL to filter beyond what BaseService#find_already_added_projects
......@@ -147,4 +149,8 @@ class Import::BitbucketServerController < Import::BaseController
def limit_per_page
BitbucketServer::Paginator::PAGE_LENGTH
end
def sanitized_filter_param
sanitize(params[:filter])
end
end
......@@ -107,6 +107,10 @@ class Member < ApplicationRecord
joins(:user).merge(User.search(query))
end
def search_invite_email(query)
invite.where(['invite_email ILIKE ?', "%#{query}%"])
end
def filter_by_2fa(value)
case value
when 'enabled'
......
......@@ -122,7 +122,7 @@ module Projects
ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError
log_error("Could not create wiki for #{project.full_name}")
Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki')
Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki').increment
end
def update_pages_config
......
- page_title "Members"
- page_title _("Members")
- can_manage_members = can?(current_user, :admin_group_member, @group)
- show_invited_members = can_manage_members && @invited_members.exists?
- pending_active = params[:search_invited].present?
.project-members-page.prepend-top-default
%h4
Members
= _("Members")
%hr
- if can_manage_members
.project-members-new.append-bottom-default
%p.clearfix
Add new member to
%strong= @group.name
= _("Add new member to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
= render "new_group_member"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters
= render_if_exists 'groups/group_members/ldap_sync'
.clearfix
%h5.member.existing-title
Existing members
.card
.card-header.flex-project-members-panel
%span.flex-project-title
Members with access to
%strong= @group.name
%span.badge.badge-pill= @members.total_count
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form flex-users-form' do
.form-group
.position-relative.append-right-8
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.user-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
- if can_manage_members
= render 'shared/members/filter_2fa_dropdown'
= render 'shared/members/sort_dropdown'
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member
= paginate @members, theme: 'gitlab'
%ul.nav-links.mobile-separator.nav.nav-tabs.clearfix
%li.nav-item
= link_to "#existing_members", class: ["nav-link", ("active" unless pending_active)] , 'data-toggle' => 'tab' do
%span
= _("Existing")
%span.badge.badge-pill= @members.total_count
- if show_invited_members
%li.nav-item
= link_to "#invited_members", class: ["nav-link", ("active" if pending_active)], 'data-toggle' => 'tab' do
%span
= _("Pending")
%span.badge.badge-pill= @invited_members.total_count
.tab-content
#existing_members.tab-pane{ :class => ("active" unless pending_active) }
.card.card-without-border
.d-flex.flex-column.flex-md-row.row-content-block.second-block
%span.flex-grow-1.align-self-md-center.col-form-label
= _("Members with access to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form' do
.form-group.flex-grow
.position-relative.mr-md-2
= search_field_tag :search, params[:search], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
%button.user-search-btn.border-left{ type: "submit", "aria-label" => _("Submit search") }
= icon("search")
- if can_manage_members
= label_tag '2fa', '2FA', class: 'col-form-label label-bold pr-md-2'
= render 'shared/members/filter_2fa_dropdown'
= render 'shared/members/sort_dropdown'
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member
= paginate @members, theme: 'gitlab'
- if show_invited_members
#invited_members.tab-pane{ :class => ("active" if pending_active) }
.card.card-without-border
.d-flex.flex-column.flex-md-row.row-content-block.second-block
%span.flex-grow-1
= _("Members with pending access to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form' do
.form-group
.position-relative.mr-md-2
= search_field_tag :search_invited, params[:search_invited], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
%button.user-search-btn.border-left{ type: "submit", "aria-label" => _("Submit search") }
= icon("search")
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @invited_members, as: :member
= paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab'
......@@ -332,7 +332,7 @@
%td.shortcut
%kbd l
%td Change Label
%tbody.hidden-shortcut{ style: 'display:none' }
%tbody.hidden-shortcut.merge_requests{ style: 'display:none' }
%tr
%th
%th Merge Requests
......@@ -368,6 +368,14 @@
\/
%kbd k
%td Move to previous file
%tr
%td.shortcut
%kbd n
%td= _("Move to next unresolved discussion")
%tr
%td.shortcut
%kbd p
%td= _("Move to previous unresolved discussion")
%tbody.hidden-shortcut{ style: 'display:none' }
%tr
%th
......
......@@ -17,8 +17,13 @@
= button_tag class: 'btn btn-import btn-success js-import-all' do
= _('Import all projects')
= icon('spinner spin', class: 'loading-icon')
.btn-group
= link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
.btn-group
= link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
.input-btn-group.float-right
= form_tag status_import_bitbucket_server_path, :method => 'get' do
= text_field_tag :filter, sanitize(params[:filter]), class: 'form-control append-bottom-10', placeholder: _('Filter your projects by name'), size: 40, autoFocus: true
.table-responsive.prepend-top-10
%table.table.import-jobs
......@@ -62,7 +67,7 @@
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend
.input-group-text /
= text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
= text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, required: true
%td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do
Import
......
- filter = params[:two_factor] || 'everyone'
- filter_options = { 'everyone' => _('Everyone'), 'enabled' => _('Enabled'), 'disabled' => _('Disabled') }
.dropdown.inline.member-filter-2fa-dropdown
= dropdown_toggle('2FA: ' + filter_options[filter], { toggle: 'dropdown' })
.dropdown.inline.member-filter-2fa-dropdown.pr-md-2
= dropdown_toggle(filter_options[filter], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
%li.dropdown-header
= _("Filter by two-factor authentication")
......
.dropdown.inline.user-sort-dropdown
= label_tag :sort_by, 'Sort by', class: 'col-form-label label-bold pr-2'
.dropdown.inline.qa-user-sort-dropdown
= dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
......
---
title: Fix flashing conflict warning when editing issues
merge_request: 31469
author:
type: fixed
---
title: Resolve Keyboard shortcut for jump to NEXT unresolved discussion
merge_request: 30144
author:
type: added
---
title: Fix an issue where clicking outside the MR/branch search box in WebIDE closed the dropdown.
merge_request: 31523
author:
type: fixed
---
title: Fix :wiki_can_not_be_created_total counter
merge_request: 31673
author:
type: fixed
---
title: Add BitBucketServer project import filtering
merge_request: 31420
author:
type: added
---
title: 'Fix missing author line (`Created by: <user>`) in MRs/issues/comments of imported Bitbucket Cloud project'
merge_request: 31579
author:
type: fixed
---
title: Make it easier to find invited group members
merge_request: 28436
author:
type: fixed
......@@ -9,7 +9,7 @@ description: 'Learn how to use and administer GitLab, the most scalable Git-base
</div>
<!-- the div above will not display on the docs site but will display on /help -->
# GitLab Documentation
# GitLab Docs
Welcome to [GitLab](https://about.gitlab.com/) Documentation.
......
......@@ -52,7 +52,7 @@ section.
**Admin Area > Users**. You will find the option of the access level under
the 'Access' section.
![Admin Area Form](img/auditor_access_form.png)
![Admin Area Form](img/auditor_access_form.png)
1. Click **Save changes** or **Create user** for the changes to take effect.
......
......@@ -74,43 +74,38 @@ The OpenID Connect will provide you with a client details and secret for you to
}
```
> **Note:**
>
> - For more information on each configuration option refer to
the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage) and
the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html).
NOTE: **Note:**
For more information on each configuration option refer to the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage)
and the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html).
1. For the configuration above, change the values for the provider to match your OpenID Connect client setup. Use the following as a guide:
- `<your_oidc_label>` is the label that will be displayed on the login page.
- `<your_oidc_url>` (optional) is the URL that points to the OpenID Connect provider. For example, `https://example.com/auth/realms/your-realm`.
If this value is not provided, the URL is constructed from the `client_options` in the following format: `<client_options.scheme>://<client_options.host>:<client_options.port>`.
- If `discovery` is set to `true`, the OpenID Connect provider will try to auto discover the client options using `<your_oidc_url>/.well-known/openid-configuration`. Defaults to `false`.
- `client_auth_method` (optional) specifies the method used for authenticating the client with the OpenID Connect provider.
- Supported values are:
- `basic` - HTTP Basic Authentication
- `jwt_bearer` - JWT based authentication (private key and client secret signing)
- `mtls` - Mutual TLS or X.509 certificate validation
- Any other value will POST the client id and secret in the request body
- If not specified, defaults to `basic`.
- `<uid_field>` (optional) is the field name from the `user_info` details that will be used as `uid` value. For example, `preferred_username`.
If this value is not provided or the field with the configured value is missing from the `user_info` details, the `uid` will use the `sub` field.
- `client_options` are the OpenID Connect client-specific options. Specifically:
- `identifier` is the client identifier as configured in the OpenID Connect service provider.
- `secret` is the client secret as configured in the OpenID Connect service provider.
- `redirect_uri` is the GitLab URL to redirect the user to after successful login. For example, `http://example.com/users/auth/openid_connect/callback`.
- `end_session_endpoint` (optional) is the URL to the endpoint that end the session (logout). Can be provided if auto-discovery disabled or unsuccessful.
The following `client_options` are optional unless auto-discovery is disabled or unsuccessful:
- `authorization_endpoint` is the URL to the endpoint that authorizes the end user.
- `token_endpoint` is the URL to the endpoint that provides Access Token.
- `userinfo_endpoint` is the URL to the endpoint that provides the user information.
- `jwks_uri` is the URL to the endpoint where the Token signer publishes its keys.
1. Save the configuration file.
1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source)
for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
- `<your_oidc_label>` is the label that will be displayed on the login page.
- `<your_oidc_url>` (optional) is the URL that points to the OpenID Connect provider. For example, `https://example.com/auth/realms/your-realm`.
If this value is not provided, the URL is constructed from the `client_options` in the following format: `<client_options.scheme>://<client_options.host>:<client_options.port>`.
- If `discovery` is set to `true`, the OpenID Connect provider will try to auto discover the client options using `<your_oidc_url>/.well-known/openid-configuration`. Defaults to `false`.
- `client_auth_method` (optional) specifies the method used for authenticating the client with the OpenID Connect provider.
- Supported values are:
- `basic` - HTTP Basic Authentication
- `jwt_bearer` - JWT based authentication (private key and client secret signing)
- `mtls` - Mutual TLS or X.509 certificate validation
- Any other value will POST the client id and secret in the request body
- If not specified, defaults to `basic`.
- `<uid_field>` (optional) is the field name from the `user_info` details that will be used as `uid` value. For example, `preferred_username`.
If this value is not provided or the field with the configured value is missing from the `user_info` details, the `uid` will use the `sub` field.
- `client_options` are the OpenID Connect client-specific options. Specifically:
- `identifier` is the client identifier as configured in the OpenID Connect service provider.
- `secret` is the client secret as configured in the OpenID Connect service provider.
- `redirect_uri` is the GitLab URL to redirect the user to after successful login. For example, `http://example.com/users/auth/openid_connect/callback`.
- `end_session_endpoint` (optional) is the URL to the endpoint that end the session (logout). Can be provided if auto-discovery disabled or unsuccessful.
- The following `client_options` are optional unless auto-discovery is disabled or unsuccessful:
- `authorization_endpoint` is the URL to the endpoint that authorizes the end user.
- `token_endpoint` is the URL to the endpoint that provides Access Token.
- `userinfo_endpoint` is the URL to the endpoint that provides the user information.
- `jwks_uri` is the URL to the endpoint where the Token signer publishes its keys.
1. Save the configuration file.
1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source)
for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
On the sign in page, there should now be an OpenID Connect icon below the regular sign in form.
Click the icon to begin the authentication process. The OpenID Connect provider will ask the user to
......
......@@ -27,8 +27,6 @@ but not recommended and out of the scope of this document.
Read the [insecure Registry documentation][docker-insecure] if you want to
implement this.
---
**Installations from source**
If you have installed GitLab from source:
......@@ -116,8 +114,6 @@ GitLab from source respectively.
Be careful to choose a port different than the one that Registry listens to (`5000` by default),
otherwise you will run into conflicts.
---
**Omnibus GitLab installations**
1. Your `/etc/gitlab/gitlab.rb` should contain the Registry URL as well as the
......@@ -141,8 +137,6 @@ otherwise you will run into conflicts.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
......@@ -158,8 +152,6 @@ otherwise you will run into conflicts.
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
---
Users should now be able to login to the Container Registry with their GitLab
credentials using:
......@@ -177,8 +169,6 @@ domain (e.g., `registry.gitlab.example.com`).
Let's assume that you want the container Registry to be accessible at
`https://registry.gitlab.example.com`.
---
**Omnibus GitLab installations**
1. Place your TLS certificate and key in
......@@ -210,8 +200,6 @@ look like:
> registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/certificate.key"
> ```
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
......@@ -226,8 +214,6 @@ look like:
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
---
Users should now be able to login to the Container Registry using their GitLab
credentials:
......@@ -252,8 +238,6 @@ Registry application itself.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
......@@ -272,8 +256,6 @@ If the Container Registry is enabled, then it will be available on all new
projects. To disable this function and let the owners of a project to enable
the Container Registry by themselves, follow the steps below.
---
**Omnibus GitLab installations**
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
......@@ -284,8 +266,6 @@ the Container Registry by themselves, follow the steps below.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `default_projects_features`
......@@ -321,8 +301,6 @@ This path is accessible to:
> **Warning** You should confirm that all GitLab, Registry and web server users
have access to this directory.
---
**Omnibus GitLab installations**
The default location where images are stored in Omnibus, is
......@@ -336,8 +314,6 @@ The default location where images are stored in Omnibus, is
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
The default location where images are stored in source installations, is
......@@ -446,8 +422,6 @@ In the examples below we set the Registry's port to `5001`.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
1. Open the configuration file of your Registry server and edit the
......@@ -565,8 +539,6 @@ To configure a notification endpoint in Omnibus:
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
Configuring the notification endpoint is done in your registry config YML file created
......@@ -640,8 +612,6 @@ Start with a value between `25000000` (25MB) and `50000000` (50MB).
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**For installations from source**
1. Edit `config/gitlab.yml`:
......@@ -673,8 +643,6 @@ You can add a configuration option for backwards compatibility.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**For installations from source**
1. Edit the YML configuration file you created when you [deployed the registry][registry-deploy]. Add the following snippet:
......
......@@ -14,14 +14,8 @@ Fetching large repositories can take a long time for teams located far from a si
Geo provides local, read-only instances of your GitLab instances, reducing the time it takes to clone and fetch large repositories and speeding up development.
> **Notes:**
>
> - Geo is part of [GitLab Premium](https://about.gitlab.com/pricing/#self-managed).
> - We recommend you use:
> - At least GitLab Enterprise Edition 10.0 for basic Geo features.
> - The latest version for a better experience.
> - Make sure that all nodes run the same GitLab version.
> - Geo requires PostgreSQL 9.6 and Git 2.9, in addition to GitLab's usual [minimum requirements](../../../install/requirements.md).
NOTE: **Note:**
Check the [requirements](#requirements-for-running-geo) carefully before setting up Geo.
For a video introduction to Geo, see [Introduction to GitLab Geo - GitLab Features](https://www.youtube.com/watch?v=-HDLxSjEh6w).
......@@ -117,6 +111,13 @@ The following are required to run Geo:
- [Ubuntu](https://www.ubuntu.com) 16.04+
- PostgreSQL 9.6+ with [FDW](https://www.postgresql.org/docs/9.6/postgres-fdw.html) support and [Streaming Replication](https://wiki.postgresql.org/wiki/Streaming_Replication)
- Git 2.9+
- All nodes must run the same GitLab version.
Additionally, check GitLab's [minimum requirements](../../../install/requirements.md),
and we recommend you use:
- At least GitLab Enterprise Edition 10.0 for basic Geo features.
- The latest version for a better experience.
### Firewall rules
......
......@@ -20,84 +20,85 @@ It is recommended to run pgbouncer alongside the `gitlab-rails` service, or on i
1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section:
```ruby
# Disable all components except Pgbouncer and Consul agent
roles ['pgbouncer_role']
# Configure Pgbouncer
pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
# Configure Consul agent
consul['watchers'] = %w(postgresql)
# START user configuration
# Please set the real values as explained in Required Information section
# Replace CONSUL_PASSWORD_HASH with with a generated md5 value
# Replace PGBOUNCER_PASSWORD_HASH with with a generated md5 value
pgbouncer['users'] = {
'gitlab-consul': {
password: 'CONSUL_PASSWORD_HASH'
},
'pgbouncer': {
password: 'PGBOUNCER_PASSWORD_HASH'
}
}
# Replace placeholders:
#
# Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
# with the addresses gathered for CONSUL_SERVER_NODES
consul['configuration'] = {
retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
}
#
# END user configuration
```
> `pgbouncer_role` was introduced with GitLab 10.3
1. Run `gitlab-ctl reconfigure`
```ruby
# Disable all components except Pgbouncer and Consul agent
roles ['pgbouncer_role']
# Configure Pgbouncer
pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
# Configure Consul agent
consul['watchers'] = %w(postgresql)
# START user configuration
# Please set the real values as explained in Required Information section
# Replace CONSUL_PASSWORD_HASH with with a generated md5 value
# Replace PGBOUNCER_PASSWORD_HASH with with a generated md5 value
pgbouncer['users'] = {
'gitlab-consul': {
password: 'CONSUL_PASSWORD_HASH'
},
'pgbouncer': {
password: 'PGBOUNCER_PASSWORD_HASH'
}
}
# Replace placeholders:
#
# Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
# with the addresses gathered for CONSUL_SERVER_NODES
consul['configuration'] = {
retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
}
#
# END user configuration
```
NOTE: **Note:**
`pgbouncer_role` was introduced with GitLab 10.3.
1. Run `gitlab-ctl reconfigure`
1. Create a `.pgpass` file so Consul is able to
reload pgbouncer. Enter the `PGBOUNCER_PASSWORD` twice when asked:
```sh
gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
```
```sh
gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
```
#### PGBouncer Checkpoint
1. Ensure the node is talking to the current master:
```sh
gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD
```
```sh
gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD
```
If there is an error `psql: ERROR: Auth failed` after typing in the
password, ensure you previously generated the MD5 password hashes with the correct
format. The correct format is to concatenate the password and the username:
`PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text
needed to generate an MD5 password hash for the `pgbouncer` user.
If there is an error `psql: ERROR: Auth failed` after typing in the
password, ensure you previously generated the MD5 password hashes with the correct
format. The correct format is to concatenate the password and the username:
`PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text
needed to generate an MD5 password hash for the `pgbouncer` user.
1. Once the console prompt is available, run the following queries:
```sh
show databases ; show clients ;
```
The output should be similar to the following:
```sh
show databases ; show clients ;
```
```
name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0
pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0
(2 rows)
The output should be similar to the following:
type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls
------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+-----
C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 |
(2 rows)
```
```
name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0
pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0
(2 rows)
type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls
------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+-----
C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 |
(2 rows)
```
### Running Pgbouncer as part of a non-HA GitLab installation
......