Commit 6e8d50d9 authored by Achilleas Pipinellis's avatar Achilleas Pipinellis

Merge branch 'ce-to-ee-2018-07-03' into 'master'

CE upstream - 2018-07-03 09:22 UTC

Closes #1355, gitaly#801, gitaly#779, gitaly#755, gitaly#754, gitaly#727, gitaly#680, gitaly#633, gitaly#547, gitaly#546, gitaly#541, gitaly#1256, #6224, and gitlab-ce#46120

See merge request gitlab-org/gitlab-ee!6359
parents 52cf1722 605f69ab
...@@ -65,7 +65,7 @@ We're hiring developers, support people, and production engineers all the time, ...@@ -65,7 +65,7 @@ We're hiring developers, support people, and production engineers all the time,
There are two editions of GitLab: There are two editions of GitLab:
- GitLab Community Edition (CE) is available freely under the MIT Expat license. - GitLab Community Edition (CE) is available freely under the MIT Expat license.
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/products/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/products/). - GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/pricing/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
## Website ## Website
......
...@@ -12,7 +12,7 @@ export const defaultAutocompleteConfig = { ...@@ -12,7 +12,7 @@ export const defaultAutocompleteConfig = {
members: true, members: true,
issues: true, issues: true,
mergeRequests: true, mergeRequests: true,
epics: false, epics: true,
milestones: true, milestones: true,
labels: true, labels: true,
}; };
...@@ -493,6 +493,7 @@ GfmAutoComplete.atTypeMap = { ...@@ -493,6 +493,7 @@ GfmAutoComplete.atTypeMap = {
'@': 'members', '@': 'members',
'#': 'issues', '#': 'issues',
'!': 'mergeRequests', '!': 'mergeRequests',
'&': 'epics',
'~': 'labels', '~': 'labels',
'%': 'milestones', '%': 'milestones',
'/': 'commands', '/': 'commands',
......
...@@ -9,6 +9,13 @@ export default class GLForm { ...@@ -9,6 +9,13 @@ export default class GLForm {
this.form = form; this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input'); this.textarea = this.form.find('textarea.js-gfm-input');
this.enableGFM = Object.assign({}, GFMConfig.defaultAutocompleteConfig, enableGFM); this.enableGFM = Object.assign({}, GFMConfig.defaultAutocompleteConfig, enableGFM);
// Disable autocomplete for keywords which do not have dataSources available
const dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
Object.keys(this.enableGFM).forEach(item => {
if (item !== 'emojis') {
this.enableGFM[item] = !!dataSources[item];
}
});
// Before we start, we should clean up any previous data for this form // Before we start, we should clean up any previous data for this form
this.destroy(); this.destroy();
// Setup the form // Setup the form
......
...@@ -8,6 +8,7 @@ export default () => { ...@@ -8,6 +8,7 @@ export default () => {
members: false, members: false,
issues: false, issues: false,
mergeRequests: false, mergeRequests: false,
epics: false,
milestones: false, milestones: false,
labels: false, labels: false,
}); });
......
...@@ -8,10 +8,11 @@ export default (initGFM = true) => { ...@@ -8,10 +8,11 @@ export default (initGFM = true) => {
new DueDateSelectors(); // eslint-disable-line no-new new DueDateSelectors(); // eslint-disable-line no-new
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new GLForm($('.milestone-form'), { new GLForm($('.milestone-form'), {
emojis: initGFM, emojis: true,
members: initGFM, members: initGFM,
issues: initGFM, issues: initGFM,
mergeRequests: initGFM, mergeRequests: initGFM,
epics: initGFM,
milestones: initGFM, milestones: initGFM,
labels: initGFM, labels: initGFM,
}); });
......
<script> <script>
import Icon from '~/vue_shared/components/icon.vue';
import timeagoMixin from '../../vue_shared/mixins/timeago'; import timeagoMixin from '../../vue_shared/mixins/timeago';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
import LoadingButton from '../../vue_shared/components/loading_button.vue'; import LoadingButton from '../../vue_shared/components/loading_button.vue';
...@@ -14,6 +15,7 @@ export default { ...@@ -14,6 +15,7 @@ export default {
LoadingButton, LoadingButton,
MemoryUsage, MemoryUsage,
StatusIcon, StatusIcon,
Icon,
}, },
directives: { directives: {
tooltip, tooltip,
...@@ -110,11 +112,10 @@ export default { ...@@ -110,11 +112,10 @@ export default {
class="deploy-link js-deploy-url" class="deploy-link js-deploy-url"
> >
{{ deployment.external_url_formatted }} {{ deployment.external_url_formatted }}
<i <icon
class="fa fa-external-link" :size="16"
aria-hidden="true" name="external-link"
> />
</i>
</a> </a>
</template> </template>
<span <span
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
}; };
</script> </script>
<template> <template>
<div class="space-children flex-container-block append-right-10"> <div class="space-children d-flex append-right-10">
<div <div
v-if="isLoading" v-if="isLoading"
class="mr-widget-icon" class="mr-widget-icon"
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
<div class="mr-widget-body media"> <div class="mr-widget-body media">
<status-icon status="success" /> <status-icon status="success" />
<div class="media-body"> <div class="media-body">
<h4 class="flex-container-block"> <h4 class="d-flex align-items-start">
<span class="append-right-10"> <span class="append-right-10">
{{ s__("mrWidget|Set by") }} {{ s__("mrWidget|Set by") }}
<mr-widget-author :author="mr.setToMWPSBy" /> <mr-widget-author :author="mr.setToMWPSBy" />
...@@ -119,7 +119,7 @@ ...@@ -119,7 +119,7 @@
</p> </p>
<p <p
v-else v-else
class="flex-container-block" class="d-flex align-items-start"
> >
<span class="append-right-10"> <span class="append-right-10">
{{ s__("mrWidget|The source branch will not be removed") }} {{ s__("mrWidget|The source branch will not be removed") }}
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
members: this.enableAutocomplete, members: this.enableAutocomplete,
issues: this.enableAutocomplete, issues: this.enableAutocomplete,
mergeRequests: this.enableAutocomplete, mergeRequests: this.enableAutocomplete,
epics: this.enableAutocomplete,
milestones: this.enableAutocomplete, milestones: this.enableAutocomplete,
labels: this.enableAutocomplete, labels: this.enableAutocomplete,
}); });
......
...@@ -350,11 +350,6 @@ ...@@ -350,11 +350,6 @@
} }
} }
.flex-container-block {
display: -webkit-flex;
display: flex;
}
.flex-right { .flex-right {
margin-left: auto; margin-left: auto;
} }
...@@ -266,12 +266,7 @@ li.note { ...@@ -266,12 +266,7 @@ li.note {
} }
.milestone { .milestone {
&.milestone-closed {
background: $gray-light;
}
.progress { .progress {
margin-bottom: 0;
margin-top: 4px; margin-top: 4px;
box-shadow: none; box-shadow: none;
background-color: $border-gray-light; background-color: $border-gray-light;
......
...@@ -68,8 +68,7 @@ ...@@ -68,8 +68,7 @@
} }
.nav-sidebar { .nav-sidebar {
transition: width $sidebar-transition-duration, transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
left $sidebar-transition-duration;
position: fixed; position: fixed;
z-index: 400; z-index: 400;
width: $contextual-sidebar-width; width: $contextual-sidebar-width;
...@@ -77,12 +76,12 @@ ...@@ -77,12 +76,12 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
background-color: $gray-light; background-color: $gray-light;
box-shadow: inset -2px 0 0 $border-color; box-shadow: inset -1px 0 0 $border-color;
transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0);
&:not(.sidebar-collapsed-desktop) { &:not(.sidebar-collapsed-desktop) {
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) { @media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
box-shadow: inset -2px 0 0 $border-color, box-shadow: inset -1px 0 0 $border-color,
2px 1px 3px $dropdown-shadow-color; 2px 1px 3px $dropdown-shadow-color;
} }
} }
...@@ -214,7 +213,7 @@ ...@@ -214,7 +213,7 @@
> li { > li {
> a { > a {
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
margin-right: 2px; margin-right: 1px;
} }
&:hover { &:hover {
...@@ -224,7 +223,7 @@ ...@@ -224,7 +223,7 @@
&.is-showing-fly-out { &.is-showing-fly-out {
> a { > a {
margin-right: 2px; margin-right: 1px;
} }
.sidebar-sub-level-items { .sidebar-sub-level-items {
...@@ -317,14 +316,14 @@ ...@@ -317,14 +316,14 @@
.toggle-sidebar-button, .toggle-sidebar-button,
.close-nav-button { .close-nav-button {
width: $contextual-sidebar-width - 2px; width: $contextual-sidebar-width - 1px;
transition: width $sidebar-transition-duration; transition: width $sidebar-transition-duration;
position: fixed; position: fixed;
bottom: 0; bottom: 0;
padding: $gl-padding; padding: $gl-padding;
background-color: $gray-light; background-color: $gray-light;
border: 0; border: 0;
border-top: 2px solid $border-color; border-top: 1px solid $border-color;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -379,7 +378,7 @@ ...@@ -379,7 +378,7 @@
.toggle-sidebar-button { .toggle-sidebar-button {
padding: 16px; padding: 16px;
width: $contextual-sidebar-collapsed-width - 2px; width: $contextual-sidebar-collapsed-width - 1px;
.collapse-text, .collapse-text,
.icon-angle-double-left { .icon-angle-double-left {
......
...@@ -45,4 +45,9 @@ ...@@ -45,4 +45,9 @@
&.status-box-upcoming { &.status-box-upcoming {
background: $gl-text-color-secondary; background: $gl-text-color-secondary;
} }
&.status-box-milestone {
color: $gl-text-color;
background: $gray-darker;
}
} }
...@@ -265,13 +265,17 @@ ...@@ -265,13 +265,17 @@
vertical-align: baseline; vertical-align: baseline;
} }
a.autodevops-badge { a {
color: $gl-text-color;
&.autodevops-badge {
color: $white-light; color: $white-light;
} }
a.autodevops-link { &.autodevops-link {
color: $gl-link-color; color: $gl-link-color;
} }
}
.commit-row-description { .commit-row-description {
font-size: 14px; font-size: 14px;
......
...@@ -79,6 +79,7 @@ ...@@ -79,6 +79,7 @@
justify-content: space-between; justify-content: space-between;
padding: $gl-padding; padding: $gl-padding;
border-radius: $border-radius-default; border-radius: $border-radius-default;
border: 1px solid $theme-gray-100;
&.sortable-ghost { &.sortable-ghost {
opacity: 0.3; opacity: 0.3;
...@@ -89,6 +90,7 @@ ...@@ -89,6 +90,7 @@
cursor: move; cursor: move;
cursor: -webkit-grab; cursor: -webkit-grab;
cursor: -moz-grab; cursor: -moz-grab;
border: 0;
&:active { &:active {
cursor: -webkit-grabbing; cursor: -webkit-grabbing;
......
...@@ -737,6 +737,10 @@ ...@@ -737,6 +737,10 @@
> *:not(:last-child) { > *:not(:last-child) {
margin-right: .3em; margin-right: .3em;
} }
svg {
vertical-align: text-top;
}
} }
.deploy-link { .deploy-link {
......
...@@ -3,8 +3,20 @@ ...@@ -3,8 +3,20 @@
} }
.milestones { .milestones {
padding: $gl-padding-8;
margin-top: $gl-padding-8;
border-radius: $border-radius-default;
background-color: $theme-gray-100;
.milestone { .milestone {
padding: 10px 16px; border: 0;
padding: $gl-padding-top $gl-padding;
border-radius: $border-radius-default;
background-color: $white-light;
&:not(:last-child) {
margin-bottom: $gl-padding-4;
}
h4 { h4 {
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
...@@ -13,6 +25,24 @@ ...@@ -13,6 +25,24 @@
.progress { .progress {
width: 100%; width: 100%;
height: 6px; height: 6px;
margin-bottom: $gl-padding-4;
}
.milestone-progress {
a {
color: $gl-link-color;
}
}
.status-box {
font-size: $tooltip-font-size;
margin-top: 0;
margin-right: $gl-padding-4;
@include media-breakpoint-down(xs) {
line-height: unset;
padding: $gl-padding-4 $gl-input-padding;
}
} }
} }
} }
...@@ -229,6 +259,10 @@ ...@@ -229,6 +259,10 @@
} }
} }
.milestone-range {
color: $gl-text-color-tertiary;
}
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
.milestone-banner-text, .milestone-banner-text,
.milestone-banner-link { .milestone-banner-link {
......
...@@ -77,7 +77,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -77,7 +77,7 @@ class Projects::MilestonesController < Projects::ApplicationController
def promote def promote
promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone) promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone)
flash[:notice] = "#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, promoted_milestone.iid)}\">group milestone</a>.".html_safe flash[:notice] = "#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, promoted_milestone.iid)}\"><u>group milestone</u></a>.".html_safe
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_to project_milestones_path(project) redirect_to project_milestones_path(project)
......
...@@ -150,6 +150,7 @@ module NotesHelper ...@@ -150,6 +150,7 @@ module NotesHelper
members: autocomplete, members: autocomplete,
issues: autocomplete, issues: autocomplete,
mergeRequests: autocomplete, mergeRequests: autocomplete,
epics: autocomplete,
milestones: autocomplete, milestones: autocomplete,
labels: autocomplete labels: autocomplete
} }
......
...@@ -132,15 +132,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -132,15 +132,10 @@ class MergeRequest < ActiveRecord::Base
end end
after_transition unchecked: :cannot_be_merged do |merge_request, transition| after_transition unchecked: :cannot_be_merged do |merge_request, transition|
begin
if merge_request.notify_conflict? if merge_request.notify_conflict?
NotificationService.new.merge_request_unmergeable(merge_request) NotificationService.new.merge_request_unmergeable(merge_request)
TodoService.new.merge_request_became_unmergeable(merge_request) TodoService.new.merge_request_became_unmergeable(merge_request)
end end
rescue Gitlab::Git::CommandError
# Checking mergeability can trigger exception, e.g. non-utf8
# We ignore this type of errors.
end
end end
def check_state?(merge_status) def check_state?(merge_status)
...@@ -713,7 +708,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -713,7 +708,14 @@ class MergeRequest < ActiveRecord::Base
end end
def notify_conflict? def notify_conflict?
(opened? || locked?) && !project.repository.can_be_merged?(diff_head_sha, target_branch) (opened? || locked?) &&
has_commits? &&
!branch_missing? &&
!project.repository.can_be_merged?(diff_head_sha, target_branch)
rescue Gitlab::Git::CommandError
# Checking mergeability can trigger exception, e.g. non-utf8
# We ignore this type of errors.
false
end end
def related_notes def related_notes
......
...@@ -67,11 +67,11 @@ class BambooService < CiService ...@@ -67,11 +67,11 @@ class BambooService < CiService
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
get_path("updateAndBuild.action?buildKey=#{build_key}") get_path("updateAndBuild.action", { buildKey: build_key })
end end
def calculate_reactive_cache(sha, ref) def calculate_reactive_cache(sha, ref)
response = get_path("rest/api/latest/result?label=#{sha}") response = get_path("rest/api/latest/result/byChangeset/#{sha}")
{ build_page: read_build_page(response), commit_status: read_commit_status(response) } { build_page: read_build_page(response), commit_status: read_commit_status(response) }
end end
...@@ -113,14 +113,16 @@ class BambooService < CiService ...@@ -113,14 +113,16 @@ class BambooService < CiService
URI.join("#{bamboo_url}/", path).to_s URI.join("#{bamboo_url}/", path).to_s
end end
def get_path(path) def get_path(path, query_params = {})
url = build_url(path) url = build_url(path)
if username.blank? && password.blank? if username.blank? && password.blank?
Gitlab::HTTP.get(url, verify: false) Gitlab::HTTP.get(url, verify: false, query: query_params)
else else
url << '&os_authType=basic' query_params[:os_authType] = 'basic'
Gitlab::HTTP.get(url, verify: false, Gitlab::HTTP.get(url,
verify: false,
query: query_params,
basic_auth: { basic_auth: {
username: username, username: username,
password: password password: password
......
- if milestone.expired? and not milestone.closed? - if milestone.expired? and not milestone.closed?
%span.cred (Expired) .status-box.status-box-expired.append-bottom-5 Expired
- if milestone.upcoming? - if milestone.upcoming?
%span.clgray (Upcoming) .status-box.status-box-mr-merged.append-bottom-5 Upcoming
- if milestone.due_date || milestone.start_date - if milestone.closed?
%span .status-box.status-box-closed.append-bottom-5 Closed
= milestone_date_range(milestone)
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%div{ class: div_class } %div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true, = form.text_field :title, required: true, maxlength: 255, autofocus: true,
autocomplete: 'off', class: 'form-control pad qa-issuable-form-title' autocomplete: 'off', class: 'form-control pad qa-issuable-form-title', placeholder: _('Title')
- if issuable.respond_to?(:work_in_progress?) - if issuable.respond_to?(:work_in_progress?)
%p.form-text.text-muted %p.form-text.text-muted
......
- dashboard = local_assigns[:dashboard] - dashboard = local_assigns[:dashboard]
- custom_dom_id = dom_id(milestone.try(:milestones) ? milestone.milestones.first : milestone) - custom_dom_id = dom_id(milestone.try(:milestones) ? milestone.milestones.first : milestone)
- milestone_type = milestone.group_milestone? ? 'Group Milestone' : 'Project Milestone'
%li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } %li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id }
.row .row
.col-sm-6 .col-sm-6
.append-bottom-5
%strong= link_to truncate(milestone.title, length: 100), milestone_path %strong= link_to truncate(milestone.title, length: 100), milestone_path
- if milestone.group_milestone? - if @group
%span - Group Milestone = " - #{milestone_type}"
- else
%span - Project Milestone
.col-sm-6 - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
.float-right.light #{milestone.percent_complete(current_user)}% complete - if milestone.due_date || milestone.start_date
.row .milestone-range.append-bottom-5
.col-sm-6 = milestone_date_range(milestone)
= link_to pluralize(milestone.total_issues_count(current_user), 'Issue'), issues_path %div
&middot; = render('shared/milestone_expired', milestone: milestone)
= link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path
.col-sm-6= milestone_progress_bar(milestone)
- if milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
.row
.col-sm-6
- if milestone.legacy_group_milestone? - if milestone.legacy_group_milestone?
.expiration= render('shared/milestone_expired', milestone: milestone)
.projects .projects
- milestone.milestones.each do |milestone| - milestone.milestones.each do |milestone|
= link_to milestone_path(milestone) do = link_to milestone_path(milestone) do
%span.badge.badge-gray %span.label-badge.label-badge-blue.d-inline-block.append-bottom-5
= dashboard ? milestone.project.full_name : milestone.project.name = dashboard ? milestone.project.full_name : milestone.project.name
- if @group
.col-sm-6.milestone-actions
- if can?(current_user, :admin_milestones, @group)
- if milestone.group_milestone?
= link_to edit_group_milestone_path(@group, milestone), class: "btn btn-sm btn-grouped" do
Edit
\
- if milestone.closed?
= link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- else
= link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close"
.col-sm-4.milestone-progress
= milestone_progress_bar(milestone)
= link_to pluralize(milestone.total_issues_count(current_user), 'Issue'), issues_path
&middot;
= link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path
.float-lg-right.light #{milestone.percent_complete(current_user)}% complete
.col-sm-2
.milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
- if @project - if @project
.row
.col-sm-6
= render('shared/milestone_expired', milestone: milestone)
.col-sm-6.milestone-actions
- if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
= link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-sm btn-grouped" do
Edit
\
- if @project.group - if @project.group
%button.js-promote-project-milestone-button.btn.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), %button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'),
disabled: true, disabled: true,
type: 'button', type: 'button',
data: { url: promote_project_milestone_path(milestone.project, milestone), data: { url: promote_project_milestone_path(milestone.project, milestone),
...@@ -60,17 +43,17 @@ ...@@ -60,17 +43,17 @@
target: '#promote-milestone-modal', target: '#promote-milestone-modal',
container: 'body', container: 'body',
toggle: 'modal' } } toggle: 'modal' } }
= _('Promote') = sprite_icon('level-up', size: 14)
= link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped"
- unless milestone.active?
%button.js-delete-milestone-button.btn.btn-sm.btn-grouped.btn-danger{ data: { toggle: 'modal', = link_to 'Reopen Milestone', project_milestone_path(@project, milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
target: '#delete-milestone-modal', - if @group
milestone_id: milestone.id, - if can?(current_user, :admin_milestones, @group)
milestone_title: markdown_field(milestone, :title), - if milestone.closed?
milestone_url: project_milestone_path(milestone.project, milestone), = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
milestone_issue_count: milestone.issues.count, - else
milestone_merge_request_count: milestone.merge_requests.count }, = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close"
disabled: true } - if dashboard
= _('Delete') .status-box.status-box-milestone
= icon('spin spinner', class: 'js-loading-icon hidden' ) = milestone_type
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
- cronjob:ci_archive_traces_cron - cronjob:ci_archive_traces_cron
- cronjob:trending_projects - cronjob:trending_projects
- cronjob:issue_due_scheduler - cronjob:issue_due_scheduler
- cronjob:prune_web_hook_logs
- gcp_cluster:cluster_install_app - gcp_cluster:cluster_install_app
- gcp_cluster:cluster_provision - gcp_cluster:cluster_provision
......
# frozen_string_literal: true
# Worker that deletes a fixed number of outdated rows from the "web_hook_logs"
# table.
class PruneWebHookLogsWorker
include ApplicationWorker
include CronjobQueue
# The maximum number of rows to remove in a single job.
DELETE_LIMIT = 50_000
def perform
# MySQL doesn't allow "DELETE FROM ... WHERE id IN ( ... )" if the inner
# query refers to the same table. To work around this we wrap the IN body in
# another sub query.
WebHookLog
.where(
'id IN (SELECT id FROM (?) ids_to_remove)',
WebHookLog
.select(:id)
.where('created_at < ?', 90.days.ago.beginning_of_day)
.limit(DELETE_LIMIT)
)
.delete_all
end
end
---
title: Milestone page list redesign
merge_request: 19832
author: Constance Okoghenun
type: changed
---
title: Fix merge request page rendering error when its target/source branch is missing
merge_request: 20280
author:
type: fixed
---
title: Add title placeholder for new issues
merge_request: 20271
author: George Tsiolis
type: changed
---
title: Fix merge request diffs when created with gitaly_diff_between enabled
merge_request:
author:
type: fixed
---
title: Updated last commit link color
merge_request: 20234
author: Constance Okoghenun
type: fixed
---
title: Don't show context button for diffs of deleted files.
merge_request:
author:
type: fixed
---
title: Prune web hook logs older than 90 days
merge_request:
author:
type: added
---
title: Fix Bamboo CI status not showing for branch plans
merge_request:
author:
type: fixed
---
title: Update external link icon in merge request widget
merge_request: 20154
author: George Tsiolis
type: changed
# See https://github.com/jnicklas/carrierwave#using-amazon-s3
# for more options
# If you change this file in a Merge Request, please also create
# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
#
production:
access_key_id: AKIA1111111111111UA
secret_access_key: secret
bucket: mygitlab.production.us
region: us-east-1
development:
access_key_id: AKIA1111111111111UA
secret_access_key: secret
bucket: mygitlab.development.us
region: us-east-1
test:
access_key_id: AKIA1111111111111UA
secret_access_key: secret
bucket: mygitlab.test.us
region: us-east-1
...@@ -398,6 +398,10 @@ Settings.cron_jobs['issue_due_scheduler_worker'] ||= Settingslogic.new({}) ...@@ -398,6 +398,10 @@ Settings.cron_jobs['issue_due_scheduler_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['issue_due_scheduler_worker']['cron'] ||= '50 00 * * *' Settings.cron_jobs['issue_due_scheduler_worker']['cron'] ||= '50 00 * * *'
Settings.cron_jobs['issue_due_scheduler_worker']['job_class'] = 'IssueDueSchedulerWorker' Settings.cron_jobs['issue_due_scheduler_worker']['job_class'] = 'IssueDueSchedulerWorker'
Settings.cron_jobs['prune_web_hook_logs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['prune_web_hook_logs_worker']['cron'] ||= '0 */1 * * *'
Settings.cron_jobs['prune_web_hook_logs_worker']['job_class'] = 'PruneWebHookLogsWorker'
# #
# Sidekiq # Sidekiq
# #
......
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
aws_file = Rails.root.join('config', 'aws.yml')
if File.exist?(aws_file)
AWS_CONFIG = YAML.load(File.read(aws_file))[Rails.env]
CarrierWave.configure do |config|
config.fog_provider = 'fog/aws'
config.fog_credentials = {
provider: 'AWS', # required
aws_access_key_id: AWS_CONFIG['access_key_id'], # required
aws_secret_access_key: AWS_CONFIG['secret_access_key'], # required
region: AWS_CONFIG['region'], # optional, defaults to 'us-east-1'
}
# required
config.fog_directory = AWS_CONFIG['bucket']
# optional, defaults to true
config.fog_public = false
# optional, defaults to {}
config.fog_attributes = { 'Cache-Control' => 'max-age=315576000' }
# optional time (in seconds) that authenticated urls will be valid.
# when fog_public is false and provider is AWS or Google, defaults to 600
config.fog_authenticated_url_expiration = 1 << 29
end
end
...@@ -252,7 +252,7 @@ straight away. ...@@ -252,7 +252,7 @@ straight away.
### GitLab self-hosted ### GitLab self-hosted
With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/products/): Core, Starter, Premium, and Ultimate. With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/pricing/): Core, Starter, Premium, and Ultimate.
Every feature available in Core is also available in Starter, Premium, and Ultimate. Every feature available in Core is also available in Starter, Premium, and Ultimate.
Starter features are also available in Premium and Ultimate, and Premium features are also Starter features are also available in Premium and Ultimate, and Premium features are also
......
...@@ -11,7 +11,7 @@ Regular users don't have access to GitLab administration tools and settings. ...@@ -11,7 +11,7 @@ Regular users don't have access to GitLab administration tools and settings.
GitLab has two product distributions: the open source GitLab has two product distributions: the open source
[GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce), [GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce),
and the open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee), and the open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee),
available through [different subscriptions](https://about.gitlab.com/products/). available through [different subscriptions](https://about.gitlab.com/pricing/).
You can [install GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/), You can [install GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/),
but the features you'll have access to depend on the subscription you choose but the features you'll have access to depend on the subscription you choose
......
...@@ -91,7 +91,7 @@ _The artifacts are stored by default in ...@@ -91,7 +91,7 @@ _The artifacts are stored by default in
- [Introduced][ee-1762] in [GitLab Premium][eep] 9.4. - [Introduced][ee-1762] in [GitLab Premium][eep] 9.4.
- Since version 9.5, artifacts are [browsable], when object storage is enabled. - Since version 9.5, artifacts are [browsable], when object storage is enabled.
9.4 lacks this feature. 9.4 lacks this feature.
> Since version 10.6, available in [GitLab CE](https://about.gitlab.com/products/) > Since version 10.6, available in [GitLab Core](https://about.gitlab.com/pricing/)
> Since version 11.0, we support direct_upload to S3. > 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 If you don't want to use the local disk where GitLab is installed to store the
......
# Job traces (logs) # Job traces (logs)
By default, all job traces (logs) are saved to `/var/opt/gitlab/gitlab-ci/builds` Job traces are sent by GitLab Runner while it's processing a job. You can see
and `/home/git/gitlab/builds` for Omnibus packages and installations from source traces in job pages, pipelines, email notifications, etc.
respectively. The job logs are organized by year and month (for example, `2017_03`),
and then by project ID.
There isn't a way to automatically expire old job logs, but it's safe to remove There isn't a way to automatically expire old job logs, but it's safe to remove
them if they're taking up too much space. If you remove the logs manually, the them if they're taking up too much space. If you remove the logs manually, the
job output in the UI will be empty. job output in the UI will be empty.
## Changing the job traces location ## Data flow
In general, there are two states in job traces: "live trace" and "archived trace".
In the following table you can see the phases a trace goes through.
| Phase | State | Condition | Data flow | Stored path |
| ----- | ----- | --------- | --------- | ----------- |
| 1: patching | Live trace | When a job is running | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`|
| 2: overwriting | Live trace | When a job is finished | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`|
| 3: archiving | Archived trace | After a job is finished | Sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`|
| 4: uploading | Archived trace | After a trace is archived | Sidekiq moves archived trace to [object storage](#uploading-traces-to-object-storage) (if configured) |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`|
The `ROOT_PATH` varies per your environment. For Omnibus GitLab it
would be `/var/opt/gitlab/gitlab-ci`, whereas for installations from source
it would be `/home/git/gitlab`.
## Changing the job traces local location
To change the location where the job logs will be stored, follow the steps below. To change the location where the job logs will be stored, follow the steps below.
...@@ -41,97 +55,110 @@ To change the location where the job logs will be stored, follow the steps below ...@@ -41,97 +55,110 @@ To change the location where the job logs will be stored, follow the steps below
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab" [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab" [restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
## Uploading traces to object storage
An archived trace is considered as a [job artifact](job_artifacts.md).
Therefore, when you [set up an object storage](job_artifacts.md#object-storage-settings),
job traces are automatically migrated to it along with the other job artifacts.
See [Data flow](#data-flow) to learn about the process.
## New live trace architecture ## New live trace architecture
> [Introduced][ce-18169] in GitLab 10.4. > [Introduced][ce-18169] in GitLab 10.4.
> [Announced as General availability][ce-46097] in GitLab 11.0.
> **Notes**: NOTE: **Note:**
- This feature is still Beta, which could impact GitLab.com/on-premises instances, and in the worst case scenario, traces will be lost. This feature is off by default. Check below how to [enable/disable](#enabling-live-trace) it.
- This feature is still being discussed in [an issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46097) for the performance improvements.
- This feature is off by default. Please check below how to enable/disable this featrue.
**What is "live trace"?** By combining the process with object storage settings, we can completely bypass
the local file storage. This is a useful option if GitLab is installed as
cloud-native, for example on Kubernetes.
Job trace that is sent by runner while jobs are running. You can see live trace in job pages UI. The data flow is the same as described in the [data flow section](#data-flow)
The live traces are archived once job finishes. with one change: _the stored path of the first two phases is different_. This new live
trace architecture stores chunks of traces in Redis and the database instead of
file storage. Redis is used as first-class storage, and it stores up-to 128KB
of data. Once the full chunk is sent, it is flushed to database. After a while,
the data in Redis and database will be archived to [object storage](#uploading-traces-to-object-storage).
**What is new architecture?** The data are stored in the following Redis namespace: `Gitlab::Redis::SharedState`.
So far, when GitLab Runner sends a job trace to GitLab-Rails, traces have been saved to file storage as text files. Here is the detailed data flow:
This was a problem for [Cloud Native-compatible GitLab application](https://gitlab.com/gitlab-com/migration/issues/23) where GitLab had to rely on File Storage.
This new live trace architecture stores chunks of traces in Redis and database instead of file storage. 1. GitLab Runner picks a job from GitLab
Redis is used as first-class storage, and it stores up-to 128kB. Once the full chunk is sent it will be flushed to database. Afterwhile, the data in Redis and database will be archived to ObjectStorage. 1. GitLab Runner sends a piece of trace to GitLab
1. GitLab appends the data to Redis
1. Once the data in Redis reach 128KB, the data is flushed to the database.
1. The above steps are repeated until the job is finished.
1. Once the job is finished, GitLab schedules a Sidekiq worker to archive the trace.
1. The Sidekiq worker archives the trace to object storage and cleans up the trace
in Redis and the database.
Here is the detailed data flow. ### Enabling live trace
1. GitLab Runner picks a job from GitLab-Rails The following commands are to be issues in a Rails console:
1. GitLab Runner sends a piece of trace to GitLab-Rails
1. GitLab-Rails appends the data to Redis
1. If the data in Redis is fulfilled 128kB, the data is flushed to Database.
1. 2.~4. is continued until the job is finished
1. Once the job is finished, GitLab-Rails schedules a sidekiq worker to archive the trace
1. The sidekiq worker archives the trace to Object Storage, and cleanup the trace in Redis and Database
**How to check if it's on or off?** ```sh
# Omnibus GitLab
gitlab-rails console
# Installation from source
cd /home/git/gitlab
sudo -u git -H bin/rails console RAILS_ENV=production
```
**To check if live trace is enabled:**
```ruby ```ruby
Feature.enabled?('ci_enable_live_trace') Feature.enabled?('ci_enable_live_trace')
``` ```
**How to enable?** **To enable live trace:**
```ruby ```ruby
Feature.enable('ci_enable_live_trace') Feature.enable('ci_enable_live_trace')
``` ```
>**Note:** NOTE: **Note:**
The transition period will be handled gracefully. Upcoming traces will be generated with the new architecture, and on-going live traces will stay with the legacy architecture (i.e. on-going live traces won't be re-generated forcibly with the new architecture). The transition period will be handled gracefully. Upcoming traces will be
generated with the new architecture, and on-going live traces will stay with the
legacy architecture, which means that on-going live traces won't be forcibly
re-generated with the new architecture.
**How to disable?** **To disable live trace:**
```ruby ```ruby
Feature.disable('ci_enable_live_trace') Feature.disable('ci_enable_live_trace')
``` ```
>**Note:** NOTE: **Note:**
The transition period will be handled gracefully. Upcoming traces will be generated with the legacy architecture, and on-going live traces will stay with the new architecture (i.e. on-going live traces won't be re-generated forcibly with the legacy architecture). The transition period will be handled gracefully. Upcoming traces will be generated
with the legacy architecture, and on-going live traces will stay with the new
**Redis namespace:** architecture, which means that on-going live traces won't be forcibly re-generated
with the legacy architecture.
`Gitlab::Redis::SharedState`
**Potential impact:**
- This feature could incur data loss: ### Potential implications
- Case 1: When all data in Redis are accidentally flushed.
- On-going live traces could be recovered by re-sending traces (This is supported by all versions of GitLab Runner)
- Finished jobs which has not archived live traces will lose the last part (~128kB) of trace data.
- Case 2: When sidekiq workers failed to archive (e.g. There was a bug that prevents archiving process, Sidekiq inconsistancy, etc):
- Currently all trace data in Redis will be deleted after one week. If the sidekiq workers can't finish by the expiry date, the part of trace data will be lost.
- This feature could consume all memory on Redis instance. If the number of jobs is 1000, 128MB (128kB * 1000) is consumed.
- This feature could pressure Database replication lag. `INSERT` are generated to indicate that we have trace chunk. `UPDATE` with 128kB of data is issued once we receive multiple chunks.
- and so on
**How to test?** In some cases, having data stored on Redis could incur data loss:
We're currently evaluating this feature on dev.gitalb.org or staging.gitlab.com to verify this features. Here is the list of tests/measurements. 1. **Case 1: When all data in Redis are accidentally flushed**
- On going live traces could be recovered by re-sending traces (this is
supported by all versions of the GitLab Runner).
- Finished jobs which have not archived live traces will lose the last part
(~128KB) of trace data.
- Features: 1. **Case 2: When Sidekiq workers fail to archive (e.g., there was a bug that
- Live traces should be visible on job pages prevents archiving process, Sidekiq inconsistency, etc.)**
- Archived traces should be visible on job pages - Currently all trace data in Redis will be deleted after one week. If the
- Live traces should be archived to Object storage Sidekiq workers can't finish by the expiry date, the part of trace data will be lost.
- Live traces should be cleaned up after archived
- etc
- Performance:
- Schedule 1000~10000 jobs and let GitLab-runners process concurrently. Measure memoery presssure, IO load, etc.
- etc
- Failover:
- Simulate Redis outage
- etc
**How to verify the correctnesss?** Another issue that might arise is that it could consume all memory on the Redis
instance. If the number of jobs is 1000, 128MB (128KB * 1000) is consumed.
- TBD Also, it could pressure the database replication lag. `INSERT`s are generated to
indicate that we have trace chunk. `UPDATE`s with 128KB of data is issued once we
receive multiple chunks.
[ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169 [ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169
[ce-46097]: https://gitlab.com/gitlab-org/gitlab-ce/issues/46097
...@@ -46,4 +46,4 @@ configuration to reflect that change. ...@@ -46,4 +46,4 @@ configuration to reflect that change.
[cli]: https://github.com/codeclimate/codeclimate [cli]: https://github.com/codeclimate/codeclimate
[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor [dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
...@@ -63,4 +63,4 @@ are still maintained they have been deprecated with GitLab 11.0 and may be remov ...@@ -63,4 +63,4 @@ are still maintained they have been deprecated with GitLab 11.0 and may be remov
in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml` in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change. configuration to reflect that change.
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
...@@ -62,4 +62,4 @@ so, the CI job must be named `dast` and the artifact path must be ...@@ -62,4 +62,4 @@ so, the CI job must be named `dast` and the artifact path must be
`gl-dast-report.json`. `gl-dast-report.json`.
[Learn more about DAST results shown in merge requests](../../user/project/merge_requests/dast.md). [Learn more about DAST results shown in merge requests](../../user/project/merge_requests/dast.md).
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
...@@ -276,7 +276,7 @@ removed with one of the future versions of GitLab. You are advised to ...@@ -276,7 +276,7 @@ removed with one of the future versions of GitLab. You are advised to
[ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017 [ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017
[ee-2346]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2346 [ee-2346]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2346
[ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229 [ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
[variables]: ../variables/README.md [variables]: ../variables/README.md
[predef]: ../variables/README.md#predefined-variables-environment-variables [predef]: ../variables/README.md#predefined-variables-environment-variables
[registry]: ../../user/project/container_registry.md [registry]: ../../user/project/container_registry.md
......
...@@ -575,6 +575,7 @@ Below you can find supported syntax reference: ...@@ -575,6 +575,7 @@ Below you can find supported syntax reference:
[ee-2112]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2112 [ee-2112]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2112
[premium]: https://about.gitlab.com/products/ "Available only in GitLab Premium" [premium]: https://about.gitlab.com/products/ "Available only in GitLab Premium"
[ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI variables"
[envs]: ../environments.md [envs]: ../environments.md
[protected branches]: ../../user/project/protected_branches.md [protected branches]: ../../user/project/protected_branches.md
[protected tags]: ../../user/project/protected_tags.md [protected tags]: ../../user/project/protected_tags.md
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## Software delivery ## Software delivery
There are two software distributions of GitLab: the open source [Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE), and the open core [Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE). GitLab is available under [different subscriptions](https://about.gitlab.com/products/). There are two software distributions of GitLab: the open source [Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE), and the open core [Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE). GitLab is available under [different subscriptions](https://about.gitlab.com/pricing/).
New versions of GitLab are released in stable branches and the master branch is for bleeding edge development. New versions of GitLab are released in stable branches and the master branch is for bleeding edge development.
......
...@@ -174,6 +174,8 @@ For example use `%{created_at}` in Ruby but `%{createdAt}` in JavaScript. ...@@ -174,6 +174,8 @@ For example use `%{created_at}` in Ruby but `%{createdAt}` in JavaScript.
# => When size == 2: 'There are 2 mice.' # => When size == 2: 'There are 2 mice.'
``` ```
Avoid using `%d` or count variables in sigular strings. This allows more natural translation in some languages.
- In JavaScript: - In JavaScript:
```js ```js
......
...@@ -192,7 +192,7 @@ Portions of this page are modifications based on work created and shared by the ...@@ -192,7 +192,7 @@ Portions of this page are modifications based on work created and shared by the
[material design]: https://material.io/guidelines/ [material design]: https://material.io/guidelines/
[features]: https://about.gitlab.com/features/ "GitLab features page" [features]: https://about.gitlab.com/features/ "GitLab features page"
[products]: https://about.gitlab.com/products/ "GitLab products page" [products]: https://about.gitlab.com/pricing/ "GitLab products page"
[serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma” in Wikipedia" [serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma” in Wikipedia"
[android project]: http://source.android.com/ [android project]: http://source.android.com/
[creative commons]: http://creativecommons.org/licenses/by/2.5/ [creative commons]: http://creativecommons.org/licenses/by/2.5/
...@@ -12,7 +12,7 @@ Since installations from source don't have Runit, Sidekiq can't be terminated an ...@@ -12,7 +12,7 @@ Since installations from source don't have Runit, Sidekiq can't be terminated an
## Select Version to Install ## Select Version to Install
Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-0-stable`). Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-1-stable`).
You can select the branch in the version dropdown in the top left corner of GitLab (below the menu bar). You can select the branch in the version dropdown in the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the [GitLab Blog](https://about.gitlab.com/blog/) for installation guide links by version. If the highest number stable branch is unclear please check the [GitLab Blog](https://about.gitlab.com/blog/) for installation guide links by version.
...@@ -300,9 +300,9 @@ sudo usermod -aG redis git ...@@ -300,9 +300,9 @@ sudo usermod -aG redis git
### Clone the Source ### Clone the Source
# Clone GitLab repository # Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-0-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-1-stable gitlab
**Note:** You can change `11-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! **Note:** You can change `11-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It ### Configure It
......
...@@ -71,7 +71,7 @@ For most installations, only two parameters are required: ...@@ -71,7 +71,7 @@ For most installations, only two parameters are required:
Other common configuration options: Other common configuration options:
- `baseIP`: the desired [external IP address](#external-ip-recommended) - `baseIP`: the desired [external IP address](#external-ip-recommended)
- `gitlab`: Choose the [desired edition](https://about.gitlab.com/products), either `ee` or `ce`. `ce` is the default. - `gitlab`: Choose the [desired edition](https://about.gitlab.com/pricing), either `ee` or `ce`. `ce` is the default.
- `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart - `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart
- `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/), with `acs` also supported for the [Azure Container Service](https://azure.microsoft.com/en-us/services/container-service/). - `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/), with `acs` also supported for the [Azure Container Service](https://azure.microsoft.com/en-us/services/container-service/).
......
...@@ -195,6 +195,12 @@ This example can be used for a bucket in Amsterdam (AMS3). ...@@ -195,6 +195,12 @@ This example can be used for a bucket in Amsterdam (AMS3).
1. [Reconfigure GitLab] for the changes to take effect 1. [Reconfigure GitLab] for the changes to take effect
CAUTION: **Warning:**
If you see `400 Bad Request` by using Digital Ocean Spaces, the cause may be the
usage of backup encryption. Remove or comment the line that
contains `gitlab_rails['backup_encryption']` since Digital Ocean Spaces
doesn't support encryption.
#### Other S3 Providers #### Other S3 Providers
Not all S3 providers are fully-compatible with the Fog library. For example, Not all S3 providers are fully-compatible with the Fog library. For example,
......
...@@ -840,5 +840,5 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/ ...@@ -840,5 +840,5 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
[postgresql]: https://www.postgresql.org/ [postgresql]: https://www.postgresql.org/
[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml [Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml
[GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md [GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507 [ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507
---
comments: false
---
# From 11.0 to 11.1
Make sure you view this update guide from the branch (version) of GitLab you would
like to install (e.g., `11-1-stable`. You can select the branch in the version
dropdown at the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the
[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
guide links by version.
### 1. Stop server
```bash
sudo service gitlab stop
```
### 2. Backup
NOTE: If you installed GitLab from source, make sure `rsync` is installed.
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 3. Update Ruby
NOTE: GitLab 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
sure to upgrade your interpreter if necessary.
You can check which version you are running with `ruby -v`.
Download Ruby and compile it:
```bash
mkdir /tmp/ruby && cd /tmp/ruby
curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz
echo 'ec82b0d53bd0adad9b19e6b45e44d54e9ec3f10c ruby-2.4.4.tar.gz' | shasum -c - && tar xzf ruby-2.4.4.tar.gz
cd ruby-2.4.4
./configure --disable-install-rdoc
make
sudo make install
```
Install Bundler:
```bash
sudo gem install bundler --no-ri --no-rdoc
```
### 4. Update Node
GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
This requires a minimum version of node v6.0.0.
You can check which version you are running with `node -v`. If you are running
a version older than `v6.0.0` you will need to update to a newer version. You
can find instructions to install from community maintained packages or compile
from source at the nodejs.org website.
<https://nodejs.org/en/download/>
GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
dependencies.
```bash
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
### 5. Update Go
NOTE: GitLab 11.0 and higher only supports Go 1.9.x and newer, and dropped support for Go
1.5.x through 1.8.x. Be sure to upgrade your installation if necessary.
You can check which version you are running with `go version`.
Download and install Go:
```bash
# Remove former Go installation folder
sudo rm -rf /usr/local/go
curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
rm go1.10.3.linux-amd64.tar.gz
```
### 6. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch --all --prune
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
sudo -u git -H git checkout -- locale
```
For GitLab Community Edition:
```bash
cd /home/git/gitlab
sudo -u git -H git checkout 11-1-stable
```
OR
For GitLab Enterprise Edition:
```bash
cd /home/git/gitlab
sudo -u git -H git checkout 11-1-stable-ee
```
### 7. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags --prune
sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
sudo -u git -H bin/compile
```
### 8. Update gitlab-workhorse
Install and compile gitlab-workhorse. GitLab-Workhorse uses
[GNU Make](https://www.gnu.org/software/make/).
If you are not using Linux you may have to run `gmake` instead of
`make` below.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all --tags --prune
sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
sudo -u git -H make
```
### 9. Update Gitaly
#### New Gitaly configuration options required
In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
```shell
echo '
[gitaly-ruby]
dir = "/home/git/gitaly/ruby"
[gitlab-shell]
dir = "/home/git/gitlab-shell"
' | sudo -u git tee -a /home/git/gitaly/config.toml
```
#### Check Gitaly configuration
Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
configuration file may contain syntax errors. The block name
`[[storages]]`, which may occur more than once in your `config.toml`
file, should be `[[storage]]` instead.
```shell
sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
```
#### Compile Gitaly
```shell
cd /home/git/gitaly
sudo -u git -H git fetch --all --tags --prune
sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
sudo -u git -H make
```
### 10. Update MySQL permissions
If you are using MySQL you need to grant the GitLab user the necessary
permissions on the database:
```bash
mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
```
If you use MySQL with replication, or just have MySQL configured with binary logging,
you will need to also run the following on all of your MySQL servers:
```bash
mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
```
You can make this setting permanent by adding it to your `my.cnf`:
```
log_bin_trust_function_creators=1
```
### 11. Update configuration files
#### New configuration options for `gitlab.yml`
There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
```sh
cd /home/git/gitlab
git diff origin/11-0-stable:config/gitlab.yml.example origin/11-1-stable:config/gitlab.yml.example
```
#### Nginx configuration
Ensure you're still up-to-date with the latest NGINX configuration changes:
```sh
cd /home/git/gitlab
# For HTTPS configurations
git diff origin/11-0-stable:lib/support/nginx/gitlab-ssl origin/11-1-stable:lib/support/nginx/gitlab-ssl
# For HTTP configurations
git diff origin/11-0-stable:lib/support/nginx/gitlab origin/11-1-stable:lib/support/nginx/gitlab
```
If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
configuration as GitLab application no longer handles setting it.
If you are using Apache instead of NGINX please see the updated [Apache templates].
Also note that because Apache does not support upstreams behind Unix sockets you
will need to let gitlab-workhorse listen on a TCP port. You can do this
via [/etc/default/gitlab].
[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/lib/support/init.d/gitlab.default.example#L38
#### SMTP configuration
If you're installing from source and use SMTP to deliver mail, you will need to add the following line
to config/initializers/smtp_settings.rb:
```ruby
ActionMailer::Base.delivery_method = :smtp
```
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
```sh
cd /home/git/gitlab
git diff origin/11-0-stable:lib/support/init.d/gitlab.default.example origin/11-1-stable:lib/support/init.d/gitlab.default.example
```
Ensure you're still up-to-date with the latest init script changes:
```bash
cd /home/git/gitlab
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
```
For Ubuntu 16.04.1 LTS:
```bash
sudo systemctl daemon-reload
```
### 12. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without postgres')
sudo -u git -H bundle install --without postgres development test --deployment
# PostgreSQL installations (note: the line below states '--without mysql')
sudo -u git -H bundle install --without mysql development test --deployment
# Optional: clean up old gems
sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Compile GetText PO files
sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
# Update node dependencies and recompile assets
sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
# Clean up cache
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
### 13. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
### 14. Check application status
Check if GitLab and its environment are configured correctly:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
```
To make sure you didn't miss anything run a more thorough check:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
If all items are green, then congratulations, the upgrade is complete!
## Things went south? Revert to previous version (11.0)
### 1. Revert the code to the previous version
Follow the [upgrade guide from 10.8 to 11.0](10.8-to-11.0.md), except for the
database migration (the backup is already migrated to the previous version).
### 2. Restore from the backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/config/gitlab.yml.example
[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-1-stable/lib/support/init.d/gitlab.default.example
...@@ -7,7 +7,7 @@ description: 'Read through the GitLab User documentation to learn how to use, co ...@@ -7,7 +7,7 @@ description: 'Read through the GitLab User documentation to learn how to use, co
Welcome to GitLab! We're glad to have you here! Welcome to GitLab! We're glad to have you here!
As a GitLab user you'll have access to all the features As a GitLab user you'll have access to all the features
your [subscription](https://about.gitlab.com/products/) your [subscription](https://about.gitlab.com/pricing/)
includes, except [GitLab administrator](../README.md#administrator-documentation) includes, except [GitLab administrator](../README.md#administrator-documentation)
settings, unless you have admin privileges to install, configure, settings, unless you have admin privileges to install, configure,
and upgrade your GitLab instance. and upgrade your GitLab instance.
......
...@@ -312,4 +312,4 @@ Read through the documentation on [LDAP users permissions](../administration/aut ...@@ -312,4 +312,4 @@ Read through the documentation on [LDAP users permissions](../administration/aut
[ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994 [ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994
[new-mod]: project/new_ci_build_permissions_model.md [new-mod]: project/new_ci_build_permissions_model.md
[ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998 [ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998
[eep]: https://about.gitlab.com/products/ [eep]: https://about.gitlab.com/pricing/
...@@ -415,5 +415,5 @@ the deployment variables above, ensuring any pods you create are labelled with ...@@ -415,5 +415,5 @@ the deployment variables above, ensuring any pods you create are labelled with
Learn how to [connect and deploy to an Amazon EKS cluster](eks_and_gitlab/index.md). Learn how to [connect and deploy to an Amazon EKS cluster](eks_and_gitlab/index.md).
[permissions]: ../../permissions.md [permissions]: ../../permissions.md
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/pricing/
[Auto DevOps]: ../../../topics/autodevops/index.md [Auto DevOps]: ../../../topics/autodevops/index.md
...@@ -6,6 +6,10 @@ Starting from GitLab 8.5: ...@@ -6,6 +6,10 @@ Starting from GitLab 8.5:
- the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key - the `project.ssh_url` key is deprecated in favor of the `project.git_ssh_url` key
- the `project.http_url` key is deprecated in favor of the `project.git_http_url` key - the `project.http_url` key is deprecated in favor of the `project.git_http_url` key
>**Note:**
Starting from GitLab 11.1, the logs of web hooks are automatically removed after
one month.
Project webhooks allow you to trigger a URL if for example new code is pushed or Project webhooks allow you to trigger a URL if for example new code is pushed or
a new issue is created. You can configure webhooks to listen for specific events a new issue is created. You can configure webhooks to listen for specific events
like pushes, issues or merge requests. GitLab will send a POST request with data like pushes, issues or merge requests. GitLab will send a POST request with data
......
...@@ -70,7 +70,7 @@ beginning of the development lifecycle until deployed to production ...@@ -70,7 +70,7 @@ beginning of the development lifecycle until deployed to production
### Use cases for Multiple Issue Boards ### Use cases for Multiple Issue Boards
With [Multiple Issue Boards](#multiple-issue-boards), available only in With [Multiple Issue Boards](#multiple-issue-boards), available only in
[GitLab Enterprise Edition](https://about.gitlab.com/products/), [GitLab Enterprise Edition](https://about.gitlab.com/pricing/),
each team can have their own board to organize their workflow individually. each team can have their own board to organize their workflow individually.
#### Scrum team #### Scrum team
......
...@@ -8,7 +8,7 @@ It allows you, your team, and your collaborators to share ...@@ -8,7 +8,7 @@ It allows you, your team, and your collaborators to share
and discuss proposals before and while implementing them. and discuss proposals before and while implementing them.
GitLab Issues and the GitLab Issue Tracker are available in all GitLab Issues and the GitLab Issue Tracker are available in all
[GitLab Products](https://about.gitlab.com/products/) as [GitLab Products](https://about.gitlab.com/pricing/) as
part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/). part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
## Use cases ## Use cases
...@@ -35,7 +35,7 @@ your project public, open to collaboration. ...@@ -35,7 +35,7 @@ your project public, open to collaboration.
### Streamline collaboration ### Streamline collaboration
With [Multiple Assignees for Issues](multiple_assignees_for_issues.md), With [Multiple Assignees for Issues](multiple_assignees_for_issues.md),
available in [GitLab Starter](https://about.gitlab.com/products/) available in [GitLab Starter](https://about.gitlab.com/pricing/)
you can streamline collaboration and allow shared responsibilities to be clearly displayed. you can streamline collaboration and allow shared responsibilities to be clearly displayed.
All assignees are shown across your workflows and receive notifications (as they All assignees are shown across your workflows and receive notifications (as they
would as single assignees), simplifying communication and ownership. would as single assignees), simplifying communication and ownership.
...@@ -145,7 +145,7 @@ Issues can be [exported as CSV](csv_export.md) from GitLab and are sent to your ...@@ -145,7 +145,7 @@ Issues can be [exported as CSV](csv_export.md) from GitLab and are sent to your
_Exporting issues to CSV is available only in [GitLab Enterprise Edition](https://about.gitlab.com/products/)._ _Exporting issues to CSV is available only in [GitLab Enterprise Edition](https://about.gitlab.com/products/)._
## Related Issues **[STARTER]** ### Related Issues **[STARTER]**
Related Issues are a bi-directional relationship between any two issues Related Issues are a bi-directional relationship between any two issues
and appear in a block below the issue description. Issues can be across groups and appear in a block below the issue description. Issues can be across groups
......
...@@ -47,7 +47,7 @@ Often multiple people likely work on the same issue together, ...@@ -47,7 +47,7 @@ Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams which can especially be difficult to track in large teams
where there is shared ownership of an issue. where there is shared ownership of an issue.
In [GitLab Starter](https://about.gitlab.com/products/), you can also In [GitLab Starter](https://about.gitlab.com/pricing/), you can also
select multiple assignees to an issue. select multiple assignees to an issue.
Learn more on the [Multiple Assignees documentation](multiple_assignees_for_issues.md). Learn more on the [Multiple Assignees documentation](multiple_assignees_for_issues.md).
......
...@@ -432,6 +432,6 @@ git checkout origin/merge-requests/1 ...@@ -432,6 +432,6 @@ git checkout origin/merge-requests/1
[ci]: ../../../ci/README.md [ci]: ../../../ci/README.md
[cc]: https://codeclimate.com/ [cc]: https://codeclimate.com/
[cd]: https://hub.docker.com/r/codeclimate/codeclimate/ [cd]: https://hub.docker.com/r/codeclimate/codeclimate/
[ee]: https://about.gitlab.com/products/ "GitLab Enterprise Edition"
[sitespeed]: https://www.sitespeed.io [sitespeed]: https://www.sitespeed.io
[sitespeed-container]: https://hub.docker.com/r/sitespeedio/sitespeed.io/ [sitespeed-container]: https://hub.docker.com/r/sitespeedio/sitespeed.io/
[ee]: https://about.gitlab.com/pricing/ "GitLab Enterprise Edition"
...@@ -82,7 +82,7 @@ your implementation with your team. ...@@ -82,7 +82,7 @@ your implementation with your team.
You can live preview changes submitted to a new branch with You can live preview changes submitted to a new branch with
[Review Apps](../../../ci/review_apps/index.md). [Review Apps](../../../ci/review_apps/index.md).
With [GitLab Starter](https://about.gitlab.com/products/), you can also request With [GitLab Starter](https://about.gitlab.com/pricing/), you can also request
[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers. [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers.
To create, delete, and [branches](branches/index.md) via GitLab's UI: To create, delete, and [branches](branches/index.md) via GitLab's UI:
...@@ -165,8 +165,7 @@ Find it under your project's **Repository > Compare**. ...@@ -165,8 +165,7 @@ Find it under your project's **Repository > Compare**.
## Locked files **[PREMIUM]** ## Locked files **[PREMIUM]**
[Lock your files](https://docs.gitlab.com/ee/user/project/file_lock.html) to [Lock your files](../file_lock.md) to prevent any conflicting changes.
prevent any conflicting changes.
## Repository's API ## Repository's API
......
...@@ -42,7 +42,7 @@ Set up your project's merge request settings: ...@@ -42,7 +42,7 @@ Set up your project's merge request settings:
### Service Desk **[PREMIUM]** ### Service Desk **[PREMIUM]**
Enable [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) for your project to offer customer support. Enable [Service Desk](../service_desk.md) for your project to offer customer support.
### Export project ### Export project
......
...@@ -236,5 +236,5 @@ See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-ce/merge_r ...@@ -236,5 +236,5 @@ See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-ce/merge_r
[reconfigure gitlab]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab" [reconfigure gitlab]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: ../../administration/restart_gitlab.md#installations-from-source "How to restart GitLab" [restart gitlab]: ../../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
[eep]: https://about.gitlab.com/products/ "GitLab Premium" [eep]: https://about.gitlab.com/pricing/ "GitLab Premium"
[ee-2760]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2760 [ee-2760]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2760
...@@ -3,10 +3,6 @@ module Banzai ...@@ -3,10 +3,6 @@ module Banzai
# HTML filter that replaces :emoji: and unicode with images. # HTML filter that replaces :emoji: and unicode with images.
# #
# Based on HTML::Pipeline::EmojiFilter # Based on HTML::Pipeline::EmojiFilter
#
# Context options:
# :asset_root
# :asset_host
class EmojiFilter < HTML::Pipeline::Filter class EmojiFilter < HTML::Pipeline::Filter
IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set
......
...@@ -250,7 +250,7 @@ module Gitlab ...@@ -250,7 +250,7 @@ module Gitlab
last_line = lines.last last_line = lines.last
if last_line.new_pos < total_blob_lines(blob) if last_line.new_pos < total_blob_lines(blob) && !deleted_file?
match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos) match_line = Gitlab::Diff::Line.new("", 'match', nil, last_line.old_pos, last_line.new_pos)
lines.push(match_line) lines.push(match_line)
end end
......
...@@ -648,18 +648,14 @@ module Gitlab ...@@ -648,18 +648,14 @@ module Gitlab
end end
def add_branch(branch_name, user:, target:) def add_branch(branch_name, user:, target:)
wrapped_gitaly_errors do
gitaly_operation_client.user_create_branch(branch_name, user, target) gitaly_operation_client.user_create_branch(branch_name, user, target)
rescue GRPC::FailedPrecondition => ex end
raise InvalidRef, ex
end end
def add_tag(tag_name, user:, target:, message: nil) def add_tag(tag_name, user:, target:, message: nil)
gitaly_migrate(:operation_user_add_tag, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_operation_client.add_tag(tag_name, user, target, message)
gitaly_add_tag(tag_name, user: user, target: target, message: message)
else
rugged_add_tag(tag_name, user: user, target: target, message: message)
end
end end
end end
...@@ -668,22 +664,14 @@ module Gitlab ...@@ -668,22 +664,14 @@ module Gitlab
end end
def rm_branch(branch_name, user:) def rm_branch(branch_name, user:)
gitaly_migrate(:operation_user_delete_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_operation_client.user_delete_branch(branch_name, user)
gitaly_operations_client.user_delete_branch(branch_name, user)
else
OperationService.new(user, self).rm_branch(find_branch(branch_name))
end
end end
end end
def rm_tag(tag_name, user:) def rm_tag(tag_name, user:)
gitaly_migrate(:operation_user_delete_tag) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_operation_client.rm_tag(tag_name, user)
gitaly_operations_client.rm_tag(tag_name, user)
else
Gitlab::Git::OperationService.new(user, self).rm_tag(find_tag(tag_name))
end
end end
end end
...@@ -692,58 +680,18 @@ module Gitlab ...@@ -692,58 +680,18 @@ module Gitlab
end end
def merge(user, source_sha, target_branch, message, &block) def merge(user, source_sha, target_branch, message, &block)
gitaly_migrate(:operation_user_merge_branch) do |is_enabled| wrapped_gitaly_errors do
if is_enabled
gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block) gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block)
else
rugged_merge(user, source_sha, target_branch, message, &block)
end end
end end
end
def rugged_merge(user, source_sha, target_branch, message)
committer = Gitlab::Git.committer_hash(email: user.email, name: user.name)
OperationService.new(user, self).with_branch(target_branch) do |start_commit|
our_commit = start_commit.sha
their_commit = source_sha
raise 'Invalid merge target' unless our_commit
raise 'Invalid merge source' unless their_commit
merge_index = rugged.merge_commits(our_commit, their_commit)
break if merge_index.conflicts?
options = {
parents: [our_commit, their_commit],
tree: merge_index.write_tree(rugged),
message: message,
author: committer,
committer: committer
}
commit_id = create_commit(options)
yield commit_id
commit_id
end
rescue Gitlab::Git::CommitError # when merge_index.conflicts?
nil
end
def ff_merge(user, source_sha, target_branch) def ff_merge(user, source_sha, target_branch)
gitaly_migrate(:operation_user_ff_branch) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_operation_client.user_ff_branch(user, source_sha, target_branch)
gitaly_ff_merge(user, source_sha, target_branch)
else
rugged_ff_merge(user, source_sha, target_branch)
end
end end
end end
def revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:) def revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
gitaly_migrate(:revert, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
args = { args = {
user: user, user: user,
commit: commit, commit: commit,
...@@ -753,11 +701,8 @@ module Gitlab ...@@ -753,11 +701,8 @@ module Gitlab
start_repository: start_repository start_repository: start_repository
} }
if is_enabled wrapped_gitaly_errors do
gitaly_operations_client.user_revert(args) gitaly_operation_client.user_revert(args)
else
rugged_revert(args)
end
end end
end end
...@@ -775,7 +720,6 @@ module Gitlab ...@@ -775,7 +720,6 @@ module Gitlab
end end
def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:) def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
gitaly_migrate(:cherry_pick) do |is_enabled|
args = { args = {
user: user, user: user,
commit: commit, commit: commit,
...@@ -785,11 +729,8 @@ module Gitlab ...@@ -785,11 +729,8 @@ module Gitlab
start_repository: start_repository start_repository: start_repository
} }
if is_enabled wrapped_gitaly_errors do
gitaly_operations_client.user_cherry_pick(args) gitaly_operation_client.user_cherry_pick(args)
else
rugged_cherry_pick(args)
end
end end
end end
...@@ -1113,22 +1054,14 @@ module Gitlab ...@@ -1113,22 +1054,14 @@ module Gitlab
end end
def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:) def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
gitaly_migrate(:rebase) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_operation_client.user_rebase(user, rebase_id,
gitaly_rebase(user, rebase_id,
branch: branch,
branch_sha: branch_sha,
remote_repository: remote_repository,
remote_branch: remote_branch)
else
git_rebase(user, rebase_id,
branch: branch, branch: branch,
branch_sha: branch_sha, branch_sha: branch_sha,
remote_repository: remote_repository, remote_repository: remote_repository,
remote_branch: remote_branch) remote_branch: remote_branch)
end end
end end
end
def rebase_in_progress?(rebase_id) def rebase_in_progress?(rebase_id)
wrapped_gitaly_errors do wrapped_gitaly_errors do
...@@ -1137,13 +1070,9 @@ module Gitlab ...@@ -1137,13 +1070,9 @@ module Gitlab
end end
def squash(user, squash_id, branch:, start_sha:, end_sha:, author:, message:) def squash(user, squash_id, branch:, start_sha:, end_sha:, author:, message:)
gitaly_migrate(:squash) do |is_enabled| wrapped_gitaly_errors do
if is_enabled
gitaly_operation_client.user_squash(user, squash_id, branch, gitaly_operation_client.user_squash(user, squash_id, branch,
start_sha, end_sha, author, message) start_sha, end_sha, author, message)
else
git_squash(user, squash_id, branch, start_sha, end_sha, author, message)
end
end end
end end
...@@ -1189,15 +1118,10 @@ module Gitlab ...@@ -1189,15 +1118,10 @@ module Gitlab
author_email: nil, author_name: nil, author_email: nil, author_name: nil,
start_branch_name: nil, start_repository: self) start_branch_name: nil, start_repository: self)
gitaly_migrate(:operation_user_commit_files) do |is_enabled| wrapped_gitaly_errors do
if is_enabled
gitaly_operation_client.user_commit_files(user, branch_name, gitaly_operation_client.user_commit_files(user, branch_name,
message, actions, author_email, author_name, message, actions, author_email, author_name,
start_branch_name, start_repository) start_branch_name, start_repository)
else
rugged_multi_action(user, branch_name, message, actions,
author_email, author_name, start_branch_name, start_repository)
end
end end
end end
# rubocop:enable Metrics/ParameterLists # rubocop:enable Metrics/ParameterLists
...@@ -1217,10 +1141,6 @@ module Gitlab ...@@ -1217,10 +1141,6 @@ module Gitlab
Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository)
end end
def gitaly_operations_client
@gitaly_operations_client ||= Gitlab::GitalyClient::OperationService.new(self)
end
def gitaly_ref_client def gitaly_ref_client
@gitaly_ref_client ||= Gitlab::GitalyClient::RefService.new(self) @gitaly_ref_client ||= Gitlab::GitalyClient::RefService.new(self)
end end
...@@ -1760,33 +1680,6 @@ module Gitlab ...@@ -1760,33 +1680,6 @@ module Gitlab
false false
end end
def gitaly_add_tag(tag_name, user:, target:, message: nil)
gitaly_operations_client.add_tag(tag_name, user, target, message)
end
def rugged_add_tag(tag_name, user:, target:, message: nil)
target_object = Ref.dereference_object(lookup(target))
raise InvalidRef.new("target not found: #{target}") unless target_object
user = Gitlab::Git::User.from_gitlab(user) unless user.respond_to?(:gl_id)
options = nil # Use nil, not the empty hash. Rugged cares about this.
if message
options = {
message: message,
tagger: Gitlab::Git.committer_hash(email: user.email, name: user.name)
}
end
Gitlab::Git::OperationService.new(user, self).add_tag(tag_name, target_object.oid, options)
find_tag(tag_name)
rescue Rugged::ReferenceError => ex
raise InvalidRef, ex
rescue Rugged::TagError
raise TagExistsError
end
def rugged_create_branch(ref, start_point) def rugged_create_branch(ref, start_point)
rugged_ref = rugged.branches.create(ref, start_point) rugged_ref = rugged.branches.create(ref, start_point)
target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target) target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
...@@ -1831,28 +1724,6 @@ module Gitlab ...@@ -1831,28 +1724,6 @@ module Gitlab
end end
end end
def rugged_revert(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
OperationService.new(user, self).with_branch(
branch_name,
start_branch_name: start_branch_name,
start_repository: start_repository
) do |start_commit|
Gitlab::Git.check_namespace!(commit, start_repository)
revert_tree_id = check_revert_content(commit, start_commit.sha)
raise CreateTreeError unless revert_tree_id
committer = user_to_committer(user)
create_commit(message: message,
author: committer,
committer: committer,
tree: revert_tree_id,
parents: [start_commit.sha])
end
end
def rugged_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:) def rugged_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
OperationService.new(user, self).with_branch( OperationService.new(user, self).with_branch(
branch_name, branch_name,
...@@ -1892,71 +1763,6 @@ module Gitlab ...@@ -1892,71 +1763,6 @@ module Gitlab
tree_id tree_id
end end
def gitaly_rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
gitaly_operation_client.user_rebase(user, rebase_id,
branch: branch,
branch_sha: branch_sha,
remote_repository: remote_repository,
remote_branch: remote_branch)
end
def git_rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
rebase_path = worktree_path(REBASE_WORKTREE_PREFIX, rebase_id)
env = git_env_for_user(user)
if remote_repository.is_a?(RemoteRepository)
env.merge!(remote_repository.fetch_env)
remote_repo_path = GITALY_INTERNAL_URL
else
remote_repo_path = remote_repository.path
end
with_worktree(rebase_path, branch, env: env) do
run_git!(
%W(pull --rebase #{remote_repo_path} #{remote_branch}),
chdir: rebase_path, env: env
)
rebase_sha = run_git!(%w(rev-parse HEAD), chdir: rebase_path, env: env).strip
update_branch(branch, user: user, newrev: rebase_sha, oldrev: branch_sha)
rebase_sha
end
end
def git_squash(user, squash_id, branch, start_sha, end_sha, author, message)
squash_path = worktree_path(SQUASH_WORKTREE_PREFIX, squash_id)
env = git_env_for_user(user).merge(
'GIT_AUTHOR_NAME' => author.name,
'GIT_AUTHOR_EMAIL' => author.email
)
diff_range = "#{start_sha}...#{end_sha}"
diff_files = run_git!(
%W(diff --name-only --diff-filter=ar --binary #{diff_range})
).chomp
with_worktree(squash_path, branch, sparse_checkout_files: diff_files, env: env) do
# Apply diff of the `diff_range` to the worktree
diff = run_git!(%W(diff --binary #{diff_range}))
run_git!(%w(apply --index --whitespace=nowarn), chdir: squash_path, env: env) do |stdin|
stdin.binmode
stdin.write(diff)
end
# Commit the `diff_range` diff
run_git!(%W(commit --no-verify --message #{message}), chdir: squash_path, env: env)
# Return the squash sha. May print a warning for ambiguous refs, but
# we can ignore that with `--quiet` and just take the SHA, if present.
# HEAD here always refers to the current HEAD commit, even if there is
# another ref called HEAD.
run_git!(
%w(rev-parse --quiet --verify HEAD), chdir: squash_path, env: env
).chomp
end
end
def local_fetch_ref(source_path, source_ref:, target_ref:) def local_fetch_ref(source_path, source_ref:, target_ref:)
args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
run_git(args) run_git(args)
...@@ -1968,22 +1774,6 @@ module Gitlab ...@@ -1968,22 +1774,6 @@ module Gitlab
run_git(args, env: source_repository.fetch_env) run_git(args, env: source_repository.fetch_env)
end end
def gitaly_ff_merge(user, source_sha, target_branch)
gitaly_operations_client.user_ff_branch(user, source_sha, target_branch)
rescue GRPC::FailedPrecondition => e
raise CommitError, e
end
def rugged_ff_merge(user, source_sha, target_branch)
OperationService.new(user, self).with_branch(target_branch) do |our_commit|
raise ArgumentError, 'Invalid merge target' unless our_commit
source_sha
end
rescue Rugged::ReferenceError, InvalidRef
raise ArgumentError, 'Invalid merge source'
end
def rugged_add_remote(remote_name, url, mirror_refmap) def rugged_add_remote(remote_name, url, mirror_refmap)
rugged.remotes.create(remote_name, url) rugged.remotes.create(remote_name, url)
...@@ -2035,39 +1825,6 @@ module Gitlab ...@@ -2035,39 +1825,6 @@ module Gitlab
remove_remote(remote_name) remove_remote(remote_name)
end end
def rugged_multi_action(
user, branch_name, message, actions, author_email, author_name,
start_branch_name, start_repository)
OperationService.new(user, self).with_branch(
branch_name,
start_branch_name: start_branch_name,
start_repository: start_repository
) do |start_commit|
index = Gitlab::Git::Index.new(self)
parents = []
if start_commit
index.read_tree(start_commit.rugged_commit.tree)
parents = [start_commit.sha]
end
actions.each { |opts| index.apply(opts.delete(:action), opts) }
committer = user_to_committer(user)
author = Gitlab::Git.committer_hash(email: author_email, name: author_name) || committer
options = {
tree: index.write_tree,
message: message,
parents: parents,
author: author,
committer: committer
}
create_commit(options)
end
end
def fetch_remote(remote_name = 'origin', env: nil) def fetch_remote(remote_name = 'origin', env: nil)
run_git(['fetch', remote_name], env: env).last.zero? run_git(['fetch', remote_name], env: env).last.zero?
end end
......
...@@ -368,7 +368,7 @@ module Gitlab ...@@ -368,7 +368,7 @@ module Gitlab
def call_commit_diff(request_params, options = {}) def call_commit_diff(request_params, options = {})
request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false) request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
request_params[:enforce_limits] = options.fetch(:limits, true) request_params[:enforce_limits] = options.fetch(:limits, true)
request_params[:collapse_diffs] = request_params[:enforce_limits] || !options.fetch(:expanded, true) request_params[:collapse_diffs] = !options.fetch(:expanded, true)
request_params.merge!(Gitlab::Git::DiffCollection.collection_limits(options).to_h) request_params.merge!(Gitlab::Git::DiffCollection.collection_limits(options).to_h)
request = Gitaly::CommitDiffRequest.new(request_params) request = Gitaly::CommitDiffRequest.new(request_params)
......
...@@ -64,6 +64,8 @@ module Gitlab ...@@ -64,6 +64,8 @@ module Gitlab
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit) target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit) Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit)
rescue GRPC::FailedPrecondition => ex
raise Gitlab::Git::Repository::InvalidRef, ex
end end
def user_delete_branch(branch_name, user) def user_delete_branch(branch_name, user)
...@@ -133,6 +135,8 @@ module Gitlab ...@@ -133,6 +135,8 @@ module Gitlab
request request
).branch_update ).branch_update
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update) Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
rescue GRPC::FailedPrecondition => e
raise Gitlab::Git::CommitError, e
end end
def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:) def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
......
...@@ -124,7 +124,7 @@ describe Projects::MilestonesController do ...@@ -124,7 +124,7 @@ describe Projects::MilestonesController do
it 'shows group milestone' do it 'shows group milestone' do
post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
expect(flash[:notice]).to eq("#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\">group milestone</a>.") expect(flash[:notice]).to eq("#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.")
expect(response).to redirect_to(project_milestones_path(project)) expect(response).to redirect_to(project_milestones_path(project))
end end
end end
......
...@@ -107,19 +107,6 @@ feature 'Group milestones' do ...@@ -107,19 +107,6 @@ feature 'Group milestones' do
expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1) expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1)
end end
it 'updates milestone' do
page.within(".milestones #milestone_#{active_group_milestone.id}") do
click_link('Edit')
end
page.within('.milestone-form') do
fill_in 'milestone_title', with: 'new title'
click_button('Update milestone')
end
expect(find('#content-body h2')).to have_content('new title')
end
it 'shows milestone detail and supports its edit' do it 'shows milestone detail and supports its edit' do
page.within(".milestones #milestone_#{active_group_milestone.id}") do page.within(".milestones #milestone_#{active_group_milestone.id}") do
click_link(active_group_milestone.title) click_link(active_group_milestone.title)
......
...@@ -13,6 +13,7 @@ describe "User deletes milestone", :js do ...@@ -13,6 +13,7 @@ describe "User deletes milestone", :js do
end end
it "deletes milestone" do it "deletes milestone" do
click_link(milestone.title)
click_button("Delete") click_button("Delete")
click_button("Delete milestone") click_button("Delete milestone")
......
...@@ -17,6 +17,9 @@ describe "User creates issue" do ...@@ -17,6 +17,9 @@ describe "User creates issue" do
expect(page).to have_no_content("Assign to") expect(page).to have_no_content("Assign to")
.and have_no_content("Labels") .and have_no_content("Labels")
.and have_no_content("Milestone") .and have_no_content("Milestone")
expect(page.find('#issue_title')['placeholder']).to eq 'Title'
expect(page.find('#issue_description')['placeholder']).to eq 'Write a comment or drag your files here…'
end end
issue_title = "500 error on profile" issue_title = "500 error on profile"
......
...@@ -9,9 +9,9 @@ feature 'Creating a new project milestone', :js do ...@@ -9,9 +9,9 @@ feature 'Creating a new project milestone', :js do
visit new_project_milestone_path(project) visit new_project_milestone_path(project)
end end
it 'description has autocomplete' do it 'description has emoji autocomplete' do
find('#milestone_description').native.send_keys('') find('#milestone_description').native.send_keys('')
fill_in 'milestone_description', with: '@' fill_in 'milestone_description', with: ':'
expect(page).to have_selector('.atwho-view') expect(page).to have_selector('.atwho-view')
end end
......
...@@ -242,7 +242,7 @@ describe "User creates wiki page" do ...@@ -242,7 +242,7 @@ describe "User creates wiki page" do
end end
end end
it "shows the autocompletion dropdown" do it "shows the emoji autocompletion dropdown" do
click_link("New page") click_link("New page")
page.within("#modal-new-wiki") do page.within("#modal-new-wiki") do
...@@ -254,7 +254,7 @@ describe "User creates wiki page" do ...@@ -254,7 +254,7 @@ describe "User creates wiki page" do
page.within(".wiki-form") do page.within(".wiki-form") do
find("#wiki_content").native.send_keys("") find("#wiki_content").native.send_keys("")
fill_in(:wiki_content, with: "@") fill_in(:wiki_content, with: ":")
end end
expect(page).to have_selector(".atwho-view") expect(page).to have_selector(".atwho-view")
......
...@@ -96,11 +96,11 @@ describe 'User updates wiki page' do ...@@ -96,11 +96,11 @@ describe 'User updates wiki page' do
expect(find('textarea#wiki_content').value).to eq('') expect(find('textarea#wiki_content').value).to eq('')
end end
it 'shows the autocompletion dropdown', :js do it 'shows the emoji autocompletion dropdown', :js do
click_link('Edit') click_link('Edit')
find('#wiki_content').native.send_keys('') find('#wiki_content').native.send_keys('')
fill_in(:wiki_content, with: '@') fill_in(:wiki_content, with: ':')
expect(page).to have_selector('.atwho-view') expect(page).to have_selector('.atwho-view')
end end
......
...@@ -75,9 +75,9 @@ feature 'Master creates tag' do ...@@ -75,9 +75,9 @@ feature 'Master creates tag' do
visit new_project_tag_path(project) visit new_project_tag_path(project)
end end
it 'description has autocomplete', :js do it 'description has emoji autocomplete', :js do
find('#release_description').native.send_keys('') find('#release_description').native.send_keys('')
fill_in 'release_description', with: '@' fill_in 'release_description', with: ':'
expect(page).to have_selector('.atwho-view') expect(page).to have_selector('.atwho-view')
end end
......
...@@ -35,7 +35,6 @@ feature 'Master deletes tag' do ...@@ -35,7 +35,6 @@ feature 'Master deletes tag' do
end end
context 'when pre-receive hook fails', :js do context 'when pre-receive hook fails', :js do
context 'when Gitaly operation_user_delete_tag feature is enabled' do
before do before do
allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag) allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag)
.and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags') .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
...@@ -48,20 +47,6 @@ feature 'Master deletes tag' do ...@@ -48,20 +47,6 @@ feature 'Master deletes tag' do
end end
end end
context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do
before do
allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute)
.and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
end
scenario 'shows the error message' do
delete_first_tag
expect(page).to have_content('Do not delete tags')
end
end
end
def delete_first_tag def delete_first_tag
page.within('.content') do page.within('.content') do
accept_confirm { first('.btn-remove').click } accept_confirm { first('.btn-remove').click }
......
...@@ -25,13 +25,13 @@ feature 'Master updates tag' do ...@@ -25,13 +25,13 @@ feature 'Master updates tag' do
expect(page).to have_content 'Awesome release notes' expect(page).to have_content 'Awesome release notes'
end end
scenario 'description has autocomplete', :js do scenario 'description has emoji autocomplete', :js do
page.within(first('.content-list .controls')) do page.within(first('.content-list .controls')) do
click_link 'Edit release notes' click_link 'Edit release notes'
end end
find('#release_description').native.send_keys('') find('#release_description').native.send_keys('')
fill_in 'release_description', with: '@' fill_in 'release_description', with: ':'
expect(page).to have_selector('.atwho-view') expect(page).to have_selector('.atwho-view')
end end
......
...@@ -26,6 +26,21 @@ describe Gitlab::Diff::File do ...@@ -26,6 +26,21 @@ describe Gitlab::Diff::File do
end end
end end
describe '#diff_lines_for_serializer' do
it 'includes bottom match line if not in the end' do
expect(diff_file.diff_lines_for_serializer.last.type).to eq('match')
end
context 'when deleted' do
let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') }
let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') }
it 'does not include bottom match line' do
expect(diff_file.diff_lines_for_serializer.last.type).not_to eq('match')
end
end
end
describe '#mode_changed?' do describe '#mode_changed?' do
it { expect(diff_file.mode_changed?).to be_falsey } it { expect(diff_file.mode_changed?).to be_falsey }
end end
......
...@@ -1971,7 +1971,6 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -1971,7 +1971,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
context 'with gitaly' do
it "calls Gitaly's OperationService" do it "calls Gitaly's OperationService" do
expect_any_instance_of(Gitlab::GitalyClient::OperationService) expect_any_instance_of(Gitlab::GitalyClient::OperationService)
.to receive(:user_ff_branch).with(user, source_sha, target_branch) .to receive(:user_ff_branch).with(user, source_sha, target_branch)
...@@ -1983,11 +1982,6 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -1983,11 +1982,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
it_behaves_like '#ff_merge' it_behaves_like '#ff_merge'
end end
context 'without gitaly', :skip_gitaly_mock do
it_behaves_like '#ff_merge'
end
end
describe '#delete_all_refs_except' do describe '#delete_all_refs_except' do
let(:repository) do let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
...@@ -2308,6 +2302,7 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2308,6 +2302,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error') expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
end end
end end
end
describe '#squash' do describe '#squash' do
let(:squash_id) { '1' } let(:squash_id) { '1' }
...@@ -2327,7 +2322,8 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2327,7 +2322,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
repository.squash(user, squash_id, opts) repository.squash(user, squash_id, opts)
end end
context 'sparse checkout', :skip_gitaly_mock do # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'sparse checkout' do
let(:expected_files) { %w(files files/js files/js/application.js) } let(:expected_files) { %w(files files/js files/js/application.js) }
it 'checks out only the files in the diff' do it 'checks out only the files in the diff' do
...@@ -2372,7 +2368,8 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2372,7 +2368,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
context 'with an ASCII-8BIT diff', :skip_gitaly_mock do # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'with an ASCII-8BIT diff' do
let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" } let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
it 'applies a ASCII-8BIT diff' do it 'applies a ASCII-8BIT diff' do
...@@ -2383,7 +2380,8 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2383,7 +2380,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
context 'with trailing whitespace in an invalid patch', :skip_gitaly_mock do # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'with trailing whitespace in an invalid patch' do
let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" } let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" }
it 'does not include whitespace warnings in the error' do it 'does not include whitespace warnings in the error' do
...@@ -2397,7 +2395,6 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -2397,7 +2395,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end end
end end
end end
end
def create_remote_branch(repository, remote_name, branch_name, source_branch_name) def create_remote_branch(repository, remote_name, branch_name, source_branch_name)
source_branch = repository.branches.find { |branch| branch.name == source_branch_name } source_branch = repository.branches.find { |branch| branch.name == source_branch_name }
......
...@@ -17,7 +17,7 @@ describe Gitlab::GitalyClient::CommitService do ...@@ -17,7 +17,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message, repository: repository_message,
left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660', left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
right_commit_id: commit.id, right_commit_id: commit.id,
collapse_diffs: true, collapse_diffs: false,
enforce_limits: true, enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h **Gitlab::Git::DiffCollection.collection_limits.to_h
) )
...@@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::CommitService do ...@@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message, repository: repository_message,
left_commit_id: Gitlab::Git::EMPTY_TREE_ID, left_commit_id: Gitlab::Git::EMPTY_TREE_ID,
right_commit_id: initial_commit.id, right_commit_id: initial_commit.id,
collapse_diffs: true, collapse_diffs: false,
enforce_limits: true, enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h **Gitlab::Git::DiffCollection.collection_limits.to_h
) )
......
...@@ -153,6 +153,13 @@ describe MergeRequestDiff do ...@@ -153,6 +153,13 @@ describe MergeRequestDiff do
expect(mr_diff.empty?).to be_truthy expect(mr_diff.empty?).to be_truthy
end end
it 'expands collapsed diffs before saving' do
mr_diff = create(:merge_request, source_branch: 'expand-collapse-lines', target_branch: 'master').merge_request_diff
diff_file = mr_diff.merge_request_diff_files.find_by(new_path: 'expand-collapse/file-5.txt')
expect(diff_file.diff).not_to be_empty
end
it 'saves binary diffs correctly' do it 'saves binary diffs correctly' do
path = 'files/images/icn-time-tracking.pdf' path = 'files/images/icn-time-tracking.pdf'
mr_diff = create(:merge_request, source_branch: 'add-pdf-text-binary', target_branch: 'master').merge_request_diff mr_diff = create(:merge_request, source_branch: 'add-pdf-text-binary', target_branch: 'master').merge_request_diff
......
...@@ -2692,6 +2692,22 @@ describe MergeRequest do ...@@ -2692,6 +2692,22 @@ describe MergeRequest do
end end
end end
end end
context 'source branch is missing' do
subject { create(:merge_request, :invalid, :opened, merge_status: :unchecked, target_branch: 'master') }
before do
allow(subject.project.repository).to receive(:can_be_merged?).and_call_original
end
it 'does not raise error' do
expect(notification_service).not_to receive(:merge_request_unmergeable)
expect(todo_service).not_to receive(:merge_request_became_unmergeable)
expect { subject.mark_as_unmergeable }.not_to raise_error
expect(subject.cannot_be_merged?).to eq(true)
end
end
end end
describe 'check_state?' do describe 'check_state?' do
......
...@@ -120,6 +120,14 @@ describe BambooService, :use_clean_rails_memory_store_caching do ...@@ -120,6 +120,14 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end end
end end
describe '#execute' do
it 'runs update and build action' do
stub_update_and_build_request
subject.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA)
end
end
describe '#build_page' do describe '#build_page' do
it 'returns the contents of the reactive cache' do it 'returns the contents of the reactive cache' do
stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref') stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref')
...@@ -216,10 +224,20 @@ describe BambooService, :use_clean_rails_memory_store_caching do ...@@ -216,10 +224,20 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end end
end end
def stub_update_and_build_request(status: 200, body: nil)
bamboo_full_url = 'http://gitlab.com/bamboo/updateAndBuild.action?buildKey=foo&os_authType=basic'
stub_bamboo_request(bamboo_full_url, status, body)
end
def stub_request(status: 200, body: nil) def stub_request(status: 200, body: nil)
bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result?label=123&os_authType=basic' bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result/byChangeset/123?os_authType=basic'
stub_bamboo_request(bamboo_full_url, status, body)
end
WebMock.stub_request(:get, bamboo_full_url).to_return( def stub_bamboo_request(url, status, body)
WebMock.stub_request(:get, url).to_return(
status: status, status: status,
headers: { 'Content-Type' => 'application/json' }, headers: { 'Content-Type' => 'application/json' },
body: body body: body
......
...@@ -1861,7 +1861,6 @@ describe Repository do ...@@ -1861,7 +1861,6 @@ describe Repository do
describe '#add_tag' do describe '#add_tag' do
let(:user) { build_stubbed(:user) } let(:user) { build_stubbed(:user) }
shared_examples 'adding tag' do
context 'with a valid target' do context 'with a valid target' do
it 'creates the tag' do it 'creates the tag' do
repository.add_tag(user, '8.5', 'master', 'foo') repository.add_tag(user, '8.5', 'master', 'foo')
...@@ -1886,52 +1885,13 @@ describe Repository do ...@@ -1886,52 +1885,13 @@ describe Repository do
end end
end end
context 'when Gitaly operation_user_add_tag feature is enabled' do
it_behaves_like 'adding tag'
end
context 'when Gitaly operation_user_add_tag feature is disabled', :disable_gitaly do
it_behaves_like 'adding tag'
it 'passes commit SHA to pre-receive and update hooks and tag SHA to post-receive hook' do
pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', project)
update_hook = Gitlab::Git::Hook.new('update', project)
post_receive_hook = Gitlab::Git::Hook.new('post-receive', project)
allow(Gitlab::Git::Hook).to receive(:new)
.and_return(pre_receive_hook, update_hook, post_receive_hook)
allow(pre_receive_hook).to receive(:trigger).and_call_original
allow(update_hook).to receive(:trigger).and_call_original
allow(post_receive_hook).to receive(:trigger).and_call_original
tag = repository.add_tag(user, '8.5', 'master', 'foo')
commit_sha = repository.commit('master').id
tag_sha = tag.target
expect(pre_receive_hook).to have_received(:trigger)
.with(anything, anything, anything, commit_sha, anything)
expect(update_hook).to have_received(:trigger)
.with(anything, anything, anything, commit_sha, anything)
expect(post_receive_hook).to have_received(:trigger)
.with(anything, anything, anything, tag_sha, anything)
end
end
end
describe '#rm_branch' do describe '#rm_branch' do
shared_examples "user deleting a branch" do
it 'removes a branch' do it 'removes a branch' do
expect(repository).to receive(:before_remove_branch) expect(repository).to receive(:before_remove_branch)
expect(repository).to receive(:after_remove_branch) expect(repository).to receive(:after_remove_branch)
repository.rm_branch(user, 'feature') repository.rm_branch(user, 'feature')
end end
end
context 'with gitaly enabled' do
it_behaves_like "user deleting a branch"
context 'when pre hooks failed' do context 'when pre hooks failed' do
before do before do
...@@ -1949,52 +1909,7 @@ describe Repository do ...@@ -1949,52 +1909,7 @@ describe Repository do
end end
end end
context 'with gitaly disabled', :disable_gitaly do
it_behaves_like "user deleting a branch"
let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
let(:blank_sha) { '0000000000000000000000000000000000000000' }
context 'when pre hooks were successful' do
it 'runs without errors' do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:execute)
.with(git_user, repository.raw_repository, old_rev, blank_sha, 'refs/heads/feature')
expect { repository.rm_branch(user, 'feature') }.not_to raise_error
end
it 'deletes the branch' do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
expect { repository.rm_branch(user, 'feature') }.not_to raise_error
expect(repository.find_branch('feature')).to be_nil
end
end
context 'when pre hooks failed' do
it 'gets an error' do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
expect do
repository.rm_branch(user, 'feature')
end.to raise_error(Gitlab::Git::PreReceiveError)
end
it 'does not delete the branch' do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
expect do
repository.rm_branch(user, 'feature')
end.to raise_error(Gitlab::Git::PreReceiveError)
expect(repository.find_branch('feature')).not_to be_nil
end
end
end
end
describe '#rm_tag' do describe '#rm_tag' do
shared_examples 'removing tag' do
it 'removes a tag' do it 'removes a tag' do
expect(repository).to receive(:before_remove_tag) expect(repository).to receive(:before_remove_tag)
...@@ -2004,15 +1919,6 @@ describe Repository do ...@@ -2004,15 +1919,6 @@ describe Repository do
end end
end end
context 'when Gitaly operation_user_delete_tag feature is enabled' do
it_behaves_like 'removing tag'
end
context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do
it_behaves_like 'removing tag'
end
end
describe '#avatar' do describe '#avatar' do
it 'returns nil if repo does not exist' do it 'returns nil if repo does not exist' do
allow(repository).to receive(:root_ref).and_raise(Gitlab::Git::Repository::NoRepository) allow(repository).to receive(:root_ref).and_raise(Gitlab::Git::Repository::NoRepository)
......
...@@ -71,17 +71,5 @@ describe Files::UpdateService do ...@@ -71,17 +71,5 @@ describe Files::UpdateService do
expect(results.data).to eq(new_contents) expect(results.data).to eq(new_contents)
end end
end end
context 'with gitaly disabled', :skip_gitaly_mock do
context 'when target branch is different than source branch' do
let(:branch_name) { "#{project.default_branch}-new" }
it 'fires hooks only once' do
expect(Gitlab::Git::HooksService).to receive(:new).once.and_call_original
subject.execute
end
end
end
end end
end end
...@@ -36,9 +36,9 @@ describe MergeRequests::RebaseService do ...@@ -36,9 +36,9 @@ describe MergeRequests::RebaseService do
end end
end end
context 'when unexpected error occurs', :disable_gitaly do context 'when unexpected error occurs' do
before do before do
allow(repository).to receive(:run_git!).and_raise('Something went wrong') allow(repository).to receive(:gitaly_operation_client).and_raise('Something went wrong')
end end
it 'saves a generic error message' do it 'saves a generic error message' do
...@@ -53,9 +53,9 @@ describe MergeRequests::RebaseService do ...@@ -53,9 +53,9 @@ describe MergeRequests::RebaseService do
end end
end end
context 'with git command failure', :disable_gitaly do context 'with git command failure' do
before do before do
allow(repository).to receive(:run_git!).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong') allow(repository).to receive(:gitaly_operation_client).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong')
end end
it 'saves a generic error message' do it 'saves a generic error message' do
...@@ -71,7 +71,7 @@ describe MergeRequests::RebaseService do ...@@ -71,7 +71,7 @@ describe MergeRequests::RebaseService do
end end
context 'valid params' do context 'valid params' do
shared_examples 'successful rebase' do describe 'successful rebase' do
before do before do
service.execute(merge_request) service.execute(merge_request)
end end
...@@ -97,26 +97,8 @@ describe MergeRequests::RebaseService do ...@@ -97,26 +97,8 @@ describe MergeRequests::RebaseService do
end end
end end
context 'when Gitaly rebase feature is enabled' do
it_behaves_like 'successful rebase'
end
context 'when Gitaly rebase feature is disabled', :disable_gitaly do
it_behaves_like 'successful rebase'
end
context 'git commands', :disable_gitaly do
it 'sets GL_REPOSITORY env variable when calling git commands' do
expect(repository).to receive(:popen).exactly(3)
.with(anything, anything, hash_including('GL_REPOSITORY'), anything)
.and_return(['', 0])
service.execute(merge_request)
end
end
context 'fork' do context 'fork' do
shared_examples 'successful fork rebase' do describe 'successful fork rebase' do
let(:forked_project) do let(:forked_project) do
fork_project(project, user, repository: true) fork_project(project, user, repository: true)
end end
...@@ -140,14 +122,6 @@ describe MergeRequests::RebaseService do ...@@ -140,14 +122,6 @@ describe MergeRequests::RebaseService do
expect(parent_sha).to eq(target_branch_sha) expect(parent_sha).to eq(target_branch_sha)
end end
end end
context 'when Gitaly rebase feature is enabled' do
it_behaves_like 'successful fork rebase'
end
context 'when Gitaly rebase feature is disabled', :disable_gitaly do
it_behaves_like 'successful fork rebase'
end
end end
end end
end end
......
...@@ -124,51 +124,6 @@ describe MergeRequests::SquashService do ...@@ -124,51 +124,6 @@ describe MergeRequests::SquashService do
message: a_string_including('squash')) message: a_string_including('squash'))
end end
end end
context 'with Gitaly disabled', :skip_gitaly_mock do
stages = {
'add worktree for squash' => 'worktree',
'configure sparse checkout' => 'config',
'get files in diff' => 'diff --name-only',
'check out target branch' => 'checkout',
'apply patch' => 'diff --binary',
'commit squashed changes' => 'commit',
'get SHA of squashed commit' => 'rev-parse'
}
stages.each do |stage, command|
context "when the #{stage} stage fails" do
before do
git_command = a_collection_containing_exactly(
a_string_starting_with("#{Gitlab.config.git.bin_path} #{command}")
).or(
a_collection_starting_with([Gitlab.config.git.bin_path] + command.split)
)
allow(repository).to receive(:popen).and_return(['', 0])
allow(repository).to receive(:popen).with(git_command, anything, anything, anything).and_return([error, 1])
end
it 'logs the stage and output' do
expect(service).to receive(:log_error).with(log_error)
expect(service).to receive(:log_error).with(error)
service.execute(merge_request)
end
it 'returns an error' do
expect(service.execute(merge_request)).to match(status: :error,
message: a_string_including('squash'))
end
it 'cleans up the temporary directory' do
expect(File.exist?(squash_dir_path)).to be(false)
service.execute(merge_request)
end
end
end
end
end end
context 'when any other exception is thrown' do context 'when any other exception is thrown' do
......
require 'spec_helper'
describe PruneWebHookLogsWorker do
describe '#perform' do
before do
hook = create(:project_hook)
5.times do
create(:web_hook_log, web_hook: hook, created_at: 5.months.ago)
end
create(:web_hook_log, web_hook: hook, response_status: '404')
end
it 'removes all web hook logs older than one month' do
described_class.new.perform
expect(WebHookLog.count).to eq(1)
expect(WebHookLog.first.response_status).to eq('404')
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment